Increase FPS in animation in javascript - javascript

I'm doing an animation using javascript:
function animate(){
window.setTimeout(function(){
//Do a new frame and recall this function till the animation is finished
}, 1000/FPS);//FPS Default 15 approximately 60FPS
}
animate();
I'm wondering how I can increase the FPS without losing the quality? Using the already built-in css3 is not an option because I have custom-built an animation.

A couple of suggestions:
Do not rely on setTimeout, setInterval, or any other built-in utility for scheduling a function invocation to provide a high degree of accuracy or precision, particularly when you are executing custom rendering code.
Decouple the ideas of frame and time in your mind. Yes an animation is internally a series of frames, but the end result is something that's supposed to appear to move fluidly through time. So instead of drawing frame 'n', scheduling a timeout for 1000/FPS and then assuming that "it's time to draw frame 'n + 1' now", try drawing frame 'n', scheduling a timeout for some very short interval (like 10 ms), and then (when the timeout fires) measuring the actual amount of time elapsed between when the animation started and the current point in time. Then use your elapsed time to decide what frame should be showing at this exact moment in time (which may still be 'n', or 'n + 1', or maybe even 'n + 3' if your rendering code takes very long to execute), and render that frame. Trust me, you'll get smoother, more consistent results that way.
In terms of how to improve the framerate and/or rendering quality once you have a reasonably set-up rendering loop, that is all about optimizing your //Do a new frame code as much as you possibly can.

It's worth knowing a couple of things: FPS above 60 is usually meaningless, as browsers struggle to repaint the screen any quicker than that. And setTimeout is inconsistent. Just because you tell a script to run every 50ms (for example), doesn't mean it will. There's typically a variance of up to 15ms in either direction.
Decent strategies for improving quality would be making use of some sort of easing calculation, syncing the FPS with the browser repaint cycle - some information about this here - and making use of motion blur, etc, to smooth the effect.
Check out http://weblogs.mozillazine.org/roc/archives/2010/11/measuring_fps.html for some Firefox-specific insights.

Related

Requestanimationframe implementation: recursive / recursive fps limited / one-off

