requestAnimationFrame

The secret to silky smooth JavaScript animation!

Creating an animation in JavaScript is one of the simplest things you can do. If you’ve ever tried to do it, you’ll most likely have used either the setTimeout or setInterval functions.

Here’s a typical example:

function draw() {
    // Drawing code goes here
}
setInterval(draw, 100);

This piece of code will call the draw function once every 100ms forever and ever, until at some point later the clearInterval function is called. An alternative to this code is to use a setTimeout function instead, this time inside the draw function:

function draw() {
    setTimeout(draw, 100);
    // Drawing code goes here
}
draw();

The single call to the draw function kicks off the animation loop, and from then on it will call itself repeatedly every 100ms.

Frame rate and setInterval

The smoothness of your animation depends on the frame rate of your animation. Frame rate is measured in frames per second (fps). Film usually runs at 24fps, video at 30fps. The higher this number is, the smoother your animation will look…to a point. More frames, means more processing, which can often cause stuttering and skipping. This is what is meant by the term dropping frames. Because most screens have a refresh rate of 60Hz, the fastest frame rate you should aim for is 60fps. Time for some math!

/**
 * 1s = 1000ms (remember that setInterval and setTimeout run on milliseconds)
 * 1000ms / 60(fps) = 16.7ms (we'll round this to 17)
 */
 
// Lights, camera…function!
setInterval(function() {
    animateEverything();
}, 17);

What’s wrong with setTimeout and setInterval?

OK, you’ve done this a million times before. What’s wrong with this picture? Well, a few things. Firstly, setTimeout doesn’t take into account what else is happening in the browser. The page could be hidden behind a tab, hogging your CPU when it doesn’t need to, or the animation itself could have been scrolled off the page making the update call again unnecessary. Chrome does throttle setInterval and setTimeout to 1fps in hidden tabs, but this isn’t to be relied upon for all browsers.

Secondly, setTimeout only updates the screen when it wants to, not when the computer is able to. That means your poor browser has to juggle redrawing the animation whilst redrawing the whole screen, and if your animation frame rate is not in synchronised with the redrawing of your screen, it could take up more processing power. That means higher CPU usage and your computer’s fan kicking in, or draining the battery on your mobile device. Nicolas Zakas does an excellent job explaining the impact timer resolution has on animation in a related article.

Another consideration is the animation of multiple elements at once. One way to approach this is to place all animation logic in one interval with the understanding that animation calls may be running even though a particular element may not require any animation for the current frame. The alternative is to use separate intervals. The problem with this approach is that each time you move something on screen, your browser has to repaint the screen. This is wasteful!

requestAnimationFrame to the rescue!

To overcome these efficiency problems, Mozilla (makers of Firefox) proposed the requestAnimationFrame function, which was later adopted and improved by the WebKit team (Chrome and Safari). It provides a native API for running any type of animation in the browser, be it using DOM elements, CSS, canvas, WebGL or anything else.

Here’s how you use it:

function draw() {
    requestAnimationFrame(draw);
    // Drawing code goes here
}
draw();

Great! It’s exactly like the setTimeout version but with requestAnimationFrame instead. Optionally you can pass a parameter along to the function that’s being called, such as the current element being animated like so: requestAnimationFrame(draw, element);

You may have noticed though that you don’t specify an interval rate. So how often is the draw function called? That all depends on the frame rate of your browser and computer, but typically it’s 60fps (which is cool as your computer’s display typically refreshes at a rate of 60Hz). The key difference here is that you are requesting the browser to draw your animation at the next available opportunity, not at a predetermined interval. It has also been hinted that browsers could choose to optimize performace of requestAnimationFrame based on load, element visibility (being scrolled out of view) and battery status.

The other beauty of requestAnimationFrame is that it will group all of your animations into a single browser repaint. This saves CPU cycles and allows your device to live a longer, happier life.

So if you use requestAnimationFrame all your animations should become silky smooth, synced with your GPU and hog much less CPU. And if you browse to a new tab, the browser will throttle the animation to a crawl, preventing it from taking over your computer whilst you’re busy. Yay!

Sounds great! Any problems?

Well, yes. Because this is a new API it’s only currently available in browsers via a vendor prefix, such as webkitRequestAnimationFrame in Chrome and Safari, and mozRequestAnimationFrame in Firefox. Browser support is not bad overall, and even Microsoft will support msRequestAnimationFrame in version 10 of Internet Explorer.

To get around the varied support, Eric Möller (Opera), Paul Irish (Google), and Tino Zijdel (Tweakers.net) have created a polyfill to make it simple to use again:

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
 
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
 
(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
    }
 
    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
 
    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());

Drop this into your code and you can use requestAnimationFrame as nature intended.

A simpler shim is available which uses only a setTimeout as a fallback. This is OK too, provided you put the call to requestAnimationFrame at the start of the loop rather than at the end. If you don’t, animation timing can become unpredictable as Erik Möller from Opera explains. So it’s probably better to stick to the more recent shim above :)

What if I want to set a frame rate?

One glaring problem remaining is this: how do you control the timing of an animation with requestAnimationFrame if you can’t specify the frame rate? Games typically require specific frame rates for their animations.

Stop! Before you go running back to setInterval, here’s a technique you can use:

var fps = 15;
function draw() {
    setTimeout(function() {
        requestAnimationFrame(draw);
        // Drawing code goes here
    }, 1000 / fps);
}

By wrapping the requestAnimationFrame in a setTimeout you get to have your cake and eat it. Your code gets the efficiency savings and you can specify a frame rate, up to 60fps.

A more sophisticated technique would be to check the number of milliseconds past since the last draw call and update the animation’s position based on the time difference. For example:

var time;
function draw() {
    requestAnimationFrame(draw);
    var now = new Date().getTime(),
        dt = now - (time || now);
 
    time = now;
 
    // Drawing code goes here... for example updating an 'x' position:
    this.x += 10 * dt; // Increase 'x' by 10 units per millisecond
}

Here’s another good example on GitHub of coding with ‘delta’ time-based animations. We’ve also created a simple bouncing ball demo to help get you started. Have a play and happy animating!

And to finish things off, here’s a video from Google I/O 2012 explaining requestAnimationFrame in nice friendly detail: