How Brilliant's canvas stays fast at 13,000 elements (and why selection draws two rectangles)
Two questions come up whenever someone opens a Brilliant canvas with a serious amount of stuff on it. The first is "why is this still fast?" The second, usually asked with a raised eyebrow, is "why did selecting things in two different frames draw me two rectangles instead of one?"
Both answers come from the same place: Brilliant treats the canvas as a hierarchy of coordinate spaces, not a flat pile of shapes. The first question is about finding elements quickly in world space. The second is about operating on them correctly in parent-local space. This post walks through both.
The world is big and mostly empty
The canvas is enormous. World coordinates in Brilliant span from -10,000,000 to 10,000,000 on each axis, which is a lot of room for a design that mostly clusters in a few thousand pixels. A typical large document is a few hundred elements; our internal large-canvas benchmark stresses the system at 13,000 elements at once, with only a slice of them visible in the viewport at any given zoom.
You cannot answer "what is under this cursor" or "what falls inside this drag rectangle" by looping over 13,000 elements on every pointer move. So we don't. A spatial index over every element's world-space bounds answers region queries by visiting only the neighborhoods a query actually touches, so most of those thirteen thousand elements never get looked at. The net effect is that broad-phase queries scale closer to logarithmic than linear with element count. That "-ish" matters, and we'll come back to why the index alone is never the final answer.
An index is only useful if it matches reality, and canvas geometry changes constantly: every drag, resize, and reparent moves world bounds around. So the index is kept coherent with the model at all times. Every query reflects every edit that preceded it, including mid-gesture, and bulk operations (a thousand-element delete, a big multi-element drag) keep the index current without paying a per-element toll. Nobody queries a stale tree, and nobody waits for a fresh one.
From a click to an element
The index gives us candidates, never answers. Hit testing runs in two stages, and the split is the whole game.
The broad phase is the spatial index: given a world point, it returns a small candidate set by bounding-box overlap. Fast, approximate, never precise. The narrow phase takes each of those handful of candidates, transforms the point into that element's parent-local space, and tests it against the element's real geometry: the rounded-rectangle path, the ellipse or arc path, the bezier edges of a vector. Expensive, but it only ever runs on the few candidates the broad phase surfaced, not on the whole canvas.
Input arrives in screen space and gets transformed to world space before any of this begins. The precise tests then happen in parent-local space, because that is where an element's points actually live. So a single click threads through three coordinate systems: screen to world for the query, world to parent-local for the precise test.
Ordering matters as much as speed. The hit test returns candidates topmost-first, and sorting only the candidate set is cheap, roughly proportional to the number of candidates rather than the number of elements on the canvas, which is exactly what you want on a hot path that fires on every pointer move. Visibility and lock filtering happen here too, so hidden and locked elements never reach the precise test. Click tolerance divides by the zoom factor, keeping the clickable margin a constant few screen pixels whether you are zoomed way out or way in.
There is a whole policy layer above this that decides selection feel, like the rule that a top-level frame with children is only grabbable by its label rather than its body. That is its own post. The point here is the shape: a logarithmic-ish broad phase feeding a precise narrow phase, with the transform into parent-local space happening per candidate, inside the loop.
Why two frames means two rectangles
Now the eyebrow question. Select two elements inside Frame A and three inside Frame B, and Brilliant draws two selection rectangles, each with its own resize handles. This is deliberate, and it falls straight out of the coordinate hierarchy.
An element's points are stored relative to its parent, not in world space. A frame can be rotated, scaled, nested inside another rotated frame. "Align these to the left" only has a meaning inside a single coordinate space. Left in Frame A, if Frame A is rotated 18 degrees, is a different direction than left in Frame B. Collapsing everything into one world-space rectangle and aligning against that would produce visually wrong, and genuinely surprising, results the moment any ancestor is rotated.
So selection is not a flat set. It is grouped by parent:
selection
└── one group per parent
├── the selected ids inside that parent
├── bounds in the parent's own local space
└── world bounds, for drawing on screenEvery geometric operation iterates those per-parent groups and works inside each parent's local space independently. Align-left with a selection spanning two parents runs twice, once per group, each pass computing its own bounds and moving its own elements within its own frame. Resize and rotate work the same way. Two parents with selected children, two rectangles, two independent operations. The multiple rectangles are not a quirk of the renderer; they are an honest picture of the fact that you are editing in two coordinate spaces at once.
A couple of details make this robust. Each parent's world bounds are computed from its elements' oriented bounding-box corners run through the parent's world transform, then taking the axis-aligned bounds of those corners. Transforming the local axis-aligned rect directly would double-inflate anything rotated. And a frame can never be co-selected with its own descendants: ancestors always win, Figma-style. Marquee across a frame and its children and you select only the frame.
Marquee correctness
Drag-selection runs the same broad-phase machinery. The rectangle query returns candidates, each candidate's world rectangle is converted into its parent's local space, and then it is precisely intersection-tested. Top-level frames with children require full containment rather than mere intersection, so dragging a marquee across a big frame selects the elements inside it rather than the frame itself. You only grab the frame by enclosing it whole.
That is a lot of interacting rules: broad phase, per-candidate transforms, the containment special case, the ancestors-win frame rule, tolerance that shifts with zoom, plus hidden, locked, and mask-shape stripping. All of it is exactly the sort of thing an optimization pass quietly breaks. So before we made the marquee faster, we froze its behavior: the fast path is held, by standing tests, to select exactly what the slow path selected, across every scene shape and every modifier. A shortcut that lies is not a shortcut we ship.
That is the throughline. The spatial index makes the canvas fast, the two-phase hit test makes clicks land where you see them, per-parent grouping makes operations correct inside every coordinate space, and the frozen behavior makes sure none of it drifts when we make it faster. The extra rectangle is the visible tip of a lot of care about coordinates.