Javascript Timekeeping

Javascript has long had two pop­u­lar meth­ods for keep­ing time — setTimeout and setInterval — but these can behave errat­ic­ally and aren’t always suited for game loops.

Thankfully there is a new anim­at­ing func­tion on the block, which trans­par­ently man­ages things like fram­er­ate and stop­ping the anim­a­tion when it’s no longer visible.

request­An­im­a­tion­Frame

The new way of tim­ing your anim­a­tion is a little more intel­li­gent than the old setTimeout method. Modern browsers now sup­port the draft window.requestAnimationFrame call which is designed to allow as many frames as is reas­on­able per second.

When you call the func­tion, the browser places your call­back in a queue which fires after the browser determ­ines it’s appro­pri­ate, at a max­imum of 60 times per second. This is par­tic­u­larly use­ful because all the optim­isa­tion involving frame refreshes and keep­ing the user inter­face respons­ive are handled by the browser. Even bet­ter, when the tab isn’t act­ive or the browser is min­im­ised, it will pre­vent ren­der­ing frames needlessly.

You call it the same way you would call a setTimeout call, except that you don’t spe­cify an interval:

var renderFn = function(time){
    // animation goes here
    window.requestAnimationFrame(renderFn);
}

To use this func­tion­al­ity today you may need a shim to sup­port vendor pre­fixed imple­ment­a­tions, in which case there’s a good requestAnimationFrame shim avail­able on Paul Irish’s blog.

Uses for setTimeout

For com­plex games this might not be enough. If we want our game world to con­tinue work­ing while our browser is min­im­ised, we’re going to need to rely on setTimeout to con­tinue cal­cu­lat­ing tra­ject­or­ies, accu­mu­lat­ing money, or com­mu­nic­at­ing with the server.

In these cases we should note that rely­ing solely on setTimeout to keep pre­cise time is not recom­men­ded. It’s not designed for mis­sion crit­ical applic­a­tions, and can often be wrong depend­ing on how much load your browser is under at the time.

If you require an in-game timer to be some­what accur­ate (think net­work play), you can still use setInt­er­val, but you should rely on the Date object, or bet­ter still performance.now() to get the actual time and cal­cu­late from that. Using these date func­tions will reduce incon­sist­en­cies borne from a laggy browser.

Set Timeout Without Focus

Another inter­est­ing quirk with the timer func­tions is that some browsers throttle the calls when the page doesn’t have focus (ie if the user is on another tab).

The table below shows some data points I gathered non-scientifically from the setTimeout func­tion in my copy of Chrome. The reques­ted time is the inter­val reques­ted in the setTimeout call, whereas the other two columns show how long it actu­ally took to receive a call back:

Interval between set­Timeout call and actual call­back (Chrome 13).
Requested Time Actual Time When Focused Actual Time When Not Focused
1500 1500 1501
1125 1125 1125
843 843 843
632 633 632
606 606 606
605 605 1000
474 474 1000
355 355 1000
266 267 1000
199 199 1001
149 149 1000
111 112 1000
83 83 1001
62 62 1000
46 46 1000
34 34 1001
25 25 1000
18 18 1001
13 13 1000

One thing you might have noticed is that when the browser wasn’t focused, any inter­vals less than 606 mil­li­seconds were auto­mat­ic­ally adjus­ted up to 1000. This is pre­sum­ably designed to make the browser more respons­ive and reduce the impact of run­away tabs. This beha­viour appears to be con­sist­ent across Firefox and Chrome, although Opera doesn’t have this limitation.

The take away here is that if you need to keep crunch­ing num­bers in the back­ground, you should optim­ise your code to per­form with an inter­val of 606 mil­li­seconds or prefer­ably higher.

Conclusion

Use requestAnimationFrame wherever pos­sible to handle your game; it’s clever enough to pause the loop when the game isn’t being used and is optim­ised to get the most per­form­ance out of the hard­ware you’re using.

When you need to keep the game tick­ing along in the back­ground, use both func­tions in con­junc­tion with each other. Let requestAnimationFrame deal with updat­ing your graph­ics, but use setTimeout for game calculations.

Finally, never trust setTimeout (or requestAnimationFrame for that mat­ter) to keep the time for you. The Javascript Date object returns pre­cise tim­ing inform­a­tion which you can rely on to per­form calculations.

Leave a Reply