From my understanding, requestAnimationFrame should run as close as possible to the browser's frame rate, which is approximately 60fps. To ensure that this is indeed taking place, I have been logging timestamps for each requestAnimationFrame invocation like so:
function animate(now){
console.log(now);
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
Console.log data shows that invocations are consistently taking place approximately 0.016674 milliseconds apart, thus indicating that the frame rate is ≈ 60fps (59.9736116108912fps to be exact).
Console.logs (sample data):
Timestamp FPS (Current - previous) timestamp
------------------------------------------------------------
100.226 59.97361161 0.016674
116.9 59.97361161 0.016674
133.574 59.97361161 0.016674
. . .
. . .
150.248 59.97361161 0.016674
166.922 59.97361161 0.016674
183.596 59.97361161 0.016674
200.27 59.97361161 0.016674
Up to this point, requestAnimationFrame invocations are occurring at consistent time intervals, and no invocation lags behind/executes too fast.
However, modifying the contents of the animate() function to execute a relatively more complex function, results in requestAnimationFrame invocations which are not as consistent.
Console.logs (sample data):
Timestamp FPS (Current - previous) timestamp
------------------------------------------------------------
7042.73 59.97361161 0.016674
7066.278 42.4664515 0.023548
7082.952 59.97361161 0.016674
7099.626 59.97361161 0.016674
. . .
. . .
17104.026 59.97361161 0.016674
17112.84 113.4558657 0.008814
17129.514 59.97361161 0.016674
17146.188 59.97361161 0.016674
As can be seen in the above data sample, timestamp differences and frame rates are no longer steady, and sometimes occur too soon/too late, resulting in inconsistent frame rates. Had requestAnimationFrame been consistently invoked late, I would have assumed that due to JavaScript's single-threaded nature, complex code residing in the animate() function is taking too long to execute, and thus results in a delayed requestAnimationFrame invocation. However, since requestAnimationFrame is occasionally being invoked too early, this does not seem to be the case.
My code (skeleton):
for (var counter = 0; counter < elements.length; counter ++) //10 elements
{
//some other code
animate(element);
}
function animate(element)
{
// function invocation => performs some complex calculations and animates the element passed in as a parameter
window.requestAnimationFrame(function() { animate(element) } );
}
As can be seen in the above code snippet, requestAnimationFrame is being invoked multiple times for each element, within the initial for loop. RequestAnimationFrame invocations are also intended to go on infinitely, for each of the elements. Since the animation to be performed is highly time-critical (animation timing is very important in this scenario and should be as accurate as possible), it is essential that requestAnimationFrame invocations occur at consistent time intervals throughout. These time intervals should ideally be as close as possible to 0.016674 (≈ 60fps).
Some detail regarding animation to be performed (on each canvas element):
I have a very specific situation, for which I am required to draw a blinking/flashing animation as accurately as possible with respect to time, i.e. canvas colour will have to change at a consistent rate, for the specified time interval. Therefore, for instance, canvas colour needs to stay red for exactly 0.025 seconds, followed by another 0.025 seconds where canvas colour is set to blue, which are then followed by another 0.025s where canvas is red and so on...(animation should go on infinitely, for each of the elements). My current approach involves keeping track of the number of frames which have elapsed within the animation loop itself (thus, each requestAnimationFrame invocation corresponds to a single frame).
Since on a 60Hz monitor an exact frame length of 0.025 seconds cannot be achieved, each red/blue canvas cycle should be "approximated". So, taking into consideration a 60Hz monitor, creating a complete cycle, where the canvas is initially red, followed by blue, a total of 3 frames would be required (1 sec/60 = 0.01666667 seconds * 3 frames = 0.05 seconds => the desired duration for a single, complete red/blue cycle). Dividing 0.05 seconds by 2 would give the desired frame length (which is 0.025 seconds), however since this cannot be achieved on a 60Hz monitor, the cycle is approximated by presenting 2 red canvas frames, followed by a single blue frame (thus forming the entire 3-frame cycle). Unfortunately, even when taking the monitor's refresh rate into consideration, the timing tends to fluctuate, resulting in undesirable inaccuracies.
Final questions:
Would you be able to clarify what is causing this inconsistent requestAnimationFrame behaviour?
Can any optimisations be applied to ensure that requestAnimationFrame invocations are executed at consistent time intervals?
Can better timing accuracy be achieved if I use some other kind of functionality (say, web workers in combination with the setInterval() function)?
setTimeout
and visual updates withinrequestAnimationFrame
– Goaliex
milliseconds after the callback finishes executing, so tasks that vary in complication can delay or speed up when the callback gets called again. – Goalie113FPS
frame? Can't repro this. Otherwise 1 if anything blocks the browser, it won't be able to maintain a consistent frame rate. 2, depends on what you are doing / what is causing the bottleneck. Some operations could be offloaded to a WebWorker, but not all. 3 depends what you are trying to do exactly. Now, you should not assume any frame rate, browsers may (and actually should) adapt it to the monitor's frame rate. So two users may have completely different results using two different monitors (even on the same browser+computer). – Hanukkah