Adobe

decor

Web Platform Team Blog

Making the web awesome

CSS animations and transitions performance: looking inside the browser

You’ve probably used CSS Animations or CSS Transitions in a project. (If not, check out CSS-Trick’s almanac entries on animations and transitions.) Some of your animations might have performed smoothly. Other might have appeared choppy. Do you wonder why?

In this article, we’ll explore how browsers handle CSS Animations and CSS Transitions, so you can develop intuition around whether an animation is likely to perform well — before writing any code! With this intuition, you’ll be able to make design decisions that play well with the browser and result in silky smooth user experiences.

Browser Internals

Let’s pop the hood of the browser, and look around. Once we understand how it works, we can drive it better.

Modern browsers typically have two important threads of execution. These threads work together to render a web page:

  • The main thread
  • The compositor thread

Typically, the main thread is responsible for:

  • Running your JavaScript.
  • Calculating your HTML elements’ CSS styles.
  • Laying out your page.
  • Painting your elements into one or more bitmaps.
  • Handing these bitmaps over to the compositor thread.

Typically, the compositor thread is responsible for:

  • Drawing bitmaps to the screen via the GPU.
  • Asking the main thread to update bitmaps for visible or soon-to-be-visible parts of the page.
  • Figuring out which parts of the page are visible.
  • Figuring out which parts are soon-to-be-visible when you’re scrolling.
  • Moving parts of the page when you scroll.

The main thread can be busy for long periods of time running your JavaScript or painting a large element. While it’s busy, it’s not responsive to user input.

On the other hand, the compositor thread tries to stay extremely responsive to user input. The compositor tries to redraw the page 60 times per second when the page is changing, even if the page is incomplete.

For example, when the user scrolls a page, the compositor thread asks the main thread to update the bitmaps for newly visible parts of the page. However, if the main thread doesn’t respond quickly enough, the compositor doesn’t wait. The compositor draws the parts of the page it has so far and draws white elsewhere.

The GPU

I mentioned the compositor thread draws bitmaps to the screen using the GPU. Let’s quickly go over the GPU.

The GPU is a chip found in most phones, tablets, and computers today. It’s extremely specialized, meaning it’s really good at certain things, and it’s not that great at others.

GPUs are really fast at:

  1. Drawing to the screen.
  2. Drawing the same bitmap over and over again.
  3. Drawing the same bitmap in a different position, rotation, or scale.

GPUs are relatively slow at:

  1. Loading bitmaps into their memory.

transition: height

Now that we have a rough idea of the software and hardware running our page, let’s look at how the browser’s main thread and compositor thread work together to perform a CSS Transition.

Suppose we’re transitioning an element’s height from 100px to 200px, like so:


div {
    height: 100px;
    transition: height 1s linear;
}

div:hover {
    height: 200px;
}

The main thread and the compositor thread will perform operations according to the timeline diagram below. Note that operations in orange boxes are potentially time-consuming. Operations in blue boxes are quick.

animate-height-2x

As you can see, there are lots of orange boxes, meaning the browser has to work pretty hard! This means the transition might be choppy.

In every frame of the transition, the browser has to perform layout, painting, and uploading new bitmaps to the GPU. As we learned, loading bitmaps into GPU memory can be a relatively slow operation.

The reason the browser has to work so hard every frame is because the contents of the element keep changing. Changing an element’s height may cause its child elements to also change in size, so the browser has to perform layout. After layout, the main thread has to regenerate the bitmap for the element.

transition: transform

So, height can be somewhat expensive to transition. Is there something cheaper?

Suppose we’re scaling an element from half size to full size. Also suppose we’re using the CSS transform property to scale it and the CSS transition property to animate the scaling, like so:


div {
    transform: scale(0.5);
    transition: transform 1s linear;
}

div:hover {
    transform: scale(1.0);
}

Let’s look at the timeline diagram for this case:

animate-transform-2x

We see a lot less orange this time, meaning the animation will probably be smooth! So, how is animating an element’s transform different than animating its height?

By definition, the CSS transform property does not change the layout of an element or the elements around it. It affects the element as a whole- it scales the whole element or rotates the whole element or moves the whole element.

This is great news for the browser! The browser only has to generate the bitmap for the element and upload it to the GPU at the start of the animation. After that, the browser doesn’t have to do any more layout, painting, or bitmap uploading. Instead, the browser can leverage the GPU’s special ability to draw the same bitmap in a different position, rotation, or scale quickly.

