requestAnimationFrame at a limited framerate - javascript

As I understand it, usage of the JS requestAnimationFrame API is intended for cases where the framerate isn't in need of being controlled, but I have a use case where it's essential that a <canvas> only updates at a certain fps interval that may be anywhere between 1 and 25 (between 1 and 25 frames per second, that is). Can I then somehow still effectively use rAF to get at the optimizations it offers?
This question has similarities to mine, but the accepted answer there made close to zero sense to me in the context of that question.
I have two possible solutions for this. The first one involves using a while loop to halt the execution of the script for a specified delay before calling requestAnimationFrame from within the callback. In the example where I saw this, it effectively limited the fps of the animation, but it also seemed to slow down the entire tab. Is this still actually a good solution? The second alternative, as mentioned in the question I linked to above, calls requestAnimationFrame within a setInterval. To me that seems a bit convoluted, but it could be that's the best option?
Or is there a better alternative to accomplish this?

Yoshi's answer is probably the best code solution to this problem. But still I'll mark this answer as correct, because after some research I basically found that my question was invalid. requestAnimationFrame is really meant to keep frame rates as high as possible, and it optimizes for scenarios where animation is meant to be kept consistent and smooth.
Worth noting though is that you don't need requestAnimationFrame to get optimization (even though rAF was touted as a great performance booster) since browsers still optimize regular drawing of a <canvas>. For example, when a tab isn't focused, Chrome for one stops drawing its canvases.
So my conclusion was that this question was invalid. Hope this helps anyone who was wondering something similar to me.

This is just a proof of concept.
All we do is set our frames per second and intervals between each frame. In the drawing function we deduct our last frame’s execution time from the current time to check whether the time elapsed since the last frame is more than our interval (which is based on the fps) or not. If the condition evaluates to true, we set the time for our current frame which is going to be the “last frame execution time” in the next drawing call.
var Timer = function(callback, fps){
var now = 0;
var delta = 0;
var then = Date.now();
var frames = 0;
var oldtime = 0;
fps = 1000 / (this.fps || fps || 60);
return requestAnimationFrame(function loop(time){
requestAnimationFrame(loop);
now = Date.now();
delta = now - then;
if (delta > fps) {
// Update time stuffs
then = now - (delta % fps);
// Calculate the frames per second.
frames = 1000 / (time - oldtime)
oldtime = time;
// Call the callback-function and pass
// our current frame into it.
callback(frames);
}
});
};
Usage:
var set;
document.onclick = function(){
set = true;
};
Timer(function(fps){
if(set) this.fps = 30;
console.log(fps);
}, 5);
http://jsfiddle.net/ARTsinn/rPAeN/

What you can do, though I don't really know if this is really any better is:
render to an invisible context with requestAnimationFrame
update a visible context with setInterval using a fixed fps
Example:
<canvas id="canvas"></canvas>​
<script type="text/javascript">
(function () {
var
ctxVisible = document.getElementById('canvas').getContext('2d'),
ctxHidden = document.createElement('canvas').getContext('2d');
// quick anim sample
(function () {
var x = 0, y = 75;
(function animLoop() {
// too lazy to use a polyfill here
webkitRequestAnimationFrame(animLoop);
ctxHidden.clearRect(0, 0, 300, 150);
ctxHidden.fillStyle = 'black';
ctxHidden.fillRect(x - 1, y - 1, 3, 3);
x += 1;
if (x > 300) {
x = 0;
}
}());
}());
// copy the hidden ctx to the visible ctx on a fixed interval (25 fps)
setInterval(function () {
ctxVisible.putImageData(ctxHidden.getImageData(0, 0, ctxHidden.canvas.width, ctxHidden.canvas.height), 0, 0);
}, 1000/40);
}());
</script>
Demo: http://jsfiddle.net/54vWN/

Related

Javascript - requestAnimationFrame Frame Rate

