Controlling the Frame Rate with requestAnimationFrame

control fps

Limiting the frame rate while using requestAnimationFrame can be a common want especially when coding Games where you want your animations and mechanics to not exceed a particular mark of frames per second. Let’s go through 2 ways of doing it.

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

Quick and Easy Way

Using setTimeout inside the rAF method is an easy way.

var fps = 30;

function draw() {
	setTimeout(function() {
		requestAnimationFrame(draw);

		// ... Code for Drawing the Frame ...

	}, 1000 / fps);
}

draw();

Nothing too fancy, simple enough!

Better Refined Approach

Ok, browsers cannot optimize setTimeout or setInterval. So it’s kinda better to do our own calculations and restrict the frame rate. Let’s see how.

var fps = 30;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;
 
function draw() {
	
	requestAnimationFrame(draw);
	
	now = Date.now();
	delta = now - then;
	
	if (delta > interval) {
		// update time stuffs
		
		// Just `then = now` is not enough.
		// Lets say we set fps at 10 which means
		// each frame must take 100ms
		// Now frame executes in 16ms (60fps) so
		// the loop iterates 7 times (16*7 = 112ms) until
		// delta > interval === true
		// Eventually this lowers down the FPS as
		// 112*10 = 1120ms (NOT 1000ms).
		// So we have to get rid of that extra 12ms
		// by subtracting delta (112) % interval (100).
		// Hope that makes sense.
		
		then = now - (delta % interval);
		
		// ... Code for Drawing the Frame ...
	}
}

draw();

Very simple code. All we do is set our frames per second and intervals between each frame. In the drawing function we deduct our last frame’s execution time from the current time to check whether the time elapsed since the last frame is more than our interval (which is based on the fps) or not. If the condition evaluates to true, we set the time for our current frame which is going to be the “last frame execution time” in the next drawing call.

Eventually all our drawing code is executed.

Demo Time

See the Pen Limiting FPS (framerate) with Request Animation Frame by Rishabh (@rishabhp) on CodePen.

No canvas based fancy animations. Just a little piece of data injected in the DOM to give you a clear idea.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

10 thoughts on “Controlling the Frame Rate with requestAnimationFrame”

  1. I wrapped your approach in a really simple gameloop. You can set the framerate as second parameter and even via this.fps. But to note, this is just a proof of concept:

    var GameLoop = function(fn, fps){
        fps = fps || 60;
    
        var now;
        var delta;
        var interval;
        var then = new Date().getTime();
    
        var frames;
        var oldtime = 0;
    
        return (function loop(time){
            requestAnimationFrame(loop);
    
            interval = 1000/(this.fps||fps);
            now = new Date().getTime();
            delta = now - then;
    
            if (delta > interval) {
                // update time stuffs
                then = now - (delta % interval);
    
                // calculate the frames per second
                frames = 1000/(time-oldtime)
                oldtime = time;
    
                // call the fn
                // and pass current fps to it
                fn(frames);
            }
        }(0));
    };
    
    var set;
    document.onclick = function(){
        set = true;
    };
    
    GameLoop(function(fps){
        if(set) this.fps = 10;
        console.log(fps);
    }, 30);
    
  2. shouldn’t fps be called maxFPS ? I assume the rAF call executes whenever the browser can fit it into the execution flow so if the browser is really busy you might get a smaller fps than the one you specified. Isn’t that how it goes…?

    1. You’re right. If the browser is really busy then we may get a smaller FPS than the one specified. To overcome this problem, we can use a for loop to run the animation updates as many times as interval goes into delta, i.e., parseInt(delta / interval);.

      Hope that helps!

  3. Your fps calculation is useless and wrong. It counts (i would say) avarage fps (but it is not true too ) and gives no information about performance. I run your example, switched tab and returned after long time and now your fps counter shows 3 fps…

  4. Hi! So I fiddled around with your code, and got actual 30 FPS by changing then=now-(delta%interval) to then+=interval, but I’m pretty sure this isn’t new to you, and there must be a good reason for using your method. If that’s the case, could you please explain the benefits of doing it your way? Thanks.

    1. Unless this post was updated I think he answered this question already:

      ” // Just `then = now` is not enough.
      // Lets say we set fps at 10 which means
      // each frame must take 100ms
      // Now frame executes in 16ms (60fps) so
      // the loop iterates 7 times (16*7 = 112ms) until
      // delta > interval === true
      // Eventually this lowers down the FPS as
      // 112*10 = 1120ms (NOT 1000ms).
      // So we have to get rid of that extra 12ms
      // by subtracting delta (112) % interval (100).
      // Hope that makes sense.”

Leave a Reply to Jeremy Iglehart Cancel reply

Your email address will not be published. Required fields are marked *

*