Tuesday, March 29, 2011
The web is becoming more interactive and animated day by day. Many web pages use the Canvas element to draw rich 2D content via the 2D context or modify DOM elements on the fly. These pages generally use the setTimeout or setInterval APIs to receive frequent callbacks, allowing them to redraw their content periodically, or use DHTML to move elements on the page. As 3D content drawn using the WebGL API increases in popularity, it will use similar animation techniques.
Unfortunately, setTimeout and setInterval don’t take into consideration whether the destination element, or even the tab that contains it, is actually visible. So, pages with high-frequency timers will consume CPU resources even if the tab is in the background. On laptops, netbooks, and mobile devices of all kinds, reducing CPU consumption is essential in order to prolong battery life. Additionally, excess CPU consumption by background tabs reduces the smoothness of animations on the foreground tab.
Excessive CPU consumption by timers on web pages is not a theoretical problem. We have measured web sites containing mostly static text content firing timers at a rate of over two hundred per second.
Mozilla recently introduced the experimental mozRequestAnimationFrame API, which has different semantics than setTimeout or setInterval. Instead of the developer specifying a target frame rate, the browser runs the given callback when it is ready to produce the next animated frame. The callbacks are specifically known to be relevant to the animation of the page, and don’t run too often.
An experimental webkitRequestAnimationFrame API has been upstreamed to WebKit, and is available starting in Chrome 10. This is essentially the same as mozRequestAnimationFrame, but supports an optional second argument which is the element that the callback intends to animate. This additional information will allow the browser to avoid animating elements that are not visible to the user. See this bug report for more details. Chrome doesn’t run requestAnimationFrame callbacks for background tabs at all, which dramatically reduces CPU consumption when multiple tabs containing animated content are in the same window.
The WebGL samples project contains a three dimensional graphics library that has been modified to use requestAnimationFrame rather than setTimeout or setInterval. Take a look at this library for a good example of how to convert existing timeout based animations to the new style, while preserving compatibility with browsers that don’t support requestAnimationFrame.
In the forthcoming Chrome 11 release, we plan to reduce CPU consumption even for pages that are using setTimeout and setInterval. For background tabs, we intend to run each independent timer no more than once per second. This change has already been implemented in the Chrome dev channel and canary builds. While there may be some compatibility impact for web pages, we believe that improving the user experience for the foreground tab, and increasing battery life, are problems needing to be addressed. Please send us your comments on this planned change.