I have a sprite sheet animation where I am using requestAnimationFrame method to animate a spritesheet with only 4 images on the sheet This is my code:
http://hyque.com/ryan/ani-ryan-2.html
The problem is that it is too fast at 60 fps, so I want to slow the fps. I have been reading several articles on different ways to do it using either setInterval or Date(). I can’t seem to get the code to work correctly. Can anyone help, please.
Here is one of the articles that I tried to merge into my code:
http://codetheory.in/controlling-the-frame-rate-with-requestanimationframe/
So what I like to use to control animation apart form the "game loop."
var lastRender = 0;
var counter = 0;
function render(time)
{
//checks to see if enough time has passed
if(time - lastRender<16){requestAnimationFrame(render);return;}
lastRender = time;
counter++;
if(counter %20 && counter != 0)
animation();
if(counter >= 60)
counter=0;
requestAnimationFrame(render);
}
requestAnimationFrame(render);
This gives you greater control over your sprites so you can now have them at different speeds and your logic stays at 60fps.
Generally, for Game Engines you will want your rendering speed to be different from your logic speed.
Logic speed should simply be as fast as possible
setInterval( logic, 0 ); // runs as fast as possible
OR
setTimer( logic, 0 ); // logic() runs this again ( i think this is generally better )
Whereas render should remain as you have it, as fast as rendering is done
requestAnimationFrame( render ) // per render frame
However, requestAnimationFrame is unstable between machines, and for the most part will run 60 frames per second ( depending on the users hardware ).
In this case, for consistency you should base your animation on consistent TIME or setTimeout.
In your animation you could use something like
var date = new Date(), // date object
milliseconds = date.getMilliseconds(), // 0 - 999 of the second
totalSpriteFrames = 4
frame = Math.floor( milliseconds / ( 1000 / totalSpriteFrames ) ); // break the second into four frames
Create the date object outside of your animation scope for optimization, and this math can easily be used to choose which " frame " your sprite is on. You can also use this for multiple sprites, just change their " totalSpriteFrames "
This is also scalable in case you end up with a frame heavy sprite with many frames.

Three.js / ShaderParticleEngine — SPE.Group.tick bad delta argument?

I'm using the ShaderParticleEngine library for Three.js to create particle emitters.
I picked several code snippets on the Internet to have a working emitter.
Firstly I believed that is wasn't working.
But in fact, the emitter was displayed on the map, but a single motionless particle was on the screen.
After some debugging I undestood that the particle was moving but infinitely slowly. I need to use tick(delta * 1000) to see the emitter in action. And the result is quite ugly (full of gaps, alone particles).I have no problem of low FPS.
The only solution I found is to remove delta argument in the tick function call: particleGroup.tick().
The result is better but is still deceiving, judge by yourself:
Online Emitter Editor:
My result:
I can't understand. I use the same code proposed in the library examples and I use the export feature in the emitter editor.
If I try other variations (eg. on particle life/velocity) I get a very different result in my game, maybe the particle life is not computed correctly because delta argument isn't given?
My game loop:
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
Delta value loop by loop:
30.0000010000003385357559
9.999985195463523e-7
30.0000020000006770715117
0.0000010000003385357559
30.0000020000006770715117
0.0000010000003385357559
0.0000020000006770715117
30.0000010000003385357559
0.000002999999196617864
0.0000010000003385357559
9.999985195463523e-7
0.000002999999196617864
0.0000010000003385357559
0.000001999998858082108
0.0000010000003385357559
20.0000020000006770715117
9.999985195463523e-7
0.0000010000003385357559
To solve smoothness, try the following:
function makeSmoothSPETick(simulator, timeDelta, maxSubstepSize){
var numSubsteps = Math.floor(timeDelta/maxSubstepSize);
var leftOverTime = timeDelta%maxSubstepSize;
while(numSubsteps-->0){
simulator.tick(maxSubstepSize);
}
if(leftOverTime> 0){
//handle the rest
simulator.tick(leftOverTime);
}
}
If you use this function in your code - it will allow you to essentially subdivide steps that are too large into smaller ones of fixed size. As SquareFeet pointed out, say 16ms for 60FPS - you could use something like this:
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
makeSmoothSPETick(particleGroup, delta, 0.016);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
You should get results visually similar to what you'd expect if you were running at smooth 60fps. Beware though, if target hardware can't handle these substeps - you may need to get more logic into your solver algorithm. I'd suggest keeping statistics for past 100 frames or so, and using that to decide how much you can split your incoming step value.
EDIT:
To make sure your timing isn't getting mangled, please try the following:
var lastFrameTime = Date.now()/1000;
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = Date.now()/1000; //getting current time in seconds since epoch
delta = time-lastFrameTime;
lastFrameTime = time;
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
I hope posting this as an answer is okay...
I've bumped the particle engine up a minor version to 0.7.7, having implemented a fix for your issue of "not-very-smooth-looking" emitters.
What was happening before was this:
SPE.Emitter.tick() called with a dt value
This tick function determines how many particles should be marked alive based on the dt argument passed to it. For larger dt values, more particles are marked as alive, for smaller values fewer are marked as alice.
The emitter then resets these particles and waits for the next call.
Assuming more than one particle is going to be marked as alive per frame, and they all originate at the same position in space, then all the particles will be at the same place when they're activated. This is why you saw some "clumping" happening.
What happens now is this:
SPE.Emitter.tick() called with a dt value, just as before.
The tick function now determines how many particles should be marked as alive, and whilst marking them so, sets each particles age to be a fraction of the dt value passed in.
So (!), assuming 100 particles are emitted per frame, and a dt value of 0.016 is passed to the emitter's tick function, each of those 100 particles that will be marked as alive is assigned an age value of (0.016 / 100) * i where i is the particle index (in this case, a value of 0 to 100).
I hope that makes sense. You can see the changes here: https://github.com/squarefeet/ShaderParticleEngine/blob/master/src/ShaderParticleEmitter.js#L240-L246
Master branch has been updated.

