requestAnimationFrame schedules a callback to run right before the browser's next repaint, making it the correct tool for any JavaScript-driven visual animation — smoother and more efficient than setTimeout or setInterval.
1requestAnimationFrame | JavaScript Tutorial - In-Depth Guide Part 1
requestAnimationFrame(callback) schedules the callback to run right before the browser's next repaint — typically about 60 times per second on standard displays.
function animate() {
moveElement();
requestAnimationFrame(animate); // schedule the next frame
}
requestAnimationFrame(animate); // kick off the loopSyncing with the Browser
2requestAnimationFrame | JavaScript Tutorial - In-Depth Guide Part 2
Unlike setInterval, which keeps firing even on a hidden/backgrounded tab (wasting battery and CPU), requestAnimationFrame automatically pauses when the tab isn't visible.
// setInterval keeps running in a hidden tab, wasting resources
// requestAnimationFrame automatically pauses — no extra code neededAuto-Pauses on Hidden Tabs
3requestAnimationFrame | JavaScript Tutorial - In-Depth Guide Part 3
The callback receives a high-resolution timestamp argument, useful for calculating exactly how much time has passed since the last frame.
let lastTime = 0;
function animate(timestamp) {
const delta = timestamp - lastTime;
lastTime = timestamp;
updatePosition(delta);
requestAnimationFrame(animate);
}The Timestamp Argument
4requestAnimationFrame | JavaScript Tutorial - In-Depth Guide Part 4
Using the delta time between frames (rather than a fixed step) keeps animation speed consistent even if the actual frame rate varies, like dropping to 30fps under heavy load.
function animate(timestamp) {
const delta = timestamp - lastTime;
lastTime = timestamp;
position += speed * (delta / 1000); // pixels per second, frame-rate independent
requestAnimationFrame(animate);
}Frame-Rate-Independent Motion
5requestAnimationFrame | JavaScript Tutorial - In-Depth Guide Part 5
cancelAnimationFrame(id) stops a scheduled animation frame callback — essential for cleaning up an animation loop when it's no longer needed, like on component unmount.
let frameId;
function animate() {
update();
frameId = requestAnimationFrame(animate);
}
frameId = requestAnimationFrame(animate);
// later, on cleanup:
cancelAnimationFrame(frameId);Cancelling with cancelAnimationFrame
6Step-by-Step Breakdown
requestAnimationFrame(callback) schedules the callback to run right before the browser's next repaint — typically about 60 times per second on standard displays.
Unlike setInterval, which keeps firing even on a hidden/backgrounded tab (wasting battery and CPU), requestAnimationFrame automatically pauses when the tab isn't visible.
Checkpoint: Does a requestAnimationFrame-based loop automatically pause when its tab is hidden/backgrounded?
- →Yes, automatically, with no extra code needed
- →No, it keeps running just like setInterval
The callback receives a high-resolution timestamp argument, useful for calculating exactly how much time has passed since the last frame.
Using the delta time between frames (rather than a fixed step) keeps animation speed consistent even if the actual frame rate varies, like dropping to 30fps under heavy load.
Checkpoint: Why is using delta time (rather than a fixed step) important for animation speed?
- →It keeps motion speed consistent regardless of the actual frame rate
- →It has no effect on animation speed at all
cancelAnimationFrame(id) stops a scheduled animation frame callback — essential for cleaning up an animation loop when it's no longer needed, like on component unmount.
Next, we'll explore 'The Event Loop: Microtask/Macrotask Ordering'.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Respect prefers-reduced-motion in requestAnimationFrame-Driven Animations
Check `window.matchMedia('(prefers-reduced-motion: reduce)')` before starting a requestAnimationFrame animation loop, and skip or drastically simplify motion effects for users who have indicated a preference for reduced motion, to avoid discomfort or vestibular issues.
SEO Implications
- 1
Efficient Animations Improve Interaction Responsiveness Metrics
Using requestAnimationFrame instead of less efficient timer-based animation loops helps maintain a responsive main thread, contributing positively to Interaction to Next Paint, a Core Web Vital influencing search ranking.
Best Practices
Always Use requestAnimationFrame (Not setTimeout/setInterval) for Visual Animations
It's synced to the display's actual repaint cycle, automatically pauses on hidden tabs, and produces smoother results than an arbitrary timer interval.
Use Delta Time for Any Animation Whose Speed Matters
Frame-rate-independent motion (using elapsed time rather than a fixed per-frame increment) ensures consistent visual speed across devices and under varying load.
Frequent Bugs
Building an animation loop with setInterval that keeps running (and draining battery) even when the user switches to a different browser tab.
Switch to requestAnimationFrame, which automatically pauses when the tab isn't visible, with no manual visibility-change handling required.
Moving an animated element by a fixed pixel amount per frame, causing it to visibly speed up or slow down as the actual frame rate fluctuates under different device/load conditions.
Use the timestamp argument to calculate delta time between frames, and scale movement by that delta rather than assuming a fixed per-frame increment.
Real-World Examples
A Frame-Rate-Independent Progress Bar Animation
A loading progress bar needed to animate smoothly to a target percentage over a fixed real-world duration, regardless of the device's actual achieved frame rate.
function animateProgress(target, durationMs) {
const start = performance.now();
function step(now) {
const elapsed = now - start;
const progress = Math.min(elapsed / durationMs, 1);
bar.style.width = `${progress * target}%`;
if (progress < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}