[javascript] Controlling fps with requestAnimationFrame?

It seems like requestAnimationFrame is the de facto way to animate things now. It worked pretty well for me for the most part, but right now I'm trying to do some canvas animations and I was wondering: Is there any way to make sure it runs at a certain fps? I understand that the purpose of rAF is for consistently smooth animations, and I might run the risk of making my animation choppy, but right now it seems to run at drastically different speeds pretty arbitrarily, and I'm wondering if there's a way to combat that somehow.

I'd use setInterval but I want the optimizations that rAF offers (especially automatically stopping when the tab is in focus).

In case someone wants to look at my code, it's pretty much:

animateFlash: function() {
    ctx_fg.fillStyle = 'rgba(177,39,116,1)';
    ctx_fg.strokeStyle = 'none';
    for(var i in nodes) {
    var instance = this;
    var rafID = requestAnimationFrame(function(){

    var unfinishedNodes = nodes.filter(function(elem){
        return elem.timer < timerMax;

    if(unfinishedNodes.length === 0) {

Where Node.drawFlash() is just some code that determines radius based off a counter variable and then draws a circle.

The answer is

Update 2016/6

The problem throttling the frame rate is that the screen has a constant update rate, typically 60 FPS.

If we want 24 FPS we will never get the true 24 fps on the screen, we can time it as such but not show it as the monitor can only show synced frames at 15 fps, 30 fps or 60 fps (some monitors also 120 fps).

However, for timing purposes we can calculate and update when possible.

You can build all the logic for controlling the frame-rate by encapsulating calculations and callbacks into an object:

function FpsCtrl(fps, callback) {

    var delay = 1000 / fps,                               // calc. time per frame
        time = null,                                      // start time
        frame = -1,                                       // frame count
        tref;                                             // rAF time reference

    function loop(timestamp) {
        if (time === null) time = timestamp;              // init start time
        var seg = Math.floor((timestamp - time) / delay); // calc frame no.
        if (seg > frame) {                                // moved to next frame?
            frame = seg;                                  // update
            callback({                                    // callback function
                time: timestamp,
                frame: frame
        tref = requestAnimationFrame(loop)

Then add some controller and configuration code:

// play status
this.isPlaying = false;

// set frame-rate
this.frameRate = function(newfps) {
    if (!arguments.length) return fps;
    fps = newfps;
    delay = 1000 / fps;
    frame = -1;
    time = null;

// enable starting/pausing of the object
this.start = function() {
    if (!this.isPlaying) {
        this.isPlaying = true;
        tref = requestAnimationFrame(loop);

this.pause = function() {
    if (this.isPlaying) {
        this.isPlaying = false;
        time = null;
        frame = -1;


It becomes very simple - now, all that we have to do is to create an instance by setting callback function and desired frame rate just like this:

var fc = new FpsCtrl(24, function(e) {
     // render each frame here

Then start (which could be the default behavior if desired):


That's it, all the logic is handled internally.


var ctx = c.getContext("2d"), pTime = 0, mTime = 0, x = 0;_x000D_
ctx.font = "20px sans-serif";_x000D_
// update canvas with some information and animation_x000D_
var fps = new FpsCtrl(12, function(e) {_x000D_
 ctx.clearRect(0, 0, c.width, c.height);_x000D_
 ctx.fillText("FPS: " + fps.frameRate() + _x000D_
                 " Frame: " + e.frame + _x000D_
                 " Time: " + (e.time - pTime).toFixed(1), 4, 30);_x000D_
 pTime = e.time;_x000D_
 var x = (pTime - mTime) * 0.1;_x000D_
 if (x > c.width) mTime = pTime;_x000D_
 ctx.fillRect(x, 50, 10, 10)_x000D_
// start the loop_x000D_
// UI_x000D_
bState.onclick = function() {_x000D_
 fps.isPlaying ? fps.pause() : fps.start();_x000D_
sFPS.onchange = function() {_x000D_
function FpsCtrl(fps, callback) {_x000D_
 var delay = 1000 / fps,_x000D_
  time = null,_x000D_
  frame = -1,_x000D_
 function loop(timestamp) {_x000D_
  if (time === null) time = timestamp;_x000D_
  var seg = Math.floor((timestamp - time) / delay);_x000D_
  if (seg > frame) {_x000D_
   frame = seg;_x000D_
    time: timestamp,_x000D_
    frame: frame_x000D_
  tref = requestAnimationFrame(loop)_x000D_
 this.isPlaying = false;_x000D_
 this.frameRate = function(newfps) {_x000D_
  if (!arguments.length) return fps;_x000D_
  fps = newfps;_x000D_
  delay = 1000 / fps;_x000D_
  frame = -1;_x000D_
  time = null;_x000D_
 this.start = function() {_x000D_
  if (!this.isPlaying) {_x000D_
   this.isPlaying = true;_x000D_
   tref = requestAnimationFrame(loop);_x000D_
 this.pause = function() {_x000D_
  if (this.isPlaying) {_x000D_
   this.isPlaying = false;_x000D_
   time = null;_x000D_
   frame = -1;_x000D_
body {font:16px sans-serif}
<label>Framerate: <select id=sFPS>_x000D_
<canvas id=c height=60></canvas><br>_x000D_
<button id=bState>Start/Stop</button>

Old answer

The main purpose of requestAnimationFrame is to sync updates to the monitor's refresh rate. This will require you to animate at the FPS of the monitor or a factor of it (ie. 60, 30, 15 FPS for a typical refresh rate @ 60 Hz).

If you want a more arbitrary FPS then there is no point using rAF as the frame rate will never match the monitor's update frequency anyways (just a frame here and there) which simply cannot give you a smooth animation (as with all frame re-timings) and you can might as well use setTimeout or setInterval instead.

This is also a well known problem in the professional video industry when you want to playback a video at a different FPS then the device showing it refresh at. Many techniques has been used such as frame blending and complex re-timing re-building intermediate frames based on motion vectors, but with canvas these techniques are not available and the result will always be jerky video.

var FPS = 24;  /// "silver screen"
var isPlaying = true;

function loop() {
    if (isPlaying) setTimeout(loop, 1000 / FPS);

    ... code for frame here

The reason why we place setTimeout first (and why some place rAF first when a poly-fill is used) is that this will be more accurate as the setTimeout will queue an event immediately when the loop starts so that no matter how much time the remaining code will use (provided it doesn't exceed the timeout interval) the next call will be at the interval it represents (for pure rAF this is not essential as rAF will try to jump onto the next frame in any case).

Also worth to note that placing it first will also risk calls stacking up as with setInterval. setInterval may be slightly more accurate for this use.

And you can use setInterval instead outside the loop to do the same.

var FPS = 29.97;   /// NTSC
var rememberMe = setInterval(loop, 1000 / FPS);

function loop() {

    ... code for frame here

And to stop the loop:


In order to reduce frame rate when the tab gets blurred you can add a factor like this:

var isFocus = 1;
var FPS = 25;

function loop() {
    setTimeout(loop, 1000 / (isFocus * FPS)); /// note the change here

    ... code for frame here

window.onblur = function() {
    isFocus = 0.5; /// reduce FPS to half   

window.onfocus = function() {
    isFocus = 1; /// full FPS

This way you can reduce the FPS to 1/4 etc.

I suggest wrapping your call to requestAnimationFrame in a setTimeout:

const fps = 25;
function animate() {
  // perform some animation task here

  setTimeout(() => {
  }, 1000 / fps);

You need to call requestAnimationFrame from within setTimeout, rather than the other way around, because requestAnimationFrame schedules your function to run right before the next repaint, and if you delay your update further using setTimeout you will have missed that time window. However, doing the reverse is sound, since you’re simply waiting a period of time before making the request.

The simplest way

const FPS = 30;
let lastTimestamp = 0;

function update(timestamp) {
  if (timestamp - lastTimestamp < 1000 / FPS) return;
   /* <<< PUT YOUR CODE HERE >>>  */

  lastTimestamp = timestamp;


For throttling FPS to any value, pls see jdmayfields answer. However, for a very quick and easy solution to halve your frame rate, you can simply do your computations only every 2nd frame by:

function render() {
  // ... computations ...
function skipFrame() { requestAnimationFrame(render); }

Similarly you could always call render but use a variable to control whether you do computations this time or not, allowing you to also cut FPS to a third or fourth (in my case, for a schematic webgl-animation 20fps is still enough while considerably lowering computational load on the clients)

var time = 0;
var time_framerate = 1000; //in milliseconds

function animate(timestamp) {
  if(timestamp > time + time_framerate) {
    time = timestamp;    

    //your code


A simple solution to this problem is to return from the render loop if the frame is not required to render:

const FPS = 60;
let prevTick = 0;    

function render() 

    // clamp to fixed framerate
    let now = Math.round(FPS * Date.now() / 1000);
    if (now == prevTick) return;
    prevTick = now;

    // otherwise, do your stuff ...

It's important to know that requestAnimationFrame depends on the users monitor refresh rate (vsync). So, relying on requestAnimationFrame for game speed for example will make it unplayable on 200Hz monitors if you're not using a separate timer mechanism in your simulation.

Skipping requestAnimationFrame cause not smooth(desired) animation at custom fps.

// Input/output DOM elements_x000D_
var $results = $("#results");_x000D_
var $fps = $("#fps");_x000D_
var $period = $("#period");_x000D_
// Array of FPS samples for graphing_x000D_
// Animation state/parameters_x000D_
var fpsInterval, lastDrawTime, frameCount_timed, frameCount, lastSampleTime, _x000D_
  currentFps=0, currentFps_timed=0;_x000D_
var intervalID, requestID;_x000D_
// Setup canvas being animated_x000D_
var canvas = document.getElementById("c");_x000D_
var canvas_timed = document.getElementById("c2");_x000D_
canvas_timed.width = canvas.width = 300;_x000D_
canvas_timed.height = canvas.height = 300;_x000D_
var ctx = canvas.getContext("2d");_x000D_
var ctx2 = canvas_timed.getContext("2d");_x000D_
// Setup input event handlers_x000D_
$fps.on('click change keyup', function() {_x000D_
    if (this.value > 0) {_x000D_
        fpsInterval = 1000 / +this.value;_x000D_
$period.on('click change keyup', function() {_x000D_
    if (this.value > 0) {_x000D_
        if (intervalID) {_x000D_
        intervalID = setInterval(sampleFps, +this.value);_x000D_
function startAnimating(fps, sampleFreq) {_x000D_
    ctx.fillStyle = ctx2.fillStyle = "#000";_x000D_
    ctx.fillRect(0, 0, canvas.width, canvas.height);_x000D_
    ctx2.fillRect(0, 0, canvas.width, canvas.height);_x000D_
    ctx2.font = ctx.font = "32px sans";_x000D_
    fpsInterval = 1000 / fps;_x000D_
    lastDrawTime = performance.now();_x000D_
    lastSampleTime = lastDrawTime;_x000D_
    frameCount = 0;_x000D_
    frameCount_timed = 0;_x000D_
    intervalID = setInterval(sampleFps, sampleFreq);_x000D_
function sampleFps() {_x000D_
    // sample FPS_x000D_
    var now = performance.now();_x000D_
    if (frameCount > 0) {_x000D_
        currentFps =_x000D_
            (frameCount / (now - lastSampleTime) * 1000).toFixed(2);_x000D_
        currentFps_timed =_x000D_
            (frameCount_timed / (now - lastSampleTime) * 1000).toFixed(2);_x000D_
        $results.text(currentFps + " | " + currentFps_timed);_x000D_
        frameCount = 0;_x000D_
        frameCount_timed = 0;_x000D_
    lastSampleTime = now;_x000D_
function drawNextFrame(now, canvas, ctx, fpsCount) {_x000D_
    // Just draw an oscillating seconds-hand_x000D_
    var length = Math.min(canvas.width, canvas.height) / 2.1;_x000D_
    var step = 15000;_x000D_
    var theta = (now % step) / step * 2 * Math.PI;_x000D_
    var xCenter = canvas.width / 2;_x000D_
    var yCenter = canvas.height / 2;_x000D_
    var x = xCenter + length * Math.cos(theta);_x000D_
    var y = yCenter + length * Math.sin(theta);_x000D_
    ctx.moveTo(xCenter, yCenter);_x000D_
    ctx.lineTo(x, y);_x000D_
   ctx.fillStyle = ctx.strokeStyle = 'white';_x000D_
    var theta2 = theta + 3.14/6;_x000D_
    ctx.moveTo(xCenter, yCenter);_x000D_
    ctx.lineTo(x, y);_x000D_
    ctx.arc(xCenter, yCenter, length*2, theta, theta2);_x000D_
    ctx.fillStyle = "rgba(0,0,0,.1)"_x000D_
    ctx.fillStyle = "#000";_x000D_
    ctx.fillStyle = "#080";_x000D_
// redraw second canvas each fpsInterval (1000/fps)_x000D_
function animate_timed() {_x000D_
    drawNextFrame( performance.now(), canvas_timed, ctx2, currentFps_timed);_x000D_
    setTimeout(animate_timed, fpsInterval);_x000D_
function animate(now) {_x000D_
    // request another frame_x000D_
    // calc elapsed time since last loop_x000D_
    var elapsed = now - lastDrawTime;_x000D_
    // if enough time has elapsed, draw the next frame_x000D_
    if (elapsed > fpsInterval) {_x000D_
        // Get ready for next frame by setting lastDrawTime=now, but..._x000D_
        // Also, adjust for fpsInterval not being multiple of 16.67_x000D_
        lastDrawTime = now - (elapsed % fpsInterval);_x000D_
      drawNextFrame(now, canvas, ctx, currentFps);_x000D_
startAnimating(+$fps.val(), +$period.val());
  padding:0px 25px;_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<h3>requestAnimationFrame skipping <span id="tvs">vs.</span> setTimeout() redraw</h3>_x000D_
    <input id="fps" type="number" value="33"/> FPS:_x000D_
    <span id="results"></span>_x000D_
    <input id="period" type="number" value="1000"/> Sample period (fps, ms)_x000D_
<canvas id="c"></canvas><canvas id="c2"></canvas>

Original code by @tavnab.

These are all good ideas in theory, until you go deep. The problem is you can't throttle an RAF without de-synchronizing it, defeating it's very purpose for existing. So you let it run at full-speed, and update your data in a separate loop, or even a separate thread!

Yes, I said it. You can do multi-threaded JavaScript in the browser!

There are two methods I know that work extremely well without jank, using far less juice and creating less heat. Accurate human-scale timing and machine efficiency are the net result.

Apologies if this is a little wordy, but here goes...

Method 1: Update data via setInterval, and graphics via RAF.

Use a separate setInterval for updating translation and rotation values, physics, collisions, etc. Keep those values in an object for each animated element. Assign the transform string to a variable in the object each setInterval 'frame'. Keep these objects in an array. Set your interval to your desired fps in ms: ms=(1000/fps). This keeps a steady clock that allows the same fps on any device, regardless of RAF speed. Do not assign the transforms to the elements here!

In a requestAnimationFrame loop, iterate through your array with an old-school for loop-- do not use the newer forms here, they are slow!

for(var i=0; i<sprite.length-1; i++){  rafUpdate(sprite[i]);  }

In your rafUpdate function, get the transform string from your js object in the array, and its elements id. You should already have your 'sprite' elements attached to a variable or easily accessible through other means so you don't lose time 'get'-ing them in the RAF. Keeping them in an object named after their html id's works pretty good. Set that part up before it even goes into your SI or RAF.

Use the RAF to update your transforms only, use only 3D transforms (even for 2d), and set css "will-change: transform;" on elements that will change. This keeps your transforms synced to the native refresh rate as much as possible, kicks in the GPU, and tells the browser where to concentrate most.

So you should have something like this pseudocode...

// refs to elements to be transformed, kept in an array
var element = [
   mario: document.getElementById('mario'),
   luigi: document.getElementById('luigi')

var sprite = [  // read/write this with SI.  read-only from RAF
   mario: { id: mario  ....physics data, id, and updated transform string (from SI) here  },
   luigi: {  id: luigi  .....same  }
   //...and so forth
] // also kept in an array (for efficient iteration)

//update one sprite js object
//data manipulation, CPU tasks for each sprite object
//(physics, collisions, and transform-string updates here.)
//pass the object (by reference).
var SIupdate = function(object){
  // get pos/rot and update with movement
  object.pos.x += object.mov.pos.x;  // example, motion along x axis
  // and so on for y and z movement
  // and xyz rotational motion, scripted scaling etc

  // build transform string ie
  object.transform =
   ') '+

   // assign rotations, order depends on purpose and set-up. 
   'rotationZ('+object.rot.z+') '+
   'rotationY('+object.rot.y+') '+
   'rotationX('+object.rot.x+') '+

   'scale3d('.... if desired
  ;  //...etc.  include 

var fps = 30; //desired controlled frame-rate

// CPU TASKS - SI psuedo-frame data manipulation
  // update each objects data
  for(var i=0; i<sprite.length-1; i++){  SIupdate(sprite[i]);  }
},1000/fps); //  note ms = 1000/fps

// GPU TASKS - RAF callback, real frame graphics updates only
var rAf = function(){
  // update each objects graphics
  for(var i=0; i<sprite.length-1; i++){  rAF.update(sprite[i])  }
  window.requestAnimationFrame(rAF); // loop

// assign new transform to sprite's element, only if it's transform has changed.
rAF.update = function(object){     
  if(object.old_transform !== object.transform){
    element[object.id].style.transform = transform;
    object.old_transform = object.transform;

window.requestAnimationFrame(rAF); // begin RAF

This keeps your updates to the data objects and transform strings synced to desired 'frame' rate in the SI, and the actual transform assignments in the RAF synced to GPU refresh rate. So the actual graphics updates are only in the RAF, but the changes to the data, and building the transform string are in the SI, thus no jankies but 'time' flows at desired frame-rate.


[setup js sprite objects and html element object references]

[setup RAF and SI single-object update functions]

[start SI at percieved/ideal frame-rate]
  [iterate through js objects, update data transform string for each]
  [loop back to SI]

[start RAF loop]
  [iterate through js objects, read object's transform string and assign it to it's html element]
  [loop back to RAF]

Method 2. Put the SI in a web-worker. This one is FAAAST and smooth!

Same as method 1, but put the SI in web-worker. It'll run on a totally separate thread then, leaving the page to deal only with the RAF and UI. Pass the sprite array back and forth as a 'transferable object'. This is buko fast. It does not take time to clone or serialize, but it's not like passing by reference in that the reference from the other side is destroyed, so you will need to have both sides pass to the other side, and only update them when present, sort of like passing a note back and forth with your girlfriend in high-school.

Only one can read and write at a time. This is fine so long as they check if it's not undefined to avoid an error. The RAF is FAST and will kick it back immediately, then go through a bunch of GPU frames just checking if it's been sent back yet. The SI in the web-worker will have the sprite array most of the time, and will update positional, movement and physics data, as well as creating the new transform string, then pass it back to the RAF in the page.

This is the fastest way I know to animate elements via script. The two functions will be running as two separate programs, on two separate threads, taking advantage of multi-core CPU's in a way that a single js script does not. Multi-threaded javascript animation.

And it will do so smoothly without jank, but at the actual specified frame-rate, with very little divergence.


Either of these two methods will ensure your script will run at the same speed on any PC, phone, tablet, etc (within the capabilities of the device and the browser, of course).

How to easily throttle to a specific FPS:

// timestamps are ms passed since document creation.
// lastTimestamp can be initialized to 0, if main loop is executed immediately
var lastTimestamp = 0,
    maxFPS = 30,
    timestep = 1000 / maxFPS; // ms for each frame

function main(timestamp) {

    // skip if timestep ms hasn't passed since last frame
    if (timestamp - lastTimestamp < timestep) return;

    lastTimestamp = timestamp;

    // draw frame here


Source: A Detailed Explanation of JavaScript Game Loops and Timing by Isaac Sukin

I always do it this very simple way without messing with timestamps:

var fps, eachNthFrame, frameCount;

fps = 30;

//This variable specifies how many frames should be skipped.
//If it is 1 then no frames are skipped. If it is 2, one frame 
//is skipped so "eachSecondFrame" is renderd.
eachNthFrame = Math.round((1000 / fps) / 16.66);

//This variable is the number of the current frame. It is set to eachNthFrame so that the 
//first frame will be renderd.
frameCount = eachNthFrame;


//I think the rest is self-explanatory
fucntion frame() {
  if (frameCount == eachNthFrame) {
    frameCount = 0;

Here's a good explanation I found: CreativeJS.com, to wrap a setTimeou) call inside the function passed to requestAnimationFrame. My concern with a "plain" requestionAnimationFrame would be, "what if I only want it to animate three times a second?" Even with requestAnimationFrame (as opposed to setTimeout) is that it still wastes (some) amount of "energy" (meaning that the Browser code is doing something, and possibly slowing the system down) 60 or 120 or however many times a second, as opposed to only two or three times a second (as you might want).

Most of the time I run my browsers with JavaScript intentially off for just this reason. But, I'm using Yosemite 10.10.3, and I think there's some kind of timer problem with it - at least on my old system (relatively old - meaning 2011).

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to performance

Why is 2 * (i * i) faster than 2 * i * i in Java? What is the difference between spark.sql.shuffle.partitions and spark.default.parallelism? How to check if a key exists in Json Object and get its value Why does C++ code for testing the Collatz conjecture run faster than hand-written assembly? Most efficient way to map function over numpy array The most efficient way to remove first N elements in a list? Fastest way to get the first n elements of a List into an Array Why is "1000000000000000 in range(1000000000000001)" so fast in Python 3? pandas loc vs. iloc vs. at vs. iat? Android Recyclerview vs ListView with Viewholder

Examples related to animation

Simple CSS Animation Loop – Fading In & Out "Loading" Text Slidedown and slideup layout with animation How to have css3 animation to loop forever jQuery animated number counter from zero to value CSS Auto hide elements after 5 seconds Fragment transaction animation: slide in and slide out Android Animation Alpha Show and hide a View with a slide up/down animation Controlling fps with requestAnimationFrame? powerpoint loop a series of animation

Examples related to canvas

How to make canvas responsive How to fill the whole canvas with specific color? Use HTML5 to resize an image before upload Convert canvas to PDF Scaling an image to fit on canvas Split string in JavaScript and detect line break Get distance between two points in canvas canvas.toDataURL() SecurityError Converting Chart.js canvas chart to image using .toDataUrl() results in blank image Chart.js canvas resize

Examples related to requestanimationframe

Controlling fps with requestAnimationFrame?