Please note: this blog has been migrated to a new location at https://jakesgordon.com. All new writing will be published over there, existing content has been left here for reference, but will no longer be updated (as of Nov 2023)

Did I Stutter ?

Tue, Jun 7, 2011

This is a request for help…

I’ve been experimenting with the HTML5 canvas for simple games like pong or animating starfields and I’m currently working on a breakout style game.

They all work well, but they all have a slightly stuttering animation loop.

There has been a lot of talk about smoothing out browser based animation using requestAnimationFrame but I have yet to see an example that is truly smooth and without stutter, and I have not been able to see any real improvement in my own examples using this over a more traditional setTimeout or setInterval loop.

You can see the problem right here:

And you can find the source code on github.

It starts off with initialization:

  var canvas     = document.getElementById(id);
  var ctx        = canvas.getContext('2d');
  var width      = canvas.width  = canvas.offsetWidth;
  var height     = canvas.height = canvas.offsetHeight;
  var color      = { background: '#EEE', ball: '#111' };
  var size       = 20;
  var x          = 50;
  var y          = 50;
  var dx         = 0.1;
  var dy         = 0.1;
  var maxx       = width  - size;
  var maxy       = height - size;
  var timestamp  = function() { return Date.now(); }

The render function in this example is trivial:

  var render = function() {
    ctx.save();
    ctx.fillStyle = color.background;
    ctx.fillRect(0, 0, width, height);
    ctx.fillStyle = color.ball;
    ctx.fillRect(x, y, size, size);
    ctx.restore();
  }

The update function is a standard bouncing ball:

  var update = function(dt) {

    x = x + (dx * dt);
    y = y + (dy * dt);
    
    if ((dx < 0) && (x <= 0))
      dx = -dx;
    else if ((dx > 0) && (x >= maxx))
      dx = -dx;
    
    if ((dy < 0) && (y <= 0))
      dy = -dy;
    else if ((dy > 0) && (y >= maxy))
      dy = -dy;

  }

And the game loop uses requestAnimationFrame:

  var last = timestamp();
  var frame = function(now) {
    update(now - last);
    render();
    last = now;
    requestAnimationFrame(frame);
  };
  requestAnimationFrame(frame);

I’ve refactored this game loop to use different strategies, using setInterval, or setTimeout instead of requestAnimationFrame, and experimented with various theories:

The code on github is down to its bare bones and I am reaching out looking for suggestions, fixes, or, quite possibly, somebody to explain to me that what I’m looking for is simply not possible in today’s browsers with their low fidelity timing mechanisms.

Or, better yet, fork the github project, fix it, and send me a pull request. That would be fantastic!

I’m testing in

So far, unusually, IE9 seems to be the smoothest… and it doesn’t support requestAnimationFrame !

Any suggestions ?