Basis Lab

Answers · The network and the web

Why is animating width so much slower than animating transform?

In shortAnimating width is slower because each change forces layout and repaint, while a transform on a promoted element skips both and only reassembles the frame.

Animating width is slower because every step changes geometry, so the browser has to lay out the affected subtree, repaint the changed regions and then reassemble the frame. A transform on a promoted element skips layout and paint and is handled during assembly.

What each stage costs

The deciding question is not whether a property is animated but where the change enters the drawing pipeline. A geometry change such as width or height enters at layout, and everything below layout runs again: paint and assembly. An appearance change such as background colour enters at paint, so layout is skipped. A transform or an opacity change on an element that has already been promoted to its own surface enters at assembly, so both layout and paint are skipped. At 60 refreshes a second one frame lasts 1000/60 = 16.67 ms, and the browser needs several of those milliseconds for its own work, leaving roughly 10 ms for the page's own stages. At 120 refreshes a second, 1000/120 = 8.33 ms, and the usable figure is nearer 4 ms.

b60=100060=16.67 ms,b120=1000120=8.33 msb_{60} = \frac{1000}{60} = 16.67\ \text{ms},\qquad b_{120} = \frac{1000}{120} = 8.33\ \text{ms}
FIG 1
changelayout (ms)paint (ms)assembly (ms)total (ms)
width on a list of 200 rows8.46.10.915.4
transform on a promoted row001.01.0
the same width change, read back in a loop112.06.30.9119.2

The width figure is not one operation for the whole list. Laying out 200 rows after a width or height change costs 8.4 ms in the measurement above, and the cost propagates down the list because each row's position depends on the heights above it. The browser cannot move one row and leave the rest where they were: changing one row's box can move every row below it. That is why the layout line is the large one. The transform line has no layout and no paint because the element already has a surface of its own; the only work left is putting that surface in a new place, which costs 1.0 ms. The paint line for width is also present, at 6.1 ms, because the changed region has to be redrawn after layout.

The guess that fails

The natural guess is that both are CSS animations, so both should cost about the same. The cost is not in the animation machinery; it is in the property. A width change is a geometry change, so it re-enters at the top of the pipeline and pays for layout, paint and assembly. A transform is a visual displacement that can be applied to an already painted surface, so on a promoted element it enters near the bottom. Calling both accelerated hides the distinction: the browser can accelerate the assembly of a layer, but it cannot skip layout for a width change, because the width is an input to layout.

There is a second way width becomes much worse than the table's first row. Style changes are normally batched into one pass per frame, so setting a width a hundred times costs one layout, not a hundred. Reading a layout-dependent value such as offsetWidth, clientHeight or scrollTop forces the pending layout immediately, because the honest answer depends on work not yet done. A read inside a loop that also writes forces one layout per iteration. The same width change measured that way takes 112.0 ms in layout instead of 8.4 ms, which is 112.0 / 8.4 = 13.3 times the layout cost, and the total becomes 112.0 + 6.3 + 0.9 = 119.2 ms. The animation did not become a different operation; the same layout ran many times.

When transform is not cheap

The 1.0 ms transform figure has a condition attached: the element must be promoted, meaning the browser has given it its own composited surface. An unpromoted element with a transform can still require paint, and then the gap narrows. Promotion is not free either: it trades an occasional drawing cost for a permanent memory cost, so promoting everything is not a fix. Width is also not uniformly catastrophic. A width change on a small subtree with nothing below it still enters at layout, but the layout region is smaller and the propagation is smaller. The ordering never reverses: width still pays for layout, and a promoted transform still skips it.

At 120 per second the gap is starker. The full frame is 8.33 ms, and the usable part is around 4 ms. The 15.4 ms width change misses even the full frame by 15.4 - 8.33 = 7.07 ms, while the 1.0 ms transform uses 1.0 / 4 = 0.25, a quarter of the usable budget. One missed frame does not make the image slightly late; the previous image is shown for another refresh, so the movement jumps twice as far and the eye catches it. That is why the same visible movement can look smooth in one implementation and stutter in another.

Worth keeping

  • A width change re-enters the drawing pipeline at layout, so layout, paint and assembly all run.
  • At 60 frames a second the frame budget is 16.67 ms, and the usable part is nearer 10 ms.
  • A transform is cheap only when the element is promoted, because then it can be handled at assembly.

Where this goes next

This question is one step of How a Browser Draws a Page, a course that works through its subject from the beginning. The page that teaches this part of it is Doing It All Again, and it reads the whole way through whether or not you have an account.

What an account changes is the practice: problems standing under every idea, marked with a reason rather than a tick, in a copy of the course that is yours.

Read that pageThe whole course

Other questions people ask about this

  1. Why does a <script> tag stop the rest of the page from being parsed?A script tag stops the parser because the script can change the page being built, so the browser must fetch and run it before it knows what comes next.
  2. Where should a session ID be stored so JavaScript can't read it?Store it in an HttpOnly cookie set by the server; that is the only browser storage JavaScript running on the page cannot read.
  3. What does it mean for an API method to be safe?A safe method is one that changes nothing on the server when it is called, so a caller may repeat it freely and a cache or a crawler may use it without anything being altered.
  4. Does signing out actually invalidate my token on the server?No. Signing out clears your browser's copy of the token, but the server keeps accepting that token until it expires.
  5. Why does the TLS handshake take so many round trips before any data is sent?It is not one key exchange. The handshake agrees a secret, proves the server's identity and checks for tampering, and modern TLS fits those into one round trip; a fresh connection still needs three before the page starts.
  6. If a website has HTTPS, can someone on the same Wi-Fi read my password?No. HTTPS encrypts the form contents and cookies, so someone on the same Wi-Fi cannot read the password itself; they can still see the site, timing and packet sizes.

Read alongside