awesome-everything RU
↑ Back to the climb

Browser & Frontend Runtime

Compositor layers: promotion, overlap, and GPU memory

Crux How elements get promoted to compositor layers, why a single transform can drag a dozen siblings with it, and what a layer costs in GPU memory.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 14 min

You add will-change: transform to one card in a list to make its hover animation smooth. DevTools’ Layer Borders panel lights up with forty coloured boxes you never asked for. The browser promoted your neighbours because it had no choice.

How elements reach the compositor

Some elements get their own compositor layer:

  • transform: translate3d(...) or any 3D transform
  • will-change: transform (or any compositable property)
  • position: fixed
  • <video>, <canvas>
  • Currently-animated opacity
  • filter: blur(...) and other graphical filters
  • Isolated stacking contexts that overlap another layer

Each layer is rasterised once (paint), uploaded to the GPU, then re-composited cheaply for the next thousand frames. Animating a layer’s transform is “free” — the main thread can sleep. Animating its top is expensive — the main thread re-layouts the world every frame.

Layer lifecycle

1. Element gets a promotion trigger (transform3d, will-change, etc.)
2. Browser paints the element’s pixels into a GPU bitmap (one-time cost)
3. Compositor moves/blends the bitmap each frame — no main thread involved
4. GPU memory holds the bitmap until the layer is destroyed (element removed or trigger revoked)

The overlap rule: layers you never asked for

The compositor must maintain visual correctness. If element A is a layer (say, transform) and element B paints on top of A in document order without being a layer, the compositor cannot resolve their stacking — A’s layer would appear on top of B even though B is supposed to be on top.

The browser fixes this by promoting B to its own layer, often dragging adjacent elements with it. The compositor then squashes adjacent non-animating layers into a shared “squashed” layer to limit explosion, but the heuristic is imperfect.

A single innocent will-change: transform on a card in the middle of a feed can promote a dozen siblings.

Use DevTools’ Rendering panel → “Layer Borders” to see what is promoted. Pay attention to layers labelled “compositor-induced” or “overlap” — those crept in without your asking.

will-change as a two-edged sword

will-change: transform is a hint to the browser: “I am about to animate this property, please pre-paint the bitmap so the compositor can move it without going back to layout or paint.” The browser promotes the element immediately.

The cost: GPU memory — width × height × 4 bytes per layer.

The will-change anti-pattern: will-change: transform on every component in a design system permanently reserves a layer per component instance. On a list of 100 cards each containing 5 will-change’d sub-elements, the page holds 500 layers — possibly hundreds of MB of GPU memory. On a phone with 256 MB of GPU memory available, the OS starts evicting; the browser re-rasterises on the fly and the page stutters more than without layers.

The fix: set will-change just before the animation starts (e.g., on mouseenter) and remove it on animationend. Treat it as a fast-forward signal, not a permanent setting.

Edge cases

The overlap rule interacts with z-index stacking contexts in subtle ways. An element with z-index: 1 and position: relative creates an isolated stacking context. If that stacking context overlaps a compositor layer, its entire painted area must be promoted to avoid cross-layer bleed. This is why adding position: relative; z-index: 1 to a tooltip that sits above an animated background can trigger a full-page layer cascade in complex layouts. The solution is to keep promotable elements (animated, fixed, video) in a separate stacking context from non-promotable siblings, or to use isolation: isolate explicitly to contain stacking context effects.

Complete the analogy

In the kitchen metaphor, a layer is a finished plate sitting on the pass — already cooked, ready for the runner. What is the JavaScript equivalent that turns a normal element into one of these pre-cooked plates so the compositor can move it without re-cooking?

Compute it

A 1920 × 1080 element is promoted to its own compositor layer. Each pixel is 4 bytes (RGBA). How many megabytes of GPU memory does this single layer consume?

MB
Quiz

A team adds `will-change: transform` to every card in their design system. GPU memory on mobile balloons. What is the mechanism?

Quiz

Element A has `will-change: transform`. Element B overlaps A in document order. Element B has no promotion trigger. What does the browser do?

Recall before you leave
  1. 01
    What is the GPU memory cost of a 1920×1080 compositor layer?
  2. 02
    Why does `will-change: transform` on one element sometimes promote its neighbours?
  3. 03
    What is the correct usage pattern for `will-change`?
Recap

A compositor layer is a GPU bitmap that the compositor can position and blend cheaply per frame, without touching the main thread. Promotion triggers include 3D transforms, will-change, position: fixed, video, canvas, and animated opacity. The overlap rule forces the browser to promote elements that overlap a layer — a single will-change in a list can trigger a dozen implicit promotions. Each full-screen layer costs ~7.91 MB of GPU memory; exhausting mobile GPU budgets causes tile eviction and re-rasterisation, producing worse jank than no layers at all. The correct pattern is to scope will-change to the duration of an animation. Layer Borders in DevTools visualises every promoted element and its reason.

Connected lessons
appears again in162
Continue the climb ↑DevTools flame strip and the frame lifecycle
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.