I’ve been working on a small canvas animation for [ffconf 2015](http://2015.ffconf.org) and realised two important optimisations that I’ve missed out on the past.

The net result: no more humming fan on my laptop.

[I’ve published 38 videos for new developers, designers, UX, UI, product owners and anyone who needs to conquer the command line today.](https://training.leftlogic.com/buy/terminal/cli2?coupon=BLOG\&utm_source=blog\&utm_medium=banner\&utm_campaign=remysharp-discount)

The result is this simple retro animation that will only last a few days in production, so I’ve included a tweaked version here:

For the sake of brevity (and actually getting this post written under the usual several hours), I’m just going to talk about what I changed.

Pinning FPS[](#pinning-fps)

I knew that the "right" approach was to use requestAnimationFrame (rAF) for animation, but my problems historically is that the call rate of my update function was way, way too often. This can either cause my animation to appear to be too quick, or results in the CPU overheating.

One nice advantage of rAF for animation is that it will stop firing when the tab is out of focus (i.e. if you switch to another tab). Whereas setInterval not only doesn’t hit the timing you want, but it’ll keep firing, burning up battery.

TIL requestAnimationFrame passes in a [high resolution timestamp](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#Parameters) to the callback.

Using the timestamp, we can get a delta of the last run, and if, and only if, the last frame was drawn X FPS ago, then we’ll draw a new frame. For example:

var lastFrameTime = 0;
function draw(elapsedTime) {
  // calculate the delta since the last frame
  var delta = elapsedTime - (lastFrameTime || 0);

  // queue up an rAF draw call
  window.requestAnimationFrame(draw);

  // if we *don't* already have a first frame, and the
  // delta is less than 33ms (30fps in this case) then
  // don't do anything and return
  if (lastFrameTime && delta < 33) {
    return;
  }
  // else we have a frame we want to draw at 30fps...

  // capture the last frame draw time so we can work out
  // a delta next time.
  lastFrameTime = elapsedTime;

  // now do the frame update and render work
  // ...
}

Minimise your paints[](#minimise-your-paints)

Originally my demo was drawing a number of squares that would scale towards the viewer giving the impression of movement. Initially innocuous:

function draw() {
  // ... calculate x, y, scale, etc
  // makes the shape: |_|
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x, y + y2);
  ctx.lineTo(x + x2, y + y2);
  ctx.lineTo(x + x2, y);
  ctx.stroke();
  ctx.closePath();
}

// update is called on a new frame
function update() {
  // ... update state then draw:
  for (i = 0; i < boxes.length; i++) {
    boxes[i].draw();
  }
}

This would be repeated for every "box" animating towards the viewer. Since I’m just drawing lines, I could batch all these together all in one go and group the collective shapes under one path, then run a single stroke:

function draw() {
  // ... calculate x, y, scale, etc
  // makes the shape: |_|
  ctx.moveTo(x, y);
  ctx.lineTo(x, y + y2);
  ctx.lineTo(x + x2, y + y2);
  ctx.lineTo(x + x2, y);
}

// update is called on a new frame
function update() {
  // ... update state then draw:
  ctx.beginPath();
  for (i = 0; i < boxes.length; i++) {
    boxes[i].draw();
  }
  ctx.stroke();
  ctx.closePath();
}

It’s a fairly tiny optimisation, but the result is the same, but with less interaction with the canvas, and given we’re aiming to be in and out quickly, it’s not a bad thing.

Single rAF handler[](#single-raf-handler)

If you’ve got more than one animation running, you’ll need to set-up multiple callbacks to requestAnimationFrame, but having multiple rAF callbacks isn’t ideal, and starts to get unwieldy when you’re working with others who also want to queue up their animations.

You really want everything to be handled in a single rAF handler.

I’ve written a small gist called [raf.js](https://gist.github.com/remy/36f388d72c1ef161582f) that allows me to put all my rAF calls through a single handler (and added some niceties like the FPS pinning and a running boolean flag).

Comments

Lock Thread

Login

Add Comment[M ↓   Markdown]()

[Upvotes]()[Newest]()[Oldest]()

![](/images/no-user.svg)

Patrick McMahon

0 points

8 years ago

you should call your requestAnimationFrame() only after the frame is ready to be drawn or you could end up with relics from the past draw. You can get all your complex animations done using only one rAF and shouldn’t use more then one. You put your draw actions in your update and that’s not how normal game engines do this. all logic should be in the update method of an entity. while the drawing of the entity should be all in the entities draw method. You can use a 2nd canvas as a buffer for your draw. with the rAF at the end it can skip a frame if the frame is not ready to be drawn.This will make it so every frame is drawn perfectly.

![](/images/no-user.svg)

Jeff Bellsey

0 points

8 years ago

Did you do any perf testing on batching multiple handlers through a single rAF callback versus multiple rAFs? Is there someone who has? There’s probably a threshold number for each browser at which batching is useful, but it may prove to be a valueless micro-opt. Thoughts? Experiments?

![](/images/no-user.svg)

rem

0 points

8 years ago

To answer:\ I did. I’m not sure, probably. From what I saw: 1 rAF was always performing better than > 1.

![](/images/no-user.svg)

WebReflection

0 points

8 years ago

thinking out loud and tried already with some interesting result; I wonder if var enterFrame = setInterval(requestAnimationFrame, 1000 / 30, draw) would bring some benefit. That requestAnimationFrame will be invoked at 30FPS even if the tab is non focused, which is the bad part, but the interval will grant 30FPS attempts (probably smoother) and a tab without focus won’t drain the battery anyway since rAF won’t actually do a thing there beside being invoked (which should be cheap enough). TL;DR combining both we have less calls to the draw function, less logic to care in terms of time operations, and 30FPS attempts to rAF. Any thoughts on this?

![](/images/no-user.svg)

WebReflection

0 points

8 years ago

Hi Remy, overall nice hints but you should probably mention a couple of extra things: 1) you are lowering the (in)famous 60FPS achievement to 30FPS. This is something I’ve done in the past too, even via setTimeout, and it works well on older devices but it will run averything twice as slow (so increments, movements, coordinates changes should be also be divided by 2 if developed to run at 60FPS) - 2) you cannot draw inside the same path using different stroke/fill style and alpha chanel would be flat. Accordingly, it’s not a universal solution. I know 'cause I’ve implemented clouds and nyan with different depths and stroking at once was creating a big one shape but removing the effect. Moreover, the most performance oriented way to deal with paths in 2d canvas will be soon the Path2D [https://developer.mozilla.o…​;](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) . Last, but not least, it’s a good technique to put, together with the 30FPS limiter, if you really need it, a flag that is true only when the canvas is actually visible. Grab its coordinates on the page and its height (divided by devicePicelRatio if used) and within a scroll handler set such flag to false when the user is not seeing the canvas. This will keep the scrolling smooth in not so powerful devices when the animation won’t even need to run. Basically an extra smarter hint for the browser, like the one implemented native when the TAB is not the active one. You can see an example here: [https://www.webreflection.c…​;](https://www.webreflection.co.uk/courses)

![](/images/no-user.svg)

jaffathecake

0 points

8 years ago

The 33ms raf thing is making me pull a bit of a face. The intention of raf is to allow code to run straight after a v-sync, using 33ms is kinda assuming 60hz, which won’t always be true. Thankfully it should play nice with 50hz, throttling down to 25fps.

Given that your intention is to avoid caning the CPU, I wonder if there’s an API we need here. Something to give an impression of your resource usage over the previous second. This could enable you to throttle depending on the device capabilitiy

![](/images/no-user.svg)

rem

0 points

8 years ago

Totally agree. Hard-coding that I want it to run at a specific speed feels…​weird (and wrong). I just want it to run at 30fps. Which actually means I don’t want it running full whack and caning the CPU, and throttling is exactly what I wanted, but in a reusable way.

![](/images/no-user.svg)

sroucheray

0 points

8 years ago

@rem You could check this simple script I published to control an animation framerate

![](/images/no-user.svg)

jaffathecake

0 points

8 years ago

Well, you’d take 60fps if it didn’t cane the CPU on that device right?

There’s also a usecase for 30fps for stylistic reasons.

![](/images/no-user.svg)

WebReflection

0 points

8 years ago

I had exact same reaction when I’ve seen the "lower to 30FPS" bit, there are cases where you really would like a rather smooth enough 30FPS than a clunky CPU greedy 36 to 56 so if only rAF would accept a second argument (backward compact) so you could requestAnimationFrame(callback, 1000 / 30) it would be pretty awesome

![](/images/no-user.svg)

rem

0 points

8 years ago

But are you still saying I shouldn’t pin the fps?

![](/images/no-user.svg)

jaffathecake

0 points

8 years ago

scratches head I dunno, I don’t think it’s going to do any harm in future, aside from needlessly limiting framerate on devices that could easily do 60fps (either due to browser improvements or more powerful hardware)

[Commento](https://commento.io)