Why is animating transform cheaper than animating top, and when does promoting an element to its own compositor layer make things worse?
Transform and opacity can be applied by the compositor to an already-rasterised layer, skipping style, layout and paint, and running off the main thread. Promotion buys that at the cost of memory, extra rasterisation and layers you forgot to retire.
What the interviewer is scoring
- Does the candidate name which pipeline stages each property skips rather than saying transform is GPU accelerated
- Whether the compositor thread running independently of a busy main thread forms part of the explanation
- That layer promotion is described with its memory and rasterisation costs, not only its benefit
- Whether will-change is treated as a hint with a lifetime rather than a permanent declaration
- Does the answer offer a real strategy for animating a layout property instead of calling it impossible
Answer
The same frame, two very different amounts of work
Both animations change where an element appears, so the interesting question is what the browser has to redo between frames to honour the change. Animating top alters the element's position in the layout, which means geometry has to be recomputed, and because layout is a tree-wide calculation, siblings and descendants can be dragged into it. Once the boxes have moved, the affected area has to be painted again, and only then can the result be composited to the screen. Every frame of that animation pays style, layout, paint and composite.
Animating transform changes none of the geometry the layout algorithm knows about. The element occupies exactly the same box, and the transform is a mapping applied to already-painted pixels. If the element's content sits on its own compositing layer, the browser has a rasterised texture in hand and the frame costs a new matrix and a re-composite. The same is true of opacity, which is a blend factor applied to a layer. Those two properties are the reliable ones precisely because they can be expressed as an operation on finished pixels rather than as a change to the document.
There is a second, less quoted reason the difference matters so much in practice. Layout and paint happen on the main thread, which is also where your JavaScript, your event handlers and your framework's rendering run. A compositor-driven animation is stepped on the compositor thread, so it keeps running smoothly through a long task that would have stalled a top animation entirely. That is why a transform-based animation often looks fine on a page that is simultaneously hydrating and a layout-property animation on the same page stutters.
Promotion is an allocation, not a free upgrade
Layers exist because the compositor needs something to move independently. A layer holds a rasterised copy of its content, and that copy lives in memory sized by the element's area and the device pixel ratio, which is why a full-screen promoted element on a high-density phone display is a meaningful allocation rather than a rounding error. Promote a hundred list rows and you have asked for a hundred textures on a device that may have very little headroom, and the symptom is not a slow animation but a page that becomes sluggish overall or has its tab discarded.
Rasterisation itself is work too. Content on a new layer has to be painted into that layer at least once, and anything that changes inside the layer forces it to be painted again, so promoting an element whose content changes every frame buys you nothing and costs you an upload. Text is worth a specific mention: because a layer is rasterised separately, text on a promoted layer without an opaque background behind it may be antialiased differently from the same text in the main layer, which shows up as a subtle weight shift when the animation starts and stops.
The other cost is one the browser imposes on you. If a promoted element overlaps content that paints above it, the browser may have to promote that content as well to preserve the correct painting order, so one deliberate promotion can produce several you did not ask for. This is the mechanism behind layer counts that grow far beyond the number of will-change declarations in the stylesheet.
will-change is a hint with a lifetime
The property tells the browser to prepare for a change, and preparing usually means promoting now rather than at the moment the animation starts, which removes a one-frame hitch at the beginning. The instruction that gets missed is that it is meant to be temporary. A rule such as will-change: transform written into a component's base styles keeps the layer alive for as long as the element exists, whether or not anything is animating, and repeated across a design system it is exactly how a page accumulates hundreds of idle layers.
/* The hint applies only while the interaction is possible, and is
withdrawn once the element is idle again. */
.card:hover { will-change: transform; }
.card { transition: transform 200ms ease-out; }
Adding and removing the hint from JavaScript around an animation is more precise still, though only worth the code where you have measured the initial hitch. If you cannot say which frame the hint is buying back, you are more likely to be adding cost than removing it.
When the thing you must animate is a layout property
Sometimes the design genuinely requires a size or position change that transforms cannot express, and the honest answer is that you substitute rather than optimise. Scaling a transform can stand in for a width change, at the cost of distorting the content and any borders, which is acceptable for a plain rectangle and wrong for a card full of text. The FLIP approach measures the first and last geometry, applies a transform that makes the new layout look like the old one, and then animates that transform away, so the layout change happens once instead of every frame. Where the change is a whole-view transition, the browser's own view transition machinery does the same trick with snapshots.
The judgement being assessed here is whether you know that "animate only transform and opacity" is a consequence of the rendering pipeline rather than a rule of thumb, and therefore when it is worth breaking.
Transform and opacity are cheap because they are operations on pixels the browser has already produced, and they stay cheap because they run on a thread your JavaScript cannot block. Every layer you create to get that benefit is memory you spend, so create them for animations you are actually running and retire them afterwards.
Likely follow-ups
- The design calls for an element's width to animate. What do you build instead, and what does the substitution cost?
- A composited animation is smooth on a laptop and drops frames on a mid-range phone. Where do you look first?
- How would you confirm from developer tools that an animation is running on the compositor rather than the main thread?
- Two elements overlap and only one of them is promoted. Why might the browser promote the other one as well?
Related questions
- A page has a poor Largest Contentful Paint. How do you diagnose it and what do you actually change?hardAlso on rendering6 min
- A list of a million small objects uses far more memory than the data inside them. Where is it going?mediumSame kind of round: concept5 min
- A promise rejects and nothing is awaiting it. What does Node do?hardSame kind of round: concept4 min
- A more complex model beats your simple one by a small margin offline. How do you decide whether it is worth shipping?hardSame kind of round: concept4 min
- Give me a subclass that the compiler accepts but that still breaks the Liskov substitution principle. What rule does it break?mediumSame kind of round: concept4 min
- When would you reach for an abstract base class rather than a Protocol, and what does isinstance check in each case?hardSame kind of round: concept5 min
- You kick off background work from a controller action and it throws once the response has gone out. What is happening?mediumSame kind of round: concept5 min
- Two transactions each check a rule, then both commit changes that break it, and neither overwrote the other's row. What happened?hardSame kind of round: concept5 min