Javascript time-based animation and requestAnimationFrame

I have been playing around with canvas and animation, with HTML5 games in mind specifically and quickly learnt the limitations of just using requestAnimationFrame (rFA) and have moved to time-based animations.
I want to maintain constant gameplay regardless of monitor refresh rate or FPS but am unsure how best to handle the animations. I have read through all sorts of implementations but have not found any best practice so to speak. Should I be using a combination of the two?
So far I have considered several options:
rFa only (changes results when fps changes):
var animate = function() {
draw();
requestAnimationFrame(animate);
}
time-based only (not always consistent):
var animate = function() {
now = Date.now();
delta = now - last;
last = now;
draw(delta);
window.setTimeout(animate, 1000/60)
}
set FPS on rFA with setInterval (not always consistent):
setInterval(function () {
draw();
requestAnimationFrame();
}, 1000/fps);
rFA trying to force fps (does not seem very robust, variable delta would work better):
var delta = 1000 / fps;
var animate = function() {
now = Date.now();
if (now - last >= delta) {
last = now;
}
draw(delta);
requestAnimationFrame(animate);
}
time-based rFA (some strange results):
var animate = function () {
now = Date.now();
delta = now - last;
last = now;
draw(delta);
requestAnimationFrame(animate);
}
Ignore the lack of browser support and the use of Date.now(), I just want to demonstrate my flow of thinking. I think that the last option is preferable, but the last two can run into problems with updating too far and missing collisions etc as well as updates taking too long that the animation looses all control.
Also when a user tabs out using rFA only the animation will pause, using a time based function to call rFA means that the game/animation will continue to run in the background which is not ideal.
What would be the best way to handle animations trying to keep consistent results regardless of fps, all of the above might be bad and my apologies for the long post (it is just what I have tried so far and am still pretty lost)? even better with with the above issues in mind?
If you have requestAnimationFrame available, I wouldn't go against it and only call draw() from its callbacks. Of course, you should always use delta timing.
Here's a sophisticated variation of raF with a fallback to setTimeout for the game logic updates in case the frame rate is too low:
var maximalUpdateDelay = 25; // ms
var updateTimeout, now;
function animate() {
updateTimeout = setTimeout(animate, maximalUpdateDelay);
var delta = -now + (now = Date.now());
update(now, delta);
}
function main() {
clearTimeout(updateTimeout);
animate(); // update the scene
draw(); // render the scene
requestAnimationFrame(main);
}
main();
I'ld recommend taking a look at the HTML 5 - Game Development course on Udacity. I don't remember the implementation of this problem from the course (but there definitely was one), but my opinion from a gameplay perspective is that just using rAF (like your first bullet) is the most fun, even if there is game slow down due to too much processing needed on slower computers.
I think you're on the right track with the last one because it should give you the most consistency across devices running at different frame rates, but you definitely want to force your delta value down if it gets too high to avoid big jumps:
var animate = function () {
now = Date.now();
delta = now - last;
last = now;
if(delta > 20) {
delta = 20;
}
draw(delta);
requestAnimationFrame(animate);
};

Run a Function For Each Milliseconds

I am trying to run a function for each milliseconds, In order to achieve so, I just preferred setInterval concept in javascript. My code is given below,
HTML:
<div id=test>0.0</div>
Script:
var xVal = 0;
var xElement = null;
xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 1);
function startWatch(){
xVal += 1;
xElement.innerHTML = xVal;
}
so the above code is working fine. But while I am testing the result with a real clock, the real clock requires 1000 milliseconds to complete 1 second, at the same time the result require more than 1000 milliseconds to complete a second.
DEMO
Can anybody tell me,
Is there any mistakes with my code? If yes then tell me, How to display milliseconds accurately.?
There are no mistakes in your code, but JavaScript timers (setInterval and setTimeout) are not precise. Browsers cannot comply with such a short interval. So I'm afraid there is no way to precisely increment the milliseconds by one, and display the updates, on a web browser. In any case, that's not even visible to the human eye!
A precise workaround would involve a larger interval, and timestamps to calculate the elapsed time in milliseconds:
var start = new Date().getTime();
setInterval(function() {
var now = new Date().getTime();
xElement.innerHTML = (now - start) + 'ms elapsed';
}, 40);
You can't. There is a minimum delay that browsers use. You cannot run a function every millisecond.
From Mozilla's docs:
...4ms is specified by the HTML5 spec and is consistent across browsers...
Source: https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout#Minimum.2F_maximum_delay_and_timeout_nesting
The DOM can't actually update 1000 times per second. Your monitor can't even display 1000 frames in one second, for that matter. Calculate the difference between the start time and current time in milliseconds within your function and use that:
(function(){
var xElement = document.getElementById("test");
var start = new Date;
(function update(){
xElement.innerHTML = (new Date - start);
setTimeout(update, 0);
})();
}();
Updated fiddle
You can't do so using your method because of the delay rendering the HTML and running the interval. Doing it this way will display the time correctly at about 60FPS.
http://jsfiddle.net/3hEs4/3/
var xElement = null;
var startTime = new Date();
xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 17);
function startWatch(){
var currentTime = new Date();
xElement.innerHTML = currentTime - startTime;
}
You might also want to look into using requestanimationframe instead of a hardcoded setInterval like that.
The setInterval callback probably does not happen with millisecond accuracy, since the thread the timer is running on might not even actually be running when the time is up, or the browser throttles events, or any other of quite a few things.
In addition, since most Javascript engines are single threaded, what the implementation of setInterval might do is once it triggers, run your callback, and then reset the clock for the next call. Since you're doing some DOM manipulation, that might take several milliseconds on its own.
In short, you're expecting a Real Time Operating System behavior from an interpreter running inside of another application, on top of what is more than likely not an RTOS.
I had the same question and couldn't find any working solution, so I created one myself. The code below essentially calls five setTimouts every 5 ms, for each ms between 5 and 10. This circumvents the minimum 4 ms constraint, and (having checked in Firefox, Chrome, and Opera) works fairly well.
const start = performance.now();
let newNow = 0;
let oldNow = 0;
const runner = function(reset) {
// whatever is here will run ca. every ms
newNow = performance.now();
console.log("new:", newNow);
console.log(" diff:", newNow - oldNow);
oldNow = newNow
if (newNow - start < 1000 && reset) {
setTimeout(function() {
runner(true);
}, 5);
for (let i = 6; i < 11; i++) {
setTimeout(function() {
runner(false);
}, i);
}
}
};
runner(true);
It could of course be written more elegantly, e.g. so that you can more easily customize things like the graduation (e.g. 0.5 ms or 2 ms instead of 1 ms), but anyway the principle is there.
I know that in theory you could call 5 setIntervals instead, but that would in reality cause a drift that would quickly ruin the ms precision.
Note also that there are legitimate cases for the use. (I for one need continual measurement of touch force, which is not possible otherwise.)

HTML Canvas Interval vs RequestAnimationFrame

So, maybe total brainfart here. The syntax for setInterval() is pretty clear. Do something every x miliseconds. How is this best translated to using the requestAnimationFrame() ?
I have about 300 objects and each is supposed to perform an animation sequence at a certain interval (every 8, 6, 2, etc seconds)? How can I best accomplish this using requestAnimationFrame() which gets called ~60 times a second? There is probably an easy answer, I just, for the life of me, can't figure it out.
To force requestAnimationFrame to stick to a specific FPS you can use both at once!
var fps = 15;
function draw() {
setTimeout(function() {
requestAnimationFrame(draw);
// Drawing code goes here
}, 1000 / fps);
}
A little weird, but noth the most confusing thing in the world.
You can also use requestAnimationFrame not with FPS but with elapsed time in order to draw objects that need to be updated based on the time difference since the last call:
var time;
function draw() {
requestAnimationFrame(draw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now;
// Drawing code goes here... for example updating an 'x' position:
this.x += 10 * dt; // Increase 'x' by 10 units per millisecond
}
These two snippets are from this fine article, which contains additional details.
Good question by the way! I don't think I've seen this answered on SO either (and I'm here way too much)
requestAnimationFrame is pretty low level, it just does what you already said: roughly gets called at 60fps (assuming the browser can keep up with that pace). So typically you would need to build something on top of that, much like a game engine that has a game loop.
In my game engine, I have this (paraphased/simplified here):
window.requestAnimationFrame(this._doFrame);
...
_doFrame: function(timestamp) {
var delta = timestamp - (this._lastTimestamp || timestamp);
for(var i = 0, len = this.elements.length; i < len; ++i) {
this.elements[i].update(delta);
}
this._lastTimestamp = timestamp;
// I used underscore.js's 'bindAll' to make _doFrame always
// get called against my game engine object
window.requestAnimationFrame(this._doFrame);
}
Then each element in my game engine knows how to update themselves. In your case each element that should update every 2, 6, 8 seconds needs to keep track of how much time has passed and update accordingly:
update: function(delta) {
this.elapsed += delta;
// has 8 seconds passed?
if(this.elapsed >= 8000) {
this.elapsed -= 8000; // reset the elapsed counter
this.doMyUpdate(); // whatever it should be
}
}
The Canvas API along with requestAnimationFrame are rather low level, they are the building blocks for things like animation and game engines. If possible I'd try to use an existing one like cocos2d-js or whatever else is out there these days.

Categories

Resources