Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to lock FPS with requestAnimationFrame?

I used script from Paul Irish https://gist.github.com/paulirish/1579671 to create animation loop inside html site.

It works although it's faster in fullscreen mode than in browser window. Also, I observed different speeds depending on canvas size and depending on browser I use.

Question: How can I ensure stable frame rate using the script?

Code is available here (Beginning WebGL, chapter 1 by Brian Danchilla): https://github.com/bdanchilla/beginningwebgl/blob/master/01/2D_movement.html

like image 337
mtx Avatar asked Jan 13 '16 12:01

mtx


People also ask

How to control the FPS with requestanimationframe?

There are two ways of controlling the fps with requestAnimationFrame (). These are discussed as follows. This is a quick and easy approach to controlling the fps. The setTimeout () function has the following declaration: Can be used to execute a function after waiting for the specified number of seconds.

How to get the requestid from the requestanimationframe?

The requestID is returned from the requestAnimationFrame () every time it is run, so we can get this value by assigning it to a variable on each animation tick: Where animate is the function that calls requestAnimationFrame () and runs the actual animation.

How to control fps using setTimeout() in JavaScript?

The code to control fps using setTimeout () is given below, the requestAnimationFrame () is passed as a function to setTimeout () for regularly updating the screen at the specified fps. // ... Code for Animating the Objects ... " secs @ " + currentFps + " fps."); The two images show the fps fluctuating around 5 fps, as assumed by the above code.

How to halve the frame rate of an animation?

However, for a very quick and easy solution to halve your frame rate, you can simply do your computations only every 2nd frame by: requestAnimationFrame (render); function render () { // ... computations ... requestAnimationFrame (skipFrame); } function skipFrame () { requestAnimationFrame (render); }


3 Answers

Something like this should work. If the time delta between two frames is shorter than your FPS limit, the update function returns and waits for the next frame. But this will only limit the updates from happening too quickly; like emackey said, there's always the possibility the update loop will run more slowly.

var updateId,
    previousDelta = 0,
    fpsLimit = 30;

function update(currentDelta) {
    updateId = requestAnimationFrame(update);

    var delta = currentDelta - previousDelta;

    if (fpsLimit && delta < 1000 / fpsLimit) {
        return;
    }

    /* your code here */

    previousDelta = currentDelta;
}
like image 102
endemic Avatar answered Oct 21 '22 05:10

endemic


To embellish what @emackey said,

The short answer is you can't. You could ask the computer to do an infinite amount of work each frame. I can't promise to do that work in a finite amount of time.

On top of that each computer has a different amount of power. A cheap integrated GPU has much less power than a high end graphics card. An intel i3 is much slower than an i7.

You also mentioned changing the canvas size. Drawing a 300x150 canvas is only 45000 pixels worth of work. Drawing a 1920x1080 canvas would be 2,073,600 pixels of work or 46x more work

The best you can do is do the least amount of work possible, and or remove features on slow hardware either automatically or by user choice. Most games do this. They graphics setting options where the user can choose resolution, texture res, anti-alising levels and all kinds of other things.

That said, you can try to do your computations so things in your app move at a consistent speed relative to time. The framerate might slower on a slow machine or with a larger canvas but the distance something moves per second will remain the same.

You can do this by using the time value passed into requestAnimationFrame

function render(time) {
   // time is time in milliseconds since the page was loaded

   ...do work...

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

For example here is NON framerate independent animation

function render(time) {

   xPosition = xPosition + velocity;

   ...

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

and here is frame rate independent animation

var then = 0;
function render(time) {
   var timeInSeconds = time * 0.001;
   var deltaTimeInSeconds = timeInSeconds - then;
   then = timeInSeconds;

   xPosition = xPosition + velocityInUnitsPerSecond * deltaTimeInSeconds;

   ...

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

Note: The time passed into requestAnimationFrame is higher resolution than Date.now()

Here's an article on it with animations

like image 35
gman Avatar answered Oct 21 '22 05:10

gman


You can't enforce a stable frame rate directly. Your page is not the only app running on the user's platform, and platform capabilities vary widely. requestAnimationFrame runs as fast as it can, not exceeding the display update interval on the target device, but potentially much slower depending on available CPU, GPU, memory, and other limitations.

The standard practice here is to measure the amount of time that has elapsed since the previous animation frame, typically with Date.now(), and each frame advance the animation by that amount of time. To the human eye, this makes the resulting animation run at a consistent speed, even if the frame rate is highly variable.

For example, sites such as Shadertoy and GLSL Sandbox run full-screen GLSL shaders and pass in a uniform called time (or iGlobalTime), which is a float representing the number of seconds elapsed since the shader started. This time value increases at irregular intervals depending on how long each animation frame took to render, but the result is that the float appears to count upwards at a stable 1.0 per second. In this way, shader playback based on this time value can appear consistent.

like image 1
emackey Avatar answered Oct 21 '22 05:10

emackey