How requestAnimationFrame Synchronizes JS Animations

The window.requestAnimationFrame() API is a native browser method designed to execute smooth, performant JavaScript animations. This article explores how requestAnimationFrame works, why it replaced legacy timer-based methods like setInterval and setTimeout, and how it synchronizes animation updates directly with the user’s display refresh rate to eliminate visual stutter and conserve system resources.

What is requestAnimationFrame?

The requestAnimationFrame (rAF) method tells the browser that a script wishes to perform an animation and requests that the browser call a specified callback function before the next repaint.

Instead of forcing the browser to render changes at arbitrary intervals, requestAnimationFrame yields rendering control to the browser. The browser then executes the callback right before the screen updates, typically matching the display’s native refresh rate (such as 60Hz, 120Hz, or 144Hz).

The Limitations of Legacy Animation Methods

Before requestAnimationFrame, developers relied on setTimeout() and setInterval() to create animations by incrementally updating element styles over fixed millisecond intervals. This approach has critical drawbacks:

How requestAnimationFrame Synchronizes Animations

The requestAnimationFrame API resolves these issues by coordinating JavaScript execution directly with the browser’s internal rendering pipeline through several mechanisms:

1. Vertical Synchronization (VSync) Alignment

Displays rely on vertical synchronization (VSync) signals to draw new frames cleanly from top to bottom. requestAnimationFrame queues JavaScript callbacks to run in sync with the hardware VSync tick. By aligning execution to this cycle, the browser ensures that state changes are computed and painted precisely when the screen is ready to display them.

2. High-Resolution Timestamps

When the browser executes the callback registered with requestAnimationFrame, it automatically passes a high-resolution timestamp (DOMHighResTimeStamp) representing the exact time the frame started rendering. Developers use this timestamp to calculate time-based deltas rather than frame-based deltas:

let start;

function animate(timestamp) {
  if (!start) start = timestamp;
  const elapsed = timestamp - start;

  // Move an element based on elapsed time (e.g., 0.1px per millisecond)
  element.style.transform = `translateX(${Math.min(0.1 * elapsed, 200)}px)`;

  if (elapsed < 2000) {
    // Request the next frame
    window.requestAnimationFrame(animate);
  }
}

// Start the animation
window.requestAnimationFrame(animate);

Using time deltas guarantees that animations progress at a consistent real-world speed regardless of whether the device runs at 60Hz, 120Hz, or experiences a temporary frame drop.

3. Rendering Pipeline Optimization

When using requestAnimationFrame, the browser batches multiple style recalculations, layout steps, and paints into a single reflow cycle. This prevents layout thrashing and reduces the overall computational load on the main thread.

4. Automatic Power and Tab Throttling

If a user navigates to a different tab or minimizes the browser window, the browser automatically pauses or severely throttles requestAnimationFrame loops. Execution resumes seamlessly when the tab returns to the foreground, eliminating background CPU and battery drain.

Summary

requestAnimationFrame synchronizes JavaScript animations by integrating script execution directly into the browser’s hardware-aligned rendering loop. By replacing fixed-interval timers with VSync-aligned frame requests, it delivers stutter-free rendering, optimized computational overhead, and improved energy efficiency.