Long readEngines, issue 7
The quiet craft of browser engines
Between a typed address and a painted frame sits one of the most patient pieces of software we use every day. A walk through its rooms, from the tokenizer to the compositor.
Nobody notices an engine when it works. You type, you wait a fraction of a second, and a page arrives, already laid out, already the right size for your hand. The work that made it happen is invisible on purpose. This is an attempt to make some of it visible, the way a stagehand might walk you across an empty theatre after the show.
Every engine, whatever its lineage, answers the same question many times a second: given this document, these styles and this window, what should each pixel be? The answer is assembled in stages, and each stage hands a smaller, more certain thing to the next.1
Bytes become a tree
The first room is the quietest. Bytes arrive from the network in whatever order the network likes, and the tokenizer reads them one character at a time, deciding whether it is inside a tag, an attribute, a comment or plain text. It is forgiving to a degree that surprises people: a missing closing tag is not an error but an instruction, resolved by rules written down long before most of today's sites existed.
The tree builder takes those tokens and grows the document object model, the structure every inspector shows in its Elements view. It does this while the bytes are still arriving, so the top of a page can exist before the bottom has left the server.
Rules meet elements
Style is where the engine becomes a negotiator. For every element it gathers the declarations that might apply, sorts them by origin, layer, specificity and order, and settles on one value for each property. Custom properties stay as tokens until the last moment; color-mix() and relative colours are resolved only when the engine knows what they refer to.
The cascade is not a bug in CSS. It is a very old, very polite agreement about who gets the last word.
A good engine avoids redoing this work. Change one class and it marks only the elements that might be affected, then recomputes those. The cost of a style change is decided less by how much you change than by how many elements your selectors could possibly touch.
/* A selector the engine can scope cheaply */
.card:has(input:checked) {
--finish: oklch(0.62 0.2 264);
outline: 1.5px solid var(--finish);
}Boxes find their places
Layout turns styled elements into boxes with positions and sizes. It is the stage that most often surprises authors, because it is where the window finally matters. A grid track sized in fractions, a container query, a line of text that wraps one word earlier on a narrower phone: all of it is decided here, top to bottom, with the occasional second pass when a child's size changes its parent's mind.2
Paint, then compose
Painting records what to draw: backgrounds, borders, text, shadows, in the right order. Compositing decides what can be drawn once and moved cheaply. When an animation touches only transform oropacity, the engine can skip the earlier rooms entirely and hand the work to the compositor, which is why those two properties are the polite way to move things.
| Stage | Typical share | Triggered by |
|---|---|---|
| Style | 2.1 ms | Class, attribute or state changes |
| Layout | 3.4 ms | Size, position, text or font changes |
| Paint | 2.8 ms | Colour, shadow, background changes |
| Composite | 0.9 ms | Transform and opacity |
| Headroom | 7.5 ms | Your scripts |
| Total | 16.7 ms | One frame |
Sixteen milliseconds is a long time for a machine and no time at all for a careless script.
Why it stays quiet
The craft is in what the engine refuses to do. It refuses to lay out what nobody can see, to repaint what did not change, to block the first paint on a script that could have waited. The best engines are conservative in the way good editors are: they keep what works and touch only what must move.
Opening an inspector is a way of thanking that work. Every row in the Elements tree, every crossed-out declaration in Styles, every bar in a network waterfall is the engine showing its notes. Read them slowly. They are more honest than they look.3
Notes
- Real engines interleave these stages far more than a diagram can show; this essay keeps them in order for clarity. ↩
- Some layouts need more than two passes, which is one reason intrinsic sizing is harder than it looks. ↩
- Honest within limits: an inspector can only report what the engine exposes, and a good one says so when a value is unavailable. ↩