Design Decisions

So, does this mean we shouldn’t animate an element’s height? No. Sometimes it’s exactly what your design warrants, and the animation could be fast enough. Maybe your element is isolated, and doesn’t cause other parts of the page to be laid out again. Maybe your element is simple to repaint, and the browser can do it quickly. Maybe your element is small, and the browser only has to upload a small bitmap to the GPU.

Of course, if you can animate a “cheaper” property like CSS transform instead of a more expensive property like CSS height, and there is no impact on your design vision, do that. For example, lets say your design involves a button that reveals a menu when tapped. Instead of animating the menu’s CSS top or height properties to reveal it, try animating the element’s CSS transform property for a similar or identical effect.

The CSS properties that are particularly fast to animate include:

This list is somewhat limited today, but as browsers advance, you’ll see more and more CSS properties becoming fast to animate. Also, don’t discount the current list. You might be surprised at just how many rich effects you can create by combining these properties. Get creative!

6 Comments

  1. March 18, 2014 at 10:13 pm, Bernard Winslow said:

    Thanks for the great write up! Is your list of fast properties exhaustive?

    The odd thing about these properties is that you can do the same thing several ways. To make an element larger, you can increase the font-size or transform the element, but transforming will usually be faster (I think). So, you really have to watch how you do things.

    The second paragraph under the heading “transition: height” has an accidental div that somebody forgot to escape.

  2. March 20, 2014 at 3:52 pm, Max Vujovic said:

    Thanks for the comment!

    The list of fast properties is exhaustive right now, as far as I know. Browser vendors are working on adding more properties to the list, though. For example, browsers might hardware accelerate “background-color” animations in the future.

    As you point out, there are many ways to achieve a similar effect. So for now, if you want to animate an element’s background color, it’d be better to put the background in its own element and animate the hue-rotate() filter function in the CSS filter property, since CSS filters are hardware accelerated.

    In your example, animating font-size is likely to be more expensive than animating transform because the browser will have to redraw all of the characters bigger, and the text might wrap differently, so the browser will have to do line layout. On the other hand, transform will just scale the element bitmap as a whole and won’t redraw the characters or relayout the text.

  3. March 27, 2014 at 10:10 am, Herrington Darkholme said:

    Thanks for the great write up!
    I have heard about repaint and reflow. But what’s the difference between ‘paint’ and ‘draw’?

    Google didn’t give me answer. I can hardly distinguish them in plain English, so maybe they are jargon for browsers?

    • March 27, 2014 at 11:33 am, Max Vujovic said:

      Great question. In English, “paint” and “draw” are fairly similar. In browser jargon, they have more specific meanings.

      “Painting an element” means generating the pixels for the element’s appearance. For example, let’s say an element has a gray background, rounded corners, and some text. When the browser “paints” the element, it figures out which pixels to fill in (and which pixels not to fill in) for the background and the rounded corners. Then, it figures out which pixels to fill in for the text. Typically, the browser will write these pixels to an offscreen bitmap. An offscreen bitmap is just an area of memory (RAM) that will store the color values for each pixel.

      “Drawing” is a more generic term. It’s used much more loosely than painting. Sometimes you’ll hear “drawing the element to a bitmap”, which means the same thing as “painting the element to a bitmap”. Other times, you’ll hear “drawing to the screen”, which is different than painting. For example, drawing to the screen with the GPU means sending instructions to the GPU to copy an (already painted) bitmap to the screen. (Note that after “painting”, the browser will first have to copy the generated bitmap from CPU RAM to GPU RAM before telling the GPU to display it somewhere on the screen).

      • March 28, 2014 at 7:45 pm, Herrington Darkholme said:

        Thank you for your Great explanation! The just balance between technical details and descriptive clearness makes much sense to me.

  4. March 31, 2014 at 6:40 pm, Carolina said:

    Great Post. So many times I forget to really think through all this properties, like the load on GPU, speed, load time and so on. The most hard ones are the pages where after scrolling you get into one more, then another, and another animation. Especially what I don’t take into consideration – and would suggest everyone to look at – is that today users have more tabs opened. Imagine if they have 10 tabs with flash ads on and then they open your tab with some heave transition/animation.