There are two primary factors that contribute to the performance of a web animation: rendering, and hardware acceleration.
Rendering is the process of taking an update to the DOM and reflecting that change on screen. This affects the performance of all animations.
Let's take a deeper look at each.
When we update the styles an element:
The browser needs to reflect this change by re-rendering the page, taking the HTML and CSS and turning it into an image that shows on your screen.
Generally speaking, the steps a browser's renderer takes to do this are:
- Layout: Calculate the size and position of elements on the page
- Paint: Draw the page into graphical layers - essentially individual images that make up the page
- Composite: Draw these layers to the viewport.
As a rule of thumb, it is quicker to do less work.
For instance, if we update one element's
height, this changes the size of the element. Changing the size of an element might then affect the size and/or position of a sibling or child element, which itself might have knock-on effects, and so on. So we need to recalculate the layout of all the affected elements.
Whenever the layout of a page changes, the browser also needs to repaint and recomposite the affected layers too. Many styles affect layout, like
position, etc. Luckily these styles are usually easy to spot when changing them affects the size and/or position of the element (with the exception of
Smooth animations usually run at 60 frames a second (fps), the same as most screen refresh rates. So if we want to change the
height of an element at 60fps, we need to be able to re-render in 16.7 milliseconds. Re-renders can easily take upwards of 100ms! This is why animating layout is so often discouraged.
However, this isn't a hard and fast rule. If an element's layout is isolated, for instance it has
position: absolute and very few children, then you might be able to animate it smoothly. Just make sure you test these animations on low-powered devices.
The best performing styles are those that trigger only the third rendering step, the compositor.
In every modern browser,
opacity will operate directly on a layer. These are therefore the safest values to animate across all devices.
Browsers are adding more values to this list, too. For instance, Chrome and Firefox handle
filter and SVGs entirely on the compositor, and Chrome is adding support for
Additionally, because Motion One is built on the Web Animations API, browsers are smart enough to automatically place elements animating these values on a new graphical layer.
There are also plenty of values like
border-radius that can be updated without triggering a render, but do require a potentially expensive paint (step 2).
You should always test the animation of these properties on low-powered devices.
However, there are still ways you can improve the performance of these animations.
First, the time a browser takes to repaint a layer is proportional to its size. So you can improve the performance of these animations by making smaller layers.
You can hint to the browser that they should create a new layer by setting the
willChange style to
Creating new layers doesn't come for free. Each takes space on the GPU. So do so sparingly.
Second, you can try replacing styles will better-performing alternatives.
We've already seen that browsers are improving support for
filter on the compositor. So instead of animating
filter with the
Likewise, browsers are adding
clipPath to the compositor. So instead of animating
clipPath with the
So how do you tell which styles will trigger layout or paint, and which will just trigger composition?
Most tutorials on this subject will recommend CSS Triggers. While this is still a good guide to catching layout-triggering styles (
top), it's very out of date, missing data on
filter and outdated info on others.
Browsers are changing all the time so the best approach is when venturing outside
opacity to test cross-browser and cross-device. Every browser has performance profiling tools, most can remotely debug mobile devices, so be sure to use those to investigate further.
Rendering performance should be your primary focus because it will be a consideration in every animation you make. But there's the additional factor of the animation process itself and whether it can be hardware accelerated.
An animation, at its most basic, mixes two values over a duration of time. For example, if we wanted to animate between
"200px" over one second, if
0.5 seconds has elapsed our animation would calculate
"150px". This is a very simple calculation and doesn't produce noticeable overhead compared to rendering.
However, in a browser there are several ways we can compute this value:
requestAnimationFrame(like Greensock and Anime.js)
- With the Web Animations API
- With CSS
However, WAAPI and CSS can run some animations off the main JS thread, directly on the compositor itself. As this is usually a GPU, this is often referred to as hardware acceleration.
An animation that is hardware accelerated will remain smooth, no matter how busy your main JS thread becomes.
Because Motion One is built on WAAPI, it can also run hardware accelerated animations.
As a general rule, if a style invokes only the composite rendering step, it can theoretically be animated on it, too.
opacity are widely supported compositor styles and these do usually animate on the compositor.
Other values like
clip-path and SVGs either have support or are gaining it in most browsers.
Motion One is unique from WAAPI and CSS in that it supports the animation of individual transforms:
Under the hood, it is animating CSS variables, and currently these are not accelerated, even though they're being applied to
So if hardware acceleration is crucial in your use-case, stick to animating
Even when animating supposedly performant styles, each browser has different rules for when an animation does or doesn't receive acceleration.
For instance, until very recently, if Chrome detected a
% based transform like this:
It wouldn't be hardware accelerated.
Webkit has quite a number of bail-outs from accelerated animations.
This seems to be because it attempts to leverage Core Animation, and as a result many of the bugs in that then surface in Webkit.
Additionally, Core Animation doesn't have a full feature overlap with the WAAPI spec.
The short term fix usually employed by the Webkit team is to detect the conditions that lead to the bugs or where the specs are mismatched and then disable hardware acceleration. Leading to a patchwork of conditions you should be aware of.
playbackRate is set to a value other than
direction is set to a value other than
step easing is used:
cubic-bezier easing is used with a
y value outside of the
0-1 range (commonly used for overshoot), AND there are more than two keyframes or two keyframes that don't use
filter is supported, but only in macOS.
There are also a myriad of timing bugs that result in massive delays in animations or synchronisation issues between the main thread and compositor.
For this reason, Motion One actually disables hardware acceleration by default in Webkit.
We'll be monitoring the state of accelerated animations in Webkit and hopefully can remove this limitation in the future. Until then, it is possible to allow Webkit to run accelerated animations with the
However, we recommend thoroughly testing these animations in Safari and an embedded
WKWebView browser like the one used in iOS Chrome.
All of this is to say, it can be a minefield ensuring your animations are hardware accelerated.
We recommend treating acceleration like progressive enhancement. It's great when a browser supports it, but usually not essential.
true, and ensure you test thoroughly in Webkit browsers.
To achieve smooth animations your main priority should be which values you animate. As a developer, that is the thing you have most control over.
opacity are cheapest to render in all browsers. Browsers are improving the rendering performance of other styles (and SVG) all the time, with
clip-path on the horizon.
Layout-triggering styles can be animated on elements that don't affect the layout of surrounding elements (for instance if they're
position: absolute). But make sure you test these animations in many browsers, especially low-powered devices.
Finally, hardware acceleration is an excellent tool to avoid jank if your website is performing heavy processing during an animation. But it isn't something you have full control over, there are many conditions under which it gets disabled.
Stay updated with the Motion One newsletter. We don't spam, and unsubscription is instant.