# JS Tip of the Day: Time-based requestAnimationFrame

**URL:** https://forum.kirupa.com/t/js-tip-of-the-day-time-based-requestanimationframe/643091
**Category:** web dev
**Created:** [May 15, 2020, 5:22pm UTC](https://forum.kirupa.com/t/js-tip-of-the-day-time-based-requestanimationframe/643091 "2020-05-15T17:22:01Z")
**Posts on this page:** 1
**Showing post:** 1

<div class="post-metadata">

### Author: ![senocular](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/senocular/32/7217_2.png) [@senocular](https://forum.kirupa.com/u/senocular)
#### Post date: [May 15, 2020, 5:22pm UTC](https://forum.kirupa.com/t/js-tip-of-the-day-time-based-requestanimationframe/643091/1 "2020-05-15T17:22:01Z")

</div>

**Time-based requestAnimationFrame**  
Level: Intermediate

When using `requestAnimationFrame` you get optimized callbacks for handling animations in a performant manner. The way these callbacks are managed, however, doesn’t guarantee a consistently spaced apart set of calls. The timing between callbacks may differ as the resources available for rendering change, or simply as a result of running on slower devices. To keep your animations looking smooth and consistent you should base your animations on time, something `requestAnimationFrame` is already inherently set up to help you do.

Callbacks used with `requestAnimationFrame` get a `timestamp` argument, a high-precision value in milliseconds representing the time since the page was loaded. Conveniently, this value can then be used to drive time-based animations.

```javascript
function callback (timestamp) {
    // animate stuff
}
requestAnimationFrame(callback);

```

For any kind of animation that is (or can be) based on a consistently incrementing value, you can refer to the `timestamp` value directly. Consider ball moving along a sine wave. This will consist of a steady forward motion along the x-axis combined with a oscillating up and down (sine wave) motion along the y-axis. Both of these can be directly based on the `timestamp` value given to the `requestAnimationFrame` callback.

```auto
<span id="ball" style="position: absolute">O</span>

<script>
let ball = document.getElementById('ball');
let bounds = {x: 300, y: 100};

function sineLoop (timestamp) {
    let x = timestamp / 25; // moderate speed
    x %= bounds.x; // wrap back to 0
    let sine = Math.sin(timestamp / 750); // moderate speed
    let y = bounds.y * (1 + sine) / 2;
    ball.style.transform = `translate(${x}px, ${y}px)`;
    
    requestAnimationFrame(sineLoop);
}
requestAnimationFrame(sineLoop);
</script>

```

Because this animates based on time, no matter how fast or slow `requestAnimationFrame` runs, the animation will consistently render in the correct place with consistent timing and won’t appear to slow down if the rate at which rendering occurs changes. You can even test this out yourself by delaying subsequent `requestAnimationFrame` calls with `setTimeout()`.

```auto
<span id="ball" style="position: absolute">O</span>

<script>
let ball = document.getElementById('ball');
let bounds = {x: 300, y: 100};

function sineLoop (timestamp) {
    let x = timestamp / 25;
    x %= bounds.x;
    let sine = Math.sin(timestamp / 750);
    let y = bounds.y * (1 + sine)/2;
    ball.style.transform = `translate(${x}px, ${y}px)`;
    
    setTimeout(requestAnimationFrame, 200, sineLoop); // delay
}
requestAnimationFrame(sineLoop);
</script>

```

Because the animation is time based, even with this delay in rendering, it still animates at the same pace as it did before, just with fewer updates.

Of course not all animations can be directly based on an ever-increasing value as seen above. If instead you have an animation that, for example, may rely on a certain speed value, you can alternately use `timestamp` to generate a modifier for that speed in order to make it time-based. All you need to do is see how much time has elapsed since the last `requestAnimationFrame` callback was called.

```auto
<span id="ball" style="position: absolute">O</span>

<script>
let ball = document.getElementById('ball');
let ballProps = {x: 0, speed: 2, bounds: 300};

let lastTimestamp = performance.now(); // current timestamp value
function ballLoop (timestamp) {
    let timeDiff = timestamp - lastTimestamp;
    let speedFactor = timeDiff * 60/1000; // based on 60 fps
    lastTimestamp = timestamp;
    
    let { x, speed, bounds } = ballProps;
    x += speed * speedFactor; // modify based on time
    x %= bounds; // wrap back to 0
    ballProps.x = x;
    ball.style.transform = `translateX(${x}px)`;
    
    requestAnimationFrame(ballLoop);
}
requestAnimationFrame(ballLoop);
</script>

```

In this example, the ball element’s position increases every frame by a base value of 2 pixels. You can imagine that in an ideal situation where frames consistently ran at 60 frames per second, incrementing `x` by this value alone would result in a smooth and consistent animation. However, if something were to happen that throttled the frames to, say, 30 fps, the ball would be moving at half speed. This is where the `speedFactor` comes into play - a modifier based on the time elapsed between each `requestAnimationFrame` callback that adjusts the speed to keep it consistent with a 60 fps animation even when render updates may not be happening that fast.

More info:

- [requestAnimationFrame on MDN](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)

---

_[View the full topic](https://forum.kirupa.com/t/js-tip-of-the-day-time-based-requestanimationframe/643091)._
