I've been really busy lately, doing both work and pet projects. At the moment I'm playing with three.js trying to clone old DOS game to JavaScript. Games (and animations in general) need animation loop in which we are going to update the scene and re-render it.
Example animations are simplified and they just move a box 60px per second. But the concepts applied are universal and can be used for more complicated real life cases.
Timing problem
We all know that animating should be done using requestAnimationFrame
. My first attempt ended up being naive and it looked something like this:
;
// Initial position
;
// Start animation
;
At the first glance this looks fine. But it has one major problem. requestAnimationFrame
is usually triggered 60 times per second, but often this is not the case. For example, the most browsers will pause it if tab goes to background. Busy (or low-end) CPU will also slow it down.
Imagine for some reason that is does get triggered only 10 times per second. (In the example bellow I faked it by using 100ms setTimeout
.) In that case our box will be moved by 1px every 100ms, ending up on 10px per second.
This means our animation speed is relative to how many times requestAnimationFrame
is called per second.
That is the big timing problem we are trying to solve. Our animation should calculate the right position based on time passed, rather then just incrementing it by 1px each update.
Delta time to the rescue
Now we know what to do - adjust the position based on time passed between two updates. Every time we are doing the update, we are going to calculate how much time has passed since the last update.
To get movement of 60px per second, we need to move our box by 1px every ~16.66ms (single frame duration for 60fps). Number of frames passed is calculated by dividing delta time by a frame duration.
If we apply it to the previous 100ms (10fps) example, we'll get delta time of 100ms. Dividing it by 16.66 gives us delta frame of 6 (100ms / 16.66ms), meaning that 6 frames has passed since we last updated the scene.
Only thing left to do is to adjust the position of our box, by multiplying 1px by delta frame. This will give us movement of 6px per 100ms, which is the exactly what we are trying to achieve (60px per second).
; // 60fps frame duration ~16.66ms
// If available we are using native "performance" API instead of "Date"
// Read more about it on MDN:
// https://developer.mozilla.org/en-US/docs/Web/API/Performance
;
;
// Initial position
;
// Initial time
;
;
We fixed the timing problem, and our box is moving 60px per second. Now we can put back requestAnimationFrame
instead of setTimeout
, and we will get smooth animation without timing problems.
Check all examples side by side comparison:
Hopefully you learned something reading this. I'm going to write more posts about three.js and JavaScript game development.