Multiple requestAnimationFrame performance

JavascriptJqueryPerformanceAnimationRequestanimationframe

Javascript Problem Overview


If I’m doing multiple animations, is it OK performance-wise to add multiple requestAnimationFrame callbacks? F.ex:

function anim1() {
    // animate element 1
}

function anim2() {
    // animate element 2
}

function anim3() {
    // animate element 3
}

requestAnimationFrame(anim1);
requestAnimationFrame(anim2);
requestAnimationFrame(anim3);

Or is it proven worse than using a single callback:

(function anim() {
    requestAnimationFrame(anim);
    anim1();
    anim2();
    anim3();
}());

I’m asking because I don’t really know what is going on behind the scenes, is requestAnimationFrame queuing callbacks when you call it multiple times?

Javascript Solutions


Solution 1 - Javascript

I don't think any of these answers really explained what I was looking for: "do n calls to requestAnimationFrame" get debounced (i.e. dequeued 1 per frame) or all get invoked in the next frame.

> When callbacks queued by requestAnimationFrame() begin to fire multiple callbacks in a single frame (mdn)

This suggests the latter, multiple callbacks can be invoked in the same frame.

I confirmed with the following test. A 60 hz refresh rate translates to a 17ms period. If it were the former, no 2 timestamps would be within 17ms of each other, but that was not the case.

let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

let update = async timestamp => {
  console.log('update called', timestamp)
  await sleep(10);
  requestAnimationFrame(update);
}

requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);

Solution 2 - Javascript

You should be using only one requestAnimationFrame call as calls to requestAnimationFrame do stack. The single callback version is thus more performant.

Solution 3 - Javascript

Someone benchmarked this. Let's talk...

https://jsperf.com/single-raf-draw-calls-vs-multiple-raf-draw-calls

I looked at the performance comparison (you should too). You're welcome to disagree. These are drawing primitives on a canvas element.

  		function timeStamp() {
  		  return window.performance && window.performance.now ? window.performance.now() : new Date().getTime();
  		}
  
  		function frame() {
  			drawCircle();
  			drawLines();
  			drawRect();
  		}
  
  		function render() {
  			if (timeStamp() >= (time || timeStamp())) {
  				time = timeStamp() + delayDraw;
  				frame();
  			} 
  			requestAnimationFrame(render);
  		}
  
  		function render1() {
  			if (timeStamp() >= (time || timeStamp())) {
  				time = timeStamp() + delayDraw;
  				drawCircle();
  			} 
  			requestAnimationFrame(render1);
  		}
  
  		function render2() {
  			if (timeStamp() >= (time || timeStamp())) {
  				time = timeStamp() + delayDraw;
  				drawRect();
  			} 
  			requestAnimationFrame(render2);
  		}
  
  		function render3() {
  			if (timeStamp() >= (time || timeStamp())) {
  				time = timeStamp() + delayDraw;
  				drawLines();
  			} 
  			requestAnimationFrame(render3);
  		}

I think this code is really benchmarking 7 calls to timestamp() vs 2 calls to timestamp(). Look at the difference between Chrome 46 and 47.

  • Chrome 46: 12k/sec (one call) vs 12k/sec (3 calls)
  • Chrome 47: 270k/sec (one call) vs 810k/sec (3 calls)

I think this is so well optimized that it doesn't make a difference. This is just measuring noise at this point.

My takeaway is this doesn't need to be hand-optimized for my application.

If you're worried about performance look at the difference between Chrome 59 (1.8m ops/sec) vs Chrome 71 (506k ops/sec).

Solution 4 - Javascript

The requestAnimationFrame binds a function call and returns the frameID. Requesting multiple frames is NOT the same like adding multiple event listeners to an event- each of your functions is called in another frame. So if you continuously (each function recalls itself recursively) request several frames you're loosing the benefit that all updates are rendered within one frame. So even if there is a high framerate animations may not look that smooth.

But: you can only use cancelAnimationFrame(frameID) for all methods and may need some extra code to cancel single animations

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid HellsingView Question on Stackoverflow
Solution 1 - JavascriptjunvarView Answer on Stackoverflow
Solution 2 - JavascriptErik SchierboomView Answer on Stackoverflow
Solution 3 - JavascriptMichael ColeView Answer on Stackoverflow
Solution 4 - JavascriptWolfgangView Answer on Stackoverflow