Shots in this scene
What Each Property Costs
In one line
Animating a CSS property costs one of three things each frame: only compositing, or painting again, or laying out the page and painting. Hectal measured the cost of 40 properties in Chromium, so you can choose the cheap property from data rather than from folklore.
Think of it like this
Moving house. Sliding a finished sofa across a room costs almost nothing, because the sofa is the same. Repainting the sofa each day costs a bit. Rebuilding the room's walls each day, so everything else must move, costs a lot. Properties come in the same three grades.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- composite-only
- A change the compositor can make by moving or blending existing layers, with no main-thread layout or paint.
- repaint
- Drawing an element's pixels again, without changing any size or position.
- paint flashing
- A DevTools option that flashes green over every area the browser repaints.
- trace
- A recording of what the browser did, event by event, with times. Chromium traces show Layout and Paint events.
Step by step
01Animate nine properties at once
"The cost board" animates nine properties on nine swatches. Open the timeline and read each track's colour: it matches the table below.
02Read the table
In the table below, 0 for both Layout and Paint means the main thread did nothing. About 120 Paint events in 60 frames is a repaint on every frame, and about 61 Layout events is a layout on every frame.
03Compare a shadow done two ways
"A shadow on a big card" pulses the same shadow two ways: animating box-shadow (amber, paint every frame) or fading a pseudo-element that already has the finished shadow (green).
04Scale it up
Press 1 card / 24 cards with each variant. In my traces the shadow pulse recorded 182 Paint events in 1.5 s (about 12 ms of main-thread paint for one card, 29 ms for 24), and the opacity pulse none.
Try it yourself
- 1
Change a property and read the colour
In "The cost board" change the
colorrow's property name toborder-color(the values can stay) and press Run: its track stays amber, becauseborder-colorrepaints likecolor. Change thebackground-colorrow toborder-colortoo: that track turns from green to amber. - 2
Find the cheap shadow
In "A shadow on a big card" press Shadow pulse, then pause and click its track: the cost mark is amber and the graph shows
box-shadowchanging. Press Opacity pulse: one track nameddiv.card::after, green.
Code & diagrams
Composite: `transform`, `opacity`, `background-color`. Paint: `box-shadow`, `border-radius`, `color`. Layout: `text-shadow`, `width`, `top`. WAAPI keyframes use camelCase (`backgroundColor`).
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
animating: transform, opacity, background-color, box-shadow, border-radius, color, text-shadow, width, topChromium 153.0.8010.12, headless, software rendering: your GPU browser may differ for some properties (filter above all). Regenerate with `node scripts/dsa-tools/measure-anim-cost.cjs`.
Hectal's measured table. Chromium 153, headless, 60 frames, main-thread events counted in a trace.
(Paint events: about two are recorded per painted frame.)
property Layout Paint cost shown on the timeline
transform 0 0 composite
opacity 0 0 composite
backdrop-filter 0 0 composite
background-color 0 0 composite
clip-path 0 0 composite
filter: blur() 0 122 paint
color 0 124 paint
box-shadow 0 122 paint
border-radius 0 120 paint
text-shadow 61 122 layout
width 61 122 layout
height 61 183 layout
top, left, margin 61 122 layout
border-width 17 51 layout (it moves in whole-pixel steps)A: `box-shadow` itself animates (amber track). B: the finished shadow sits on `::after` and only its `opacity` animates (green track, targeting the pseudo-element). 24 cards make 24 animations.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
cards: 1, variant: shadow, animations: 1
cards: 1, variant: opacity, animations: 1
cards: 24, variant: opacity, animations: 24
cards: 1, variant: opacity, animations: 1Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Animate `top` instead of `transform`
In "The cost board" replace the top row with ["transform", "translateY(0px)", "translateY(6px)"] and compare its track with the old one.
Myth vs fact
Myth
background-color always repaints, so never animate it.
Fact
In the Chromium we measured it was composite only. Check your own browsers with Paint flashing before deciding; it is fine for a button hover.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
A property inside a layer of its own repaints only that layer, so the cost of a paint animation is the painted area times the frames.
Remember this
- 1
The problem this solves: "only animate
transformandopacity" is a good rule, but incomplete. In current Chromium some properties you were told are expensive are not, and some you thought cheap are not. Measure, then choose. - 2
The three costs. Composite: the browser only moves, fades or blends layers it already painted. Nothing on the main thread. Paint: the browser draws the element's pixels again each frame, but sizes and positions stay as they are. Layout: the browser works out sizes and positions again, which can move other elements, and then paints.
- 3
How Hectal measured.
scripts/dsa-tools/measure-anim-cost.cjsanimates one property at a time in Chromium 153, records a one-second trace and counts Layout and Paint events on the main thread. Layout on over a fifth of the frames means "layout", else Paint on over a fifth means "paint", else "composite". The timeline's cost colours use this table. - 4
Read the table with care. It was measured in headless Chromium with software rendering, not on a phone GPU, and other browsers differ. Treat it as a first guess and measure your own page (Topic 6.5) with Paint flashing.
- 5
Why transform and opacity are cheap. The compositor applies a layer's transform and opacity when it stacks layers, so the layer's pixels are not touched. A shadow or a border colour is part of the pixels, so changing it means drawing them again. The usual fix: paint the effect once and animate only a layer property, such as a shadow on a pseudo-element whose opacity animates.
Explain it without notes
What are the three costs of animating a property, and what does each add to a frame?
How would you animate a card's shadow without painting every frame?
Practice
A button grows a box-shadow on hover. Rewrite it so the hover costs only composite.
In "The cost board" add a tenth swatch that animates filter: blur(2px). What colour is its track?
Done when you can
You can name the three costs and give two properties for each
You can read a track's cost colour on the Motion Timeline
You can replace a shadow animation with an opacity animation of a pseudo-element
You know the table was measured in headless Chromium and how to check your own browser