I've studied requestanimationframe documentation and looked for many posts about the usage of it, but i still haven't get a clear answer on one of my thought.
I understand that requestanimationframe is scheduling a task to be executed right at the beginning of the next frame, so the code that does dom manipulations will have a better chance to be finished and painted before the pain cyle. (unless setInterval or setTimeout which usually executes a lot later, causing the well known 'running out of time before the frame gets painted' => dropping frames).
1. The recursive way
The simplest example to use requestanimation frame is the following:
function animate() {
requestAnimationFrame(animate);
// drawing code comes here
}
requestAnimationFrame(animate);
This will give you a smooth animation if you have something that needs to be updated frequently, and also giving you the benefit of not dropping any frames during your animations. This will usually gives you 60fps animations, but if your browser and screen supports 144hz/fps, then you can easily end up having 144fps animations (6.95 ms cycle).
2. Fps limited animations
Other examples also introduce ways to limit the fps to a certain number. The following code snippnet shows how to limit your fps to 30 fps:
const fpsInterval = 1000 / 30;
let previousTime = 0;
function animate(time) {
requestAnimationFrame(animate);
const deltaTime = time - previousTime;
if (deltaTime > fpsInterval) {
// Get ready for next frame by setting then=now, but also adjust for your
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
previousTime = time - (deltaTime % fpsInterval);
}
// drawing code comes here
}
requestAnimationFrame(animate);
3. One-off animations
I've been wondering a third case, when you just want your animation to be scheduled precisely, even if you have 1 or just a few amount of updates in each second.
A best example is when you have a websocket connection and each update will introduce a dom manipulation, but the update rate is far too low to do it in a recursive way.
// setting up websocket connection
ws.onmessage = (data) => {
// changing application state
myApplicationState = JSON.parse(data);
requestAnimationFrame(animate);
}
function animate () {
// drawing code comes here
}
Now here is my question for you all:
Does this make sense to call requestanimationframe right from the callback of a websocket onmessage function, or should i be using the recursive way?
So far I haven't tested it (in progress), but i have a feeling it does still going to give you the benefit of well-timed animations that can be executed without dropping a frame.
My real-life example is similar, i only have 5 messages in a second and i'd like to call requestanimationframe ONLY 5 times in a second.
My thought of doing this vs the recursive way:
Using requestanimation frame in a recursive way will incredibly increase the script execution time when measured in chrome profiling tools.
Only calling requestanimationframe when a websocket comes will still make sure to have the benefit of the feature, yet not polluting the callstack and reducing execution time
My initial measures were the following. I've spin up chrome profiling and run it for 10 seconds and measured the script execution times (we're not measuring render or paint since they are basically identical):
Script execution times:
recursive way: 4500ms
fps limited way: 4300ms
one-off animated way: 1700ms
While recursive requestanimationframe solution is giving you a super smooth and good user experience, it's also very costy for your CPU and execution times.
If you have multiple components doing animations with recursive requestanimationframe, you're going to hit a CPU bottleneck pretty soon.
Oddly this last case causing some fps drops, which I do not understand. My understanding is that you can call requestanimationframe whenever you want and it'll only execute the begginning of the next frame. But it seems there is something i dont know about.
Here is a picture of what is happening. I still don't understand it. requestanimationframe function was called before the end of the frame, but somehow because it was part of a bigger function call, it's marked as 'dropped' in chrome. Wonder if that's just a bug in the chrome profiling or was it for real dropped.
I wonder what you guys thinking about this topic. I'll update this post with some chrome performance metrics soon.
There seems to some misconception about requestAnimationFrame (rAF) magically preventing dropped frames by ensuring that whatever is executed in it will somehow run fast enough. It doesn't.
requestAnimationFrame is just a timer for "right before the next paint"*.
Its main goal is to limit the number of callbacks to just what is needed, avoiding to waste drawing operations which won't even be rendered on screen.
It does actually allow to drop frames smartly, so if one execution took the time of 3 frames to render, it won't stupidly try to execute the 3 missing frames as soon as possible, instead it will nicely discard them and get your script ready to get back from this hiccup.
So using it for updating something that doesn't match the screen refresh rate is not very useful.
One should remember that calling requestAnimationFrame is not free, this will mark the document as animated and force the event-loop to enter the update-the-rendering steps, which in itself has some performance costs. So if what you are doing in there is not going to update the rendering of the page, it can actually be detrimental to wrap your callback in a rAF callback.
Still, there are cases where it could make sense, for instance in complex pages it may be good to have a method that batches all the changes to the DOM in a rAF callback, and have all the scripts that need to access the CSSOM boxes before these changes take effect, thus avoiding useless and costly reflows.
An other case is to avoid executing scripts when the page is in the background: rAF is heavily throttled when in background and if you have some script running that doesn't need to run when the page is hidden (e.g a clock or similar), it may make sense to wrap a timer in a rAF callback to take advantage of this heavy throttling.
*Actually both Chrome and Firefox have broken this conception. In these browsers if you call requestAnimationFrame(cb) from a non-animated document (a document where no animation frame callback is scheduled, and no mouse-event occurred in the last frame), they will force the update the rendering steps to fire immediately, making this frame not synced with the monitor (and, in Chrome, sometimes not even rendered on the screen at all).

Slow down world time and rendering in Javascript for Webkit / Chrome

I would like to record browser animations in the smoothest possible framerate, including an alpha channel. Due to single threaded performance limitations I am looking for a way to slow down the producer of these animations. I currently achieve this by increasing the delay and duration of timers (in my case anime.js), this allows me to sample screen at a lower frame rate, while the target frame rate can be achieved.
The change of duration and delays is not overly painful, but I would like to do this in a more generic way. For example instruct the browser to have a world clock that runs on 10% of real speed. I have already found the Proxy suggestion below, but this does not seem to impact the current animation rendering.
Slow down time with JavaScript
What can be done to globally reduce all the animation, as if everything would take 10 times longer?

What can cause requestAnimationFrame to drop frames in an efficient webgl loop?

I have been programming a javascript demo/test to learn WebGL. I have a fairly efficient game loop structure that (according to Chrome Dev Tools) only takes 1-2 milliseconds to run. I am using requestAnimationFrame to schedule the running of the loop (because this is apparently the "proper" way to perform 60fps animation). When I look at the Timeline of constructing the frame, the actual javascript code is minimal, but the 'idle' part of the frame can push the frame well over the 30 fps line. The FPS counter shows 20-40fps with lots of drops (almost like a saw tooth).
Is there anything else I can account for if my rendering loop is already 1-2ms when it has to fit into 16 ms to run 60fps?
If I convert the loop into a setTimeout loop it can hold 60fps easily. I can even render it in Retina Resolution without impacting the 60fps.
e.g.
// Timeout version
function gameLoop()
{
setTimeout(gameLoop, 1000/60);
//Process movement, AI, game logic
renderLoop();
}
function renderLoop()
{
//Drawing all of the 3d stuff
}
v.s.
function gameLoop()
{
requestAnimationFrame(gameLoop);
//Process movement, AI, game logic
renderLoop()
}
Function renderLoop()
{
//draw objects
}
I also at some point had the gameLoop running "separately" on a setTimeout while the renderLoop was being called by a requestAnimationFrame. Since they are all on the same thread, this seems a bit dodgey since they could step on each others toes.
requestAnimationFrame implementation varies in different browsers, and it is up to a browser to maintain it's underlying behaviour.
There is no guarantee that it will render 60fps, the only it guarantees that you will get your function executed as close as possible to the moment of rendering (just before swapping buffers to send image data to the screen).
If you use setTimeout you might get more frequent function calls, but that does not gives you 60fps necessary, as screen might refresh still in 30fps, or any other. In that case you are trying to render too often - and that is gpu and energy inefficient (especially on mobile).
Most people couple their update and render logic in single frequency (same function). In any case you need to update your logic (speeds etc.) with delta-time modifier.
That way even with 30fps, speeds of things moving will be same, regardless of fps.

RequestAnimationFrame behaviour..hows it work?

I have been playing around with requestAnimationframe for chrome, and wondered how it actually behaves.
When i load my canvas and draw, I get a steady 60FPS. If i scroll around using offset like a click and drag around a map, the FPS will drop (as expected)...once i stop dragging around the map, the FPS creeps back up to its steady 60fps, again as expected.
Here how ever is where I'm wondered if this is delibrate for requestAnimationframe. If i drag the map around until the FPS drop, drops below 30 for an extended period of time, once i stop dragging, it climbs back up, but this time it hits 30FPS and will not go higher. It appears as if the browser decided 30FPS is perhaps the best option.
Is this delibrately done by the browser, i been trying to find out if this is the case. Because it will go to 60fps if i dont drop below 30fps for too long.
Yes, it's something that the browsers are capable of doing.
"How it's supposed to work" isn't really something that anybody can answer, here.
The reason for that is simply that under the hood is 100% browser-specific.
But it's very safe to say that yes, the browser is capable of deciding when you should be locked into a 30Hz refresh, rather than a 60Hz refresh.
An illustration of why this is the case:
requestAnimationFrame() is also tied into the Page Visibility API if the vendors want (very true for Chrome).
Basically, if the page isn't visible, they can slow the requestAnimationFrame() updates down to a few times per second or pause them altogether.
Given that knowledge, it's entirely plausible to believe that one of two things is happening:
they're intentionally capping you at 30fps, because they feel your experience will be more stable there, based on averaged performance data
they're intentionally throttling you, but there's some bug in the system (or some less than lenient math) which is preventing you from going back up to 60, after the coast has cleared, .and if they are using averaged performance data, then that might be part of the issue.
Either way, it is at very least mostly-intentional, with the only unanswered question being why it sticks to 30fps.
Did you leave it alone for 20 or 30 minutes after the fact, to see if it went back up at any time, afterwards?
You can run Timeframe analysis from Chrome DevTools to look for maverick JS that is slowing down your animation times.
https://developers.google.com/chrome-developer-tools/docs/timeline
RAF will find the best place to paint your changes not the closest one. So, if the JS in the RAF callback is taking two frames worth of time(around 16ms per frame on your 60hz hardware), then you FPS will drop to 30.
From Paul Irish via Boris
Actually, “It’s currently capped at 1000/(16 + N) fps, where N is the number of ms it takes your callback to execute. If your callback takes 1000ms to execute, then it’s capped at under 1fps. If your callback takes 1ms to execute, you get about 60fps.” (thx, Boris)
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

How to determine the best "framerate" (setInterval delay) to use in a JavaScript animation loop?

When writing a JavaScript animation, you of course make a loop using setInterval (or repeated setTimeout). But what is the best delay to use in the setInterval/setTimeout call(s)?
In the jQuery API page for the .animate() function, the user "fbogner" says:
Just if anyone is interested: Animations are "rendered" using a setInterval with a time out of 13ms. This is quite fast! Chrome's fastest possible interval is about 10ms. All other browsers "sample" at about 20-30ms.
Any idea how jQuery determined to use this specific number?
Started bounty. I'm hoping someone with knowledge of the source code behind Chromium or Firefox can provide some hard facts that might back up the decision of a specific framerate. Or perhaps a list of animations (or frameworks) and their delays. I believe this makes for an interesting opportunity to do a bit of research.
Interesting - I just took the time to look at Google's Pac-Man source to see what they did. They set up an array of possible FPSes (90, 45, 30), start at the first one, and then each frame they check the "slowness" of the frame (amount the frame exceeded its allotted time). If the slowness exceeds 50ms 20 times, the framerate is notched down to the next in the list (90 -> 45, 45 -> 30). It appears that the framerate is never raised back up, presumably because the game is so short-lived that it wouldn't be worth the trouble to code that.
Oh, and the setInterval delay is of course set to 1000 / framerate. They do, in fact, use setInterval and not repeated setTimeouts.
I think this dynamic framerate feature is pretty neat!
I would venture to say that a substantial fraction of web users are using monitors that refresh at 60Hz, which translates to one frame every 16.66ms. So to make the monitor the bottleneck, you need to produce frames faster than 16.66ms.
There are two reasons you would pick a value like 13ms. First, the browser needs a little bit of time to repaint the screen (in my experience, never less than 1ms). Which puts you at, say, updating every 15ms, which happens to be a very interesting number - the standard timer resolution on Windows is 15ms (see John Resig's blog post). I suspect that an well-written 15ms animation looks very close to the same on a wide variety of browsers/operating systems.
FWIW, fbogner is plain wrong about non-Chrome browsers firing setInterval every 20-30ms. I wrote a test to measure the speed of setInterval firing, and got these numbers:
Chrome - 4ms
Firefox 3.5 - 15ms
IE6 - 15ms
IE8 - 15ms
The pseudo-code for this is this one:
FPS_WANTED = 25
(just a number, it can be changed while executing, or it can be constant)
TIME_OF_DRAWING = 1000/FPS_WANTED
(this is in milliseconds, I believe it is accurate enough)
( should be updated when FPS_WANTED changes)
UntilTheUserLeavesTheDrawingApplication()
{
time1 = getTime();
doAnimation();
time2 = getTime();
animationTime = time2-time1;
if (animationTime > TIME_OF_DRAWING)
{
[the FPS_WANTED cannot be reached]
You can:
1. Decrease the number of FPS to see if a lower framerate can be achieved
2. Do nothing because you want to get all you can from the CPU
}
else
{
[the FPS can be reached - you can decide to]
1. wait(TIME_OF_DRAWING-animationTime) - to keep a constant framerate of FPS_WANTED
2. increase framerate if you want
3. Do nothing because you want to get all you can from the CPU
}
}
Of course there can be variations of this but this is the basic algorithm that is valid in any case of animation.
When doing loops for animations, it's best that you find a balance between the speed of the loop, and how much work needs to be done.
For example, if you want to slide a div across the page within a second so it is a nice effect and timely. You would skip coordinates and have a reasonably fast loop time so the effect is noticeable, but not jumpy.
So it's a trial and error thing (by having to put work, time, and browser capability into account). So it doesn't only look nice on one browser.
The number told by fbogner have been tested.
The browsers throttle the js-activity to a certain degree to be usable every time.
If your javascript would be possible to run every 5msec the browser runtime would have much less cpu time to refresh the rendering or react on user input (clicks) because javascript-execution blocks the browser.
I think the chrome-devs allow you to run your javascript at much shorter intervals than the other browsers because their V8-Javascript-Engine compiles the JavaScript and therefore it runs faster and the browser will noch be blocked as long as with interpreted js-code.
But the engine is not only so much faster to allow shorter intervals the devs have certainly tested which is the best possible shortest interval to allow short intervals and don't blocking the browser for to long
Don't know the reasoning behind jQuery's interval time, as 13ms translates to 80fps which is very fast. The "standard" fps that's used in movies and such is 25fps and is fast enough that human eye won't notice any jittering. 25fps translates to 40ms, so to answer your question: anything below 40ms is enough for an animation.

Categories

Resources