Skip to content

requestAnimationFrame

This lesson explains requestAnimationFrame with clear examples, use cases, and best practices so you can use HTML5 APIs confidently in real web applications.

HTML5 requestAnimationFrame API Overview

The HTML5 requestAnimationFrame() API allows JavaScript to run animation code before the browser performs the next repaint. It is the recommended way to create smooth animations, canvas games, visual effects, sliders, counters, progress indicators, scroll effects, and custom UI motion in modern web applications.

Unlike setTimeout() or setInterval(), requestAnimationFrame() is synchronized with the browser's refresh rate. This improves animation smoothness, avoids unnecessary work, and helps the browser pause animations when the page is not visible.

Feature Description
API Name requestAnimationFrame API
Main Method requestAnimationFrame()
Cancel Method cancelAnimationFrame()
Callback Timing Runs before the next browser repaint.
Best Use Smooth animations, canvas rendering, game loops, UI motion.
Related APIs Canvas API, Performance API, Web Animations API, CSS Transitions.

Basic requestAnimationFrame Example

// HTML5 requestAnimationFrame API

function animate(timestamp) {
  console.log(
    "Animation frame:",
    timestamp
  );

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

The callback receives a high-resolution timestamp. Calling requestAnimationFrame() again inside the callback creates a continuous animation loop.

How requestAnimationFrame Works

Step Description Example Syntax
Create Callback Define animation logic in a function. function animate(time) { ... }
Schedule Frame Ask browser to run callback before next repaint. requestAnimationFrame(animate)
Read Timestamp Use timestamp to calculate smooth motion. timestamp
Update UI Change transform, opacity, canvas, or visual state. element.style.transform
Continue Loop Request another frame if animation should continue. requestAnimationFrame(animate)
Cancel Frame Stop scheduled animation when no longer needed. cancelAnimationFrame(id)

requestAnimationFrame Methods

Method Description Example Syntax
requestAnimationFrame() Schedules an animation callback before the next repaint. requestAnimationFrame(callback)
cancelAnimationFrame() Cancels a scheduled animation frame. cancelAnimationFrame(frameId)
performance.now() Provides precise timing for custom animations. performance.now()

Move Element Smoothly

<div id="box">
  Move Me
</div>

<script>
const box =
  document.querySelector("#box");

let position = 0;

function animate() {
  position += 2;

  box.style.transform =
    "translateX(" + position + "px)";

  if (position < 300) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);
</script>

This example moves an element horizontally until it reaches 300 pixels. Using transform is usually better for animation performance than changing layout-heavy properties such as left.

Time-Based Animation Example

const box =
  document.querySelector("#box");

let startTime = null;
const duration = 1000;
const distance = 300;

function animate(timestamp) {
  if (!startTime) {
    startTime = timestamp;
  }

  const elapsed =
    timestamp - startTime;

  const progress =
    Math.min(elapsed / duration, 1);

  const position =
    progress * distance;

  box.style.transform =
    "translateX(" + position + "px)";

  if (progress < 1) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

Time-based animation makes movement consistent across devices with different refresh rates.

Cancel Animation Frame

let frameId = null;

function animate() {
  console.log("Running animation");

  frameId =
    requestAnimationFrame(animate);
}

document
  .querySelector("#start")
  .addEventListener("click", () => {
    frameId =
      requestAnimationFrame(animate);
  });

document
  .querySelector("#stop")
  .addEventListener("click", () => {
    cancelAnimationFrame(frameId);
  });

Use cancelAnimationFrame() to stop animations when users pause, close, hide, or leave animated UI.

Canvas Animation Example

const canvas =
  document.querySelector("#gameCanvas");

const context =
  canvas.getContext("2d");

let x = 0;

function draw() {
  context.clearRect(
    0,
    0,
    canvas.width,
    canvas.height
  );

  context.beginPath();
  context.arc(
    x,
    80,
    20,
    0,
    Math.PI * 2
  );
  context.fill();

  x += 2;

  if (x < canvas.width) {
    requestAnimationFrame(draw);
  }
}

requestAnimationFrame(draw);

Canvas games and visualizations commonly use requestAnimationFrame() for rendering loops.

Basic Game Loop Pattern

let lastTime = 0;

function gameLoop(timestamp) {
  const deltaTime =
    timestamp - lastTime;

  lastTime =
    timestamp;

  update(deltaTime);
  render();

  requestAnimationFrame(gameLoop);
}

function update(deltaTime) {
  console.log(
    "Update game state:",
    deltaTime
  );
}

function render() {
  console.log("Render frame");
}

requestAnimationFrame(gameLoop);

Game loops separate updating state from rendering frames. The deltaTime value helps make movement and physics independent from frame rate.

Animated Progress Bar Example

const progressBar =
  document.querySelector("#progress");

let startTime = null;
const duration = 2000;

function animateProgress(timestamp) {
  if (!startTime) {
    startTime = timestamp;
  }

  const progress =
    Math.min(
      (timestamp - startTime) / duration,
      1
    );

  progressBar.style.width =
    progress * 100 + "%";

  progressBar.setAttribute(
    "aria-valuenow",
    Math.round(progress * 100)
  );

  if (progress < 1) {
    requestAnimationFrame(animateProgress);
  }
}

requestAnimationFrame(animateProgress);

Animation can update both visual styles and accessibility attributes such as aria-valuenow.

Scroll-Based UI Update Example

let ticking = false;

function updateHeader() {
  const scrollY =
    window.scrollY;

  document.body.classList.toggle(
    "is-scrolled",
    scrollY > 50
  );

  ticking = false;
}

window.addEventListener("scroll", () => {
  if (!ticking) {
    requestAnimationFrame(updateHeader);
    ticking = true;
  }
});

This pattern prevents scroll handlers from running too frequently and allows the browser to coordinate visual updates efficiently.

Respect Reduced Motion Preference

const prefersReducedMotion =
  window.matchMedia(
    "(prefers-reduced-motion: reduce)"
  ).matches;

if (!prefersReducedMotion) {
  requestAnimationFrame(animate);
} else {
  console.log(
    "Reduced motion enabled. Skip animation."
  );
}

Always respect users who prefer reduced motion. Provide instant state changes or simpler transitions instead of large animated movement.

requestAnimationFrame vs setTimeout vs setInterval

Feature requestAnimationFrame setTimeout / setInterval
Best For Visual animation and rendering. Delayed or repeated non-visual tasks.
Repaint Sync Runs before browser repaint. Not synchronized with repaint.
Hidden Tabs Usually paused or throttled. May still run, but often throttled.
Smoothness Better for animation. Can cause jank or missed frames.
Timestamp Receives high-resolution timestamp. No automatic animation timestamp.

Animation Performance Tips

  • Animate transform and opacity whenever possible.
  • Avoid animating layout-heavy properties such as top, left, width, and height.
  • Use time-based animation with the callback timestamp.
  • Cancel animations when they are no longer needed.
  • Avoid heavy DOM reads and writes inside every frame.
  • Batch layout reads before layout writes.
  • Use CSS transitions or Web Animations API for simple animations.
  • Use Canvas or WebGL for complex visual rendering.

Common requestAnimationFrame Use Cases

  • Custom UI animations.
  • Canvas games and visualizations.
  • Progress bars and counters.
  • Scroll-based visual updates.
  • Physics simulations.
  • Drag and resize animations.
  • Chart animations.
  • Animated dashboards and data visualizations.

requestAnimationFrame Best Practices

  • Use requestAnimationFrame() for visual updates.
  • Use setTimeout() for delays that are not animation-related.
  • Use the timestamp argument for smooth time-based motion.
  • Store frame IDs so animations can be cancelled.
  • Respect prefers-reduced-motion.
  • Keep each frame lightweight to avoid jank.
  • Clean up animations when components unmount or pages change.
  • Prefer CSS animations for simple state transitions when possible.

Common requestAnimationFrame Mistakes

  • Using setInterval() for smooth visual animations.
  • Forgetting to call requestAnimationFrame() again inside a loop.
  • Not cancelling animations when no longer needed.
  • Using frame count instead of elapsed time for movement.
  • Running heavy calculations on every frame.
  • Animating layout-heavy CSS properties.
  • Ignoring reduced motion preferences.
  • Creating multiple unnecessary animation loops.

requestAnimationFrame vs CSS Animations vs Web Animations API

Feature requestAnimationFrame CSS Animations Web Animations API
Control Level Full JavaScript control. Style-based control. JavaScript control over animation objects.
Best For Games, canvas, custom logic. Simple UI transitions. Programmatic UI animations.
Performance Depends on code quality. Often optimized by browser. Browser-optimized animation engine.
Needs Manual Loop Yes. No. No.
Good for Canvas Yes. No. No.

Key Takeaways

  • requestAnimationFrame() schedules animation before the next repaint.
  • It is better than timers for visual animation.
  • Use the timestamp argument for frame-rate-independent movement.
  • Use cancelAnimationFrame() to stop scheduled frames.
  • Keep each animation frame lightweight for smooth performance.
  • Respect reduced motion preferences for accessibility.

Pro Tip

Use requestAnimationFrame() when JavaScript must control each frame, such as canvas games, physics, or custom scroll effects. For simple button hover effects, fades, and transitions, CSS animations or the Web Animations API are usually cleaner and easier to maintain.