The JavaScript requestAnimationFrame() method
The requestAnimationFrame() method schedules a function to run just before the browser's next repaint, syncing your animation to the display's refresh rate (usually 60fps). For a continuous animation, call it again inside the callback to form a loop. It's far smoother and more efficient than animating with setInterval().
Overview
requestAnimationFrame() (often shortened to rAF) asks the browser to run your function right before it paints the next frame. Because it's tied to the display's refresh cycle, the animation runs as smoothly as the screen allows — typically 60 times a second — and the browser can optimize it, pausing it entirely when the tab is in the background to save battery.
For a one-off, you call it once. For a continuous animation, the pattern is to call requestAnimationFrame() again from inside the callback, creating a self-sustaining loop that runs each frame until you stop it. Your callback receives a high-resolution timestamp, which you use to make movement time-based (so it looks the same regardless of frame rate) rather than tied to a fixed step per frame.
It's the correct replacement for the old habit of animating with setInterval(), which isn't synced to the display, can drop or stack frames, and keeps running in hidden tabs. To stop a rAF loop, save the ID it returns and pass it to cancelAnimationFrame(id). For simple property transitions, plain CSS transitions or animations are even better — reach for rAF when you need JavaScript-driven, frame-by-frame control (canvas, physics, custom easing).
Syntax
const id = requestAnimationFrame(callback);
function loop(timestamp) {
// update something based on timestamp
requestAnimationFrame(loop); // keep going
}
requestAnimationFrame(loop);
cancelAnimationFrame(id); // stop it
Parameters
The requestAnimationFrame() method accepts the following parameters.
| Parameter | Description |
|---|---|
callback |
A function to run before the next repaint. It receives a high-resolution timestamp (milliseconds) as its argument. |
Example
<div style="background:#e2e8f0;border-radius:6px;overflow:hidden">
<div id="bar" style="height:20px;width:0;background:#1c7ce9"></div>
</div>
<p id="out" style="font:14px system-ui"></p>
<script>
const bar = document.getElementById('bar');
const start = performance.now();
function grow(now) {
const pct = Math.min((now - start) / 2000 * 100, 100); // 2s to fill
bar.style.width = pct + '%';
document.getElementById('out').textContent = Math.round(pct) + '%';
if (pct < 100) requestAnimationFrame(grow);
}
requestAnimationFrame(grow);
</script>
Best practices
- Loop by calling
requestAnimationFrame()again inside the callback. - Use the timestamp argument for time-based movement, so animation is consistent across frame rates.
- Prefer it over setInterval() for animation — it syncs to the display and pauses in hidden tabs.
- Save the returned ID and call
cancelAnimationFrame(id)to stop; for simple effects, use CSS transitions instead.
Frequently asked questions
What is requestAnimationFrame used for?
How do I create an animation loop?
requestAnimationFrame(loop), and inside loop call requestAnimationFrame(loop) again to schedule the next frame.Why use requestAnimationFrame instead of setInterval?
How do I stop a requestAnimationFrame loop?
cancelAnimationFrame(id).