Learning material for professionals eager to explore the science behind building a successful online product

TALK SCRIPT: 60FPS animations on the Web

This is the script of a talk I gave at the Turin Web Performance Meetup. You can find the integral talk in italian on YouTube.

You're at the cinema. The theater is stuffed and you are sitting right in the middle of the row. At some point, they pause the show and the user comes in. She asks you to shift to the seat on your right, but nobody can switch place with you.

A bit annoyed, you get up and what does it happen?

The whole row has to stand up and shift one place to the right. There's a big confusion and a lot of time gets wasted.

There, if you felt annoyance inside of you by thinking of this usher who caused all this mess, hold tight to it. It's going to be useful later on.

What I want to talk about to you today is the concept of animation on the Web. In particular, I'd like that you walk away from this talk with a better understanding of the following:

Most CSS properties force expensive recalculation on the browser whenever their value changes, therefore, transitioning those properties over time result in continuous "expensive recalculations" many times per second and the corresponding animation will look laggy.

What jank is and why it happens

But why does forcing the browser to compute stuff affect an animation's performance?

Well, let's think about it. What's an animation made of? It consists of a regular and consistent sequence of frames.

The illusion of movement is given by the rapid alternation of pictures, or frames, each one representing a slightly different state of the motion

Jank, or lag, in an animation is the visible manifestation of the renderer skipping a frame. In other words, whenever a frame lasts on screen sensibly longer than the others, lag is perceived.

Alright, that's clear. But why would that happen? Why would a frame last on screen longer than needed? What are the root casues of jank?

The main reason for a frame to stay on screen longer than needed, is the following frame not being ready.

Possible causes of long frames

Let's see what could keep the browser busy during the duration of a frame (pic shamelessy stolen from a Google Developers's article, which you should read):

The process of "making" a frame (at least in Blink/Chrome) is composed of these 5 main phases, and each one of these can be subject to delays, thus causing jank.

During JavaScript execution, the main thread blocks. Thus, if it spends a lot of time running your program, it will easily end up skipping a frame.

After this first phase, the browser needs to compute new styles (e.g- a new CSS class has been added) and all layout information (size and shape of DOM elements). Both tasks can be very costly, according to the size of the DOM. Browsers are smart and limit the scope of these steps to the portion of the tree that actually changed, but it can still be a cause of jank.

Last two phases are painting and compositing.

Painting is the actual process of pushing pixels on screen. Browsers incrementally update only those parts that need repainting, in order to avoid painting the whole page on every frame.

Painting can be done onto multiple layers: think of Photoshop layers, where every layer can be more or less in the foreground. For this reason, there is still the need to composite those layers together, in the correct order, so that the final result can be displayed to the user.

What we can do to help the browser hit those elusive 60fps

This is when that feeling of annoyance at the cinema comes in handy. In the browser, the people sitting next to each other are the elements of the page. The usher that forces everyone to move is us, the developers.

If you think about it, it would be much simpler if the person who's called to move stood up and made a step forwards.

And this is precisely the solutions we are looking for. We need to be able to tell the browser to paint the moving elements (e.g- a side menu) on its own layer. This way, on every frame, it would just suffice to repeat the compositing phase: the browser would move this new layer and recomposite the layers in a single image.

In addition to this, the compositing phase is very cheap, since we can offload it to the GPU, which is perfect for this kind of jobs.

The benefits are multiple:

  • Compositing layers on the GPU is more efficient than it is on the CPU, since the former is designed specifically for these types of workloads
  • The GPU and the CPU can work together and operate at the same time, achieving true parallelism

Getting practical

Nice. So what can we do, practically, to act on this knowledge?

We are missing one last piece of the puzzle: not all CSS properties are made equal.

When their value changes, some of them trigger a complete repaint, some of them cause the browser to recompute layout information, some of them only need compositing to take place.

There it is our solution: we could animate only those properties. But how do we know which ones they are?

The long answer would be going to a site that tells you what parts of the pipeline a given property is going to trigger.

The short answer is: transition transform and opacity whenever possible.

That sounds limiting, right? Well, if you think about it there are a lot of animations that can be built this way. Transform allows you to move, scale and rotate an element. Opacity allows you to play with its transparency. You'd be surprised by how much can be done with just these two properties.

Before you leave and go on promoting all your elements to their own layers, there a couple of things that you need to know:

  • The browser can't always identify the elements that will need their own layers (for instance, when the transform is added via JavaScript). It's therefore necessary that we hint the browser, thanks to the new standard will-change: transform.

  • Every layer has a cost. The more you create, the heavier it becomes for the browser, especially in terms of occupied memory. Try to limit your layers count as much as possible.

I hope that this presentation encouraged you to learn more about this world, and I'd like to leave you with a couple of interesting reads.

Care to leave a comment? Drop down a line on the Twitters