How to lock FPS with requestAnimationFrame?
Asked Answered
W

3

8

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

Wallaroo answered 13/1, 2016 at 12:15 Comment(1)
found interesting post on this topic: codetheory.in/…Wallaroo
H
5

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;
}
Holland answered 13/1, 2016 at 15:19 Comment(1)
If you put (1000 / fpsLimit) - tolerance and set tolerance to a small value such as 0.1 or 0.01, the framerate will be more stable.Stamps
G
4

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

Goodoh answered 15/1, 2016 at 3:54 Comment(0)
V
1

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.

Vannesavanness answered 13/1, 2016 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.