Mozilla

Animating with javascript: from setInterval to requestAnimationFrame

Animating DOM elements[1] or the content of a canvas is a classical use case for setInterval. But the interval is not as reliable as it seems, and a more suitable API is now available…

Animating with setInterval

To animate an element moving 400 pixels on the right with javascript, the basic thing to do is to move it 10 pixels at a time on a regular interval.

JSFiddle demo.

An HTML5 game based on this logic would normally run at ~60fps[2], but if the animations were too complex or running on a low-spec. device (a mobile phone for instance) and processing a frame were taking more than 16ms, then the game would run at a lower framerate: when processing 1 frame takes 33ms, the game runs at 30fps and game elements move twice as slowly as they should. Animations would still look smooth enough, but the game experience would be altered.

Animating at constant speed

To animate at constant speed, we need to calculate the time delta since the last frame and move the element proportionally.

Animating with requestAnimationFrame

Since the interval parameter is irrelevant in complex animations, as there’s no guarantee that it will be honored, a new API has been designed: requestAnimationFrame. It’s simply a way to tell the browser “before drawing the next frame on the screen, execute this game logic/animation processing”. The browser is in charge of choosing the best moment to execute the code, which results in a more efficient use of resources[3].

Here’s how an animation with requestAnimationFrame would be written.
Note: Following code snippets don’t include feature detections and workarounds necessary to work in current browsers. Should you want to play with them, you should try the ready-to-use animLoop.js.

Dealing with inactive tabs

requestAnimationFrame was built with another benefit in mind: letting the browser choose the best frame interval allows to have a long interval in inactive tabs. Users could play a CPU intensive game, then open a new tab or minimize the window, and the game would pause[4], leaving resources available for other tasks.
Note: the potential impact of such behavior on resource and battery usage is so positive that browser vendors decided to adopt it for setTimeout and setInterval as well[5].

This behavior also means that the calculated time delta might be really high when switching back to a tab containing an animation. This will result in animation appearing to jump or creating “wormholes[6], as illustrated here.

Wormholes can be fixed by clamping the time delta to a maximum value, or not rendering a frame when the time delta is too high.

JSFiddle demo.

Problems with animation queues

Libraries such as jQuery queue animations on elements to execute them one after the other. This queue is generally only used for animations that are purposefully consecutive.
But if animations are triggered by a timer, the queue might grow without bound in inactive tabs, as paused animations stack up in the queue. When switching back to affected tabs, a user will see a large number of animations playing consecutively when only one should happen on a regular interval:

JSFiddle demo.

This problem is visible in some auto-playing slideshows such as mb.gallery. To work around it, developers can empty animation queues before triggering new animations[7].
JSFiddle demo.

Conclusion

The delays of setTimeout and setInterval and of course requestAnimationFrame are unpredictable and much longer in inactive tabs. These facts should be taken into account not only when writing animation logic, but in fps counters, time countdowns, and everywhere time measurement is crucial.

[1] The DOM can now be animated with CSS3 Transitions and CSS3 Animations.
[2] 1 frame every 16ms is 62.5 frames per second.
[3] See the illustration of this fact on msdn.
[4] The behavior of requestAnimationFrame in inactive tabs is still being worked on at the w3c, and might differ in other browsers.
[5] See related Firefox bug and related chromium bug.
[6] This term was first coined by Seth Ladd in his “Intro to HTML5 Game Development” talk.
[7] See documentation of your js library, such as effects and stop() for jQuery.

Posted by on at

12 comments

Comments are now closed.

  1. Feross wrote on August 19th, 2011 at 11:02 pm:

    This is a really great overview of requestAnimationFrame. Thank you!

    <3 Mozilla Hacks blog.

  2. Pablo wrote on September 3rd, 2011 at 4:40 am:

    Great article, thanks.

    Just one question though, what does the “+new Date” mean? I never seen a plus sign preceding the new keyword.

    Thanks in advance!
    Pablo

    1. Kris Gray wrote on September 15th, 2011 at 12:03 pm:

      It converts the date to an integer and returns that which is new Date().getTime()

  3. Pablo wrote on September 16th, 2011 at 5:59 am:

    Thank you very much Kris!
    It’s not easy to google “+new Date” :(

    Pablo

  4. Chad Elliott wrote on January 1st, 2012 at 1:17 pm:

    It used to be best practice to put every animation into a single loop using a queue. With this new API, I keep seeing code that makes separate calls to requestAnimationFrame for each animation. Is the browser queuing the animations automatically behind the scenes before rendering a new fram or should we be creating our own API to queue the animations?

    Here’s an example of what I’m talking about.

    http://jsfiddle.net/THEtheChad/RUsnb/

  5. Erik Landvall wrote on May 21st, 2012 at 11:22 am:

    Just created a JavaScript based on thees principles. You can find it over at github: https://github.com/erik-landvall/animator

  6. Ivan Kuckir wrote on July 26th, 2012 at 12:49 am:

    Hi folks,
    I have written a “Tweener” based on this principle :) Check it out at tweener.ivank.net.

  7. Jens Ahrengot Boddum wrote on September 30th, 2012 at 2:34 pm:

    Great introduction to programmatic animation. One of the most intriguing and creative parts of front end development if you ask me.

    Now, when it comes to JavaScript animation, I feel like I should mention Greensock. It’s a new player in the world of animation frameworks and it performs above and beyond everything else currently available. If you’re interested, check out my tutorial: Greensock JavaScript Animation.

  8. Jens Ahrengot Boddum wrote on September 30th, 2012 at 2:48 pm:

    Whoops, broken link. Here you go: “http://ahrengot.com/tutorials/greensock-javascript-animation/

  9. zogzog wrote on December 25th, 2012 at 5:03 am:

    I have the following problem :
    i want to display a regular fade effect on random elements in the dom. This effect effect should last longer than the interval time.
    What i noticed using setinterval, is that the effect on one element is stopped as soon as another effect runs on another dom element. Moreover if i have a scrollbar in the main window, when i scroll all effects disapear : when the browser is “busy” the effects are stopped or not launched.
    Any idea of what is going on and how to workaround this ?
    Thanks a lot

  10. zogzog78 wrote on December 25th, 2012 at 11:23 am:

    Hello,
    i would like to update at a regular time the text of different elements of the dom with an animation (change of the background color). If i want an element of the dom to be modified every seconde : i use a setInterval to repeat the function which every seconde changes the values of different elements of the dom and changes their background color, i see that the animation (which could last for 3 seconds) to change the background color is simply stopped as soon as another dom element is changed every seconde.
    How is it possible to see the animation of 3 secondes for an element of the dom while another dom element is processed every second ?

  11. DC wrote on January 19th, 2013 at 10:36 pm:

    GREAT !…..thanx

Comments are closed for this article.