5.1 Two trees, one picture
The journey from the two trees to the screen is a fixed sequence: compute the styles, compute the geometry, draw the pixels, assemble the layers. The names are style, layout, paint, and composite. The order is not negotiable, because each stage consumes the output of the previous one — you cannot place a box before you know its font size, and you cannot paint it before you know where it goes.
5.2 Style: resolving the cascade
The style stage walks the DOM and, for every element, computes its computed style — the final value of every CSS property after the cascade, specificity, inheritance, and custom-property resolution have all been applied. Selector matching happens here: the engine decides which of the CSSOM's rules apply to which nodes. The output is the render tree (WebKit and Blink call it the layout tree; Gecko, the frame tree) — a tree of the elements that will actually be drawn. It is not the DOM. Elements with display: none are absent entirely; pseudo-elements like ::before are present though they exist in no markup; visibility: hidden elements are present but will paint nothing. The render tree is the DOM filtered and annotated for display.
5.3 Layout: computing geometry
Layout — historically called reflow in Gecko — takes the render tree and computes the exact position and size of every box: the box model, normal flow, flex, grid, the recent fragmentation work. Its output is a geometry tree where every node knows its rectangle in the page. Layout is the expensive stage, and it is expensive because it is global-ish: changing the width of one element can change the position of its siblings, its children, and everything after it in flow. One small write can force the browser to recompute the geometry of a large part of the page.
The classic performance trap lives here: forced synchronous layout, also called layout thrashing. The browser batches your style and layout changes to run them once per frame — but if your JavaScript writes a style and then reads a geometry property like offsetWidth, the browser must flush layout immediately to answer the read, because the answer depends on the write. Do that in a loop — write, read, write, read — and you force dozens of synchronous layouts in a single frame, turning a smooth interaction into a stall. The fix is to batch: read all the geometry first, then write all the changes. This is a Module 6 concern as much as a Module 5 one, because it is about when on the main thread the work happens.
5.4 Paint and composite
Paint turns the laid-out boxes into draw commands: fill this rectangle, stroke this border, render this text run, draw this shadow. The browser does not usually paint straight to the screen; it records lists of paint operations, often grouped into separate layers for parts of the page that can be handled independently. Rasterization — actually producing the pixels from those commands — frequently happens on the GPU.
Composite is the final assembly. The page is divided into layers; each is rasterized; the compositor places them in the right order with their transforms and opacity and hands the result to the screen. The decisive fact is that the compositor runs on its own thread and the GPU, separate from the main thread. Once a layer has been painted, the compositor can move it, scale it, rotate it, or fade it without repainting and without re-running layout — it is just re-placing an existing texture. That is the entire reason the next diagram matters.
5.5 What a change costs
Every visual change re-enters the pipeline, but not always at the top. The stage where a change enters determines its cost: a change that forces layout is expensive because paint and composite must follow; a change that only composites is nearly free because it skips the main thread's heavy stages entirely.
transform and opacity” is the most repeated advice in front-end performance. Those two properties enter the pipeline at the bottom, run only on the compositor and GPU, and so can hit sixty frames a second while the main thread is busy. Animating width or top forces the whole machine to run every frame.Cheap pixels come from the bottom of the pipeline. If you can express a change as a transform or an opacity, the GPU will do it while the main thread sleeps.
Promoting an element to its own compositor layer — with will-change or a 3D transform — is how you tell the browser “this will move; keep it ready.” It is powerful and easy to overuse: every layer costs memory, and a page that promotes everything trades smooth animation for exhausted GPU memory. Layer promotion is a hint to spend, not a free win.
5.6 Layout shift: when geometry changes late
Module 4 showed resources arriving over time. Some of them change geometry when they land, and that re-runs layout — visibly. An image with no declared dimensions takes up no space until it loads, then suddenly claims its size and shoves everything below it downward. A web font that arrives after first paint may have different metrics than the fallback, so the text re-flows the instant it swaps. The user, mid-read or mid-tap, watches the page jump. This is layout shift, measured as Cumulative Layout Shift, and it is the pipeline's most user-hostile behavior.
width and height (or aspect-ratio) on images so the box is sized from the start, and font-display plus matched fallback metrics so a font swap does not re-flow. Demo Company's building image and web-font pages are this diagram, live.5.7 The lab: watch the pipeline run
DevTools exposes every stage. In the Rendering tab, turn on “Paint flashing” to see green flashes wherever the browser repaints, and “Layer borders” to see which elements have been promoted to their own compositor layer. In the Performance panel, record an interaction and read the main-thread track: purple is layout, green is paint, and you can spot a forced synchronous layout as a tall layout spike wedged inside your scripting time. Load Demo Company's web-font page and watch the text repaint — or shift — the moment the font swaps in.
5.8 What you now have
The four-stage pipeline from two trees to a screen: style resolves the cascade into a render tree; layout computes geometry and is the expensive, global stage where forced synchronous layout lurks; paint records draw commands into layers; composite assembles those layers on a separate thread and the GPU. The actionable core is the cost model — a change that triggers layout drags the whole pipeline behind it, while a transform or opacity change rides the compositor nearly for free — and its corollary, layout shift, where late-arriving resources re-run layout in the user's face.
Every heavy stage in this module — style, layout, paint — runs on the main thread, and that thread does one thing at a time. It also runs your JavaScript. Module 6 is the scheduler that decides how rendering and scripting share that single thread: the event loop, the sixteen-millisecond frame budget, the task and microtask queues, and requestAnimationFrame — the OS scheduler from Module 1, finally in full.
The pipeline is fixed; the cost is yours to choose. You pick which stage your changes re-enter, and the user feels the difference at sixty frames a second.