Compositor Layers
When browsers promote layers, why transform/opacity animate cheaply, and layer explosion costs.
- browser
- compositor
- layers
- rendering
- performance
After layout and paint, the browser often composites pre-painted layers on the GPU. Animating properties that only need recomposite (transform, opacity in favorable cases) avoids layout and sometimes paint. Promoting everything to its own layer is not free — memory and upload costs explode.
Docs: web.dev Animations guide, Layers panel / rendering, CSS triggers.
Pipeline reminder
DOM/CSSOM → style → layout → paint → composite → pixels
Composite is last. If a property only invalidates composite, you skip expensive earlier stages for that frame.
What tends to create layers
Browsers use heuristics. Common promoters:
transform/opacityanimations or will-changeposition: fixed/ sticky in some cases- 3D transforms (
translateZ(0)hacks — treat as last resort) <video>, canvas, some filters- Overflow scrolling containers
.modal {
transform: translateY(0);
transition: transform 200ms ease, opacity 200ms ease;
will-change: transform; /* only while animating if you can */
}
Prefer animating transform/opacity. Animating top, left, width, height usually forces layout.
DevTools: prove it
- Performance recording → look for Paint vs Composite Layers.
- Layers panel / Rendering → Layer borders to see promotions.
- If every card is a layer, you may have over-promoted.
Layer explosion
Each layer costs memory (backing store). Mobile GPUs run out of patience:
| Symptom | Likely cause |
|---|---|
Scroll jank with many will-change: transform |
Too many layers |
| Memory growth on list pages | Promoting every row |
| First animation smooth, later lag | Layer count scales with DOM |
/* Bad at list scale */
.list-item {
will-change: transform;
transform: translateZ(0);
}
Promote the moving element for the duration of the animation, then clear will-change.
Paint containment vs layers
contain: paint / content-visibility reduce invalidation work; they are not the same as compositor layers but often pair with performance work. Virtualize long lists so you never paint thousands of cards — virtual lists.
Main thread vs compositor thread
Scroll and transform-only animations can run on the compositor thread when layers are ready and main thread isn’t forcing updates. Heavy JS on main still causes INP issues even if scroll is smooth. See Main thread vs compositor.
Practical rules
- Animate transform/opacity when possible.
- Don’t sprinkle
translateZ(0)as cargo cult. - Use
will-changesparingly and temporarily. - Measure layer count on real pages, not demos.
- Reduce paint area (overflow hidden, containment) when paint shows up in profiles.
Interview out-loud
“Compositor layers let the GPU glue pre-painted textures. Transform and opacity can skip layout/paint; overusing will-change creates memory-heavy layer explosion. I verify with Performance and Layers, not folklore.”
Mobile memory note
Each additional layer can allocate a full-size texture. A product listing grid with will-change: transform on every card may look smooth in a desktop Performance recording and OOM on a low-end Android device. Prefer promoting the carousel track or the single dragging element, not every cell.
Related
Further depth
Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.
Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.
Further reading
Related guides
- Main Thread vs Compositor ThreadWhat runs on the main thread vs compositor, why scroll can be smooth while clicks feel dead, and fix strategies.
- Browser Rendering PipelineFrom bytes to pixels — parse, style, layout, paint, composite — and how your JS/CSS kicks each stage.
- BFCache Back Forward CacheHow the back/forward cache freezes pages for instant history nav, what blocks it, and how to restore state safely.
- Browser DevTools Network PanelRead waterfalls, timing phases, headers, throttling, and initiator chains in the Network panel like a production debugger.
- Browser DevTools Performance PanelRecord main-thread timelines: long tasks, style/layout/paint, frames, and how to turn flame charts into INP fixes.