Smooth as Butter: Achieving 60 FPS Animations with CSS3

While everyone is using CSS3 animations in mobile these days, many are not doing it properly. There are best practices to take into account that are constantly and considerably disregarded. This happens mainly because there are still people who don’t really understand the real reasons why those practices exist and are so vigorously endorsed.

There is such a large spectrum of device specifications nowadays, that if you don’t optimize your code to consider this, instead of animations that are smooth as butter, you’ll end up delivering a subpar experience to the highest share.

Remember: though there are some high-end, flagship devices out there which keep pushing the envelope, you still have most of the world using the type of device that compared to those spec monsters is nothing but an abacus with an LCD.

We want to give you a hand in harnessing the power of CSS3 correctly. To do just that we need to understand a few things first.

Understand the Timeline

What is the browser doing while rendering and playing around with the elements? This very simple timeline is called the Critical Rendering Path.

To achieve smooth animations we need to focus on changing properties that affect the Composite step, instead of adding this stress to the previous layers.

1. Styles

The browser starts calculating the styles to apply in elements — Recalculate Style.

2. Layout

In the following layer, the browser will start generating the shape and position for each of those elements — Layout. This is where the browser sets the page properties such as width and height, as well as its margins, or left/top/right/bottom for instance.

3. Paint

The browser will now start filling in the pixels for each element into layers. The properties that it uses are, for instance: box-shadow, border-radius, color, background-color, among others.

4. Composite

This is where you want to do your magic, as this is when the browser starts drawing all the layers in the screen.

Modern browsers can animate four styles pretty, pretty well, making use of the transform and opacity properties.

Position — transform: translateX(n) translateY(n) translateZ(n);

Scale — transform: scale(n);

Rotation — transform: rotate(ndeg);

Opacity — opacity: n;

How to Achieve the 60 Frames Per Second Mark

With this in mind, it’s time to roll up our sleeves and get to work.

Let’s start with the HTML. We’re going to create a very simple structure and put our app-menu inside a layout class.

Going About It the Wrong Way

See those properties we changed? You should avoid using transitions with the left/top/right/bottom properties. Those don’t create a fluid animation because they have the browser creating layouts each time, which will affect all of their children.

The result is something like this:

That animation is not smooth at all. We checked with DevTools Timeline to see what was happening under the hood, and this was the result:

Green areas represent the time spent rendering an animation.

This clearly shows FPS are irregular and that performance is quite slow.

“The green bar indicates FPS. A high bar indicates that the animation is rendering at 60 FPS. A low bar indicates sub-60 FPS. So, ideally, you want the green bar to be consistently high across the Timeline. Those red bars also indicate jank, so, alternatively, another way to gauge your progress is by eliminating those red bars.”Thank you, Kayce Basques!

Using Transform

The transform properties affect a Composite. Here we’re telling the browser that layers will be painted and ready to go as soon as the animation starts, so there are fewer hiccups when rendering the animation.

That’s exactly what Timeline is showing:

The results start getting better, FPS are more regular and, therefore, the animation is smoother.

Running Animations in GPU

Let’s take it up a notch, then. To really get it running smooth and buttery, we’re going to use the GPU to render the animation.

Though translateZ() or translate3d() will still be needed by some browsers as a fallback, the will-change property is the future. What this does is that it promotes the elements to another layer, so the browser doesn’t have to consider the layout render or painting.

See how smooth that is? Timeline substantiates that:

The animation’s FPS are much more constant and the animation renders much quicker here. But there’s still a long frame running there. There’s still a little bit of a bottleneck at the beginning.

Remember the HTML structure created right in the beginning? Let’s control the app-menu div on that structure by using JavaScript:

The problem here is that by adding that class within the layout div, we made the browser recalculate styles one more time — and that affects the rendering performance.

The 60 FPS Smooth as Butter Solution

What if we have the menu created outside of the viewport area, instead? Having this in an isolated area will ensure that you only affect the element that you really want to animate.

So, we propose the following HTML structure:

And now we can control the state of the menu in a slightly different way. We’re going to manipulate animations in a class that gets removed when the time of transition ends, by using the transitionend function in JavaScript.

Let’s put it all together and check the results, then.

Here’s a complete fully-enabled CSS3 example, with everything in its right place:

And what does Timeline show us?

Smooth as butter, see?

Edit (17th Aug): Due to popular demand, here’s a working example for you guys: