Suggest an editImprove this articleRefine the answer for “The requestAnimationFrame() function”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`requestAnimationFrame(callback)` is a built-in browser API that schedules `callback` to run right before the next repaint.** The browser redraws the screen roughly 60 times per second, that is every 16.6 ms, and calls your callback exactly when it is about to paint a new frame, which is what makes the animation as smooth as possible. The callback receives a `timestamp`, the time the frame started on the `performance.now()` scale, and the call itself returns a frame ID you can pass to `cancelAnimationFrame(id)`. If you call `requestAnimationFrame()` again from inside the callback, the animation runs in a loop, frame after frame. When the tab is inactive the browser pauses these calls on its own and saves resources, which timers do not do. ```javascript function animate(time) { box.style.transform = `translateX(${time / 10}px)`; requestAnimationFrame(animate); // ask for the next frame } requestAnimationFrame(animate); ``` **Key point:** `requestAnimationFrame()` synchronises your code with the display refresh rate, which makes it the right tool for animations instead of `setTimeout`.Shown above the full answer for quick recall.Answer (EN)Image**`requestAnimationFrame(callback)` is a built-in browser API that schedules the `callback` function to run before the next repaint, that is, before the screen is drawn again.** The browser itself decides when that moment arrives, and that is what keeps the animation in step with the display refresh rate. ## Theory ### TL;DR - `requestAnimationFrame(fn)` calls `fn` right before the next repaint. - The browser paints roughly 60 frames per second, a frame every 16.6 ms, and adapts the calls to the real refresh rate of the screen. - The callback receives a `timestamp`, the frame start time in milliseconds on the `performance.now()` scale. - The call returns a frame ID, which is cancelled with `cancelAnimationFrame(id)`. - Calling it again from inside the callback forms an animation loop, frame after frame. - On an inactive tab the browser pauses the calls, so the animation wastes neither battery nor CPU. ### Quick example ```javascript function animate(time) { console.log('Frame at', time, 'ms'); requestAnimationFrame(animate); // request the next frame } requestAnimationFrame(animate); ``` What happens here: - `animate` is called before every repaint; - `time` is a timestamp in milliseconds showing exactly when the frame started being drawn (per `performance.now()`). ### How it works under the hood 1. You call `requestAnimationFrame(fn)`. 2. The browser stores `fn` in the list of callbacks for the current frame. 3. When the browser is ready to paint a new frame (usually about 16 ms later), it calls all such functions **before the repaint**. 4. After the callbacks have run, the browser repaints the screen. If you call `requestAnimationFrame()` again from inside the callback, the animation runs **in a loop**, frame after frame. Note that this is a separate queue of visual updates: it is processed before rendering, not together with ordinary macrotasks. ### Smooth movement of an element ```javascript const box = document.querySelector('.box'); let start = null; function move(timestamp) { if (!start) start = timestamp; const progress = timestamp - start; box.style.transform = `translateX(${progress / 5}px)`; if (progress < 2000) { // 2 seconds requestAnimationFrame(move); } } requestAnimationFrame(move); ``` Here the element moves smoothly for two seconds. Progress is measured not by the number of frames but by the difference between timestamps, so the animation lasts the same amount of time on a fast and on a slow device. The browser optimises the calls by itself: if the tab is not visible, `requestAnimationFrame` **is paused** so that no resources are wasted. ### Differences from `setTimeout` and `setInterval` | Criterion | `setTimeout` / `setInterval` | `requestAnimationFrame` | | --- | --- | --- | | Frequency | Fixed (every 16 ms, for example) | Synchronised with the screen refresh rate | | Power usage | Not paused in hidden tabs | Stops automatically when the tab is hidden | | Smoothness | Can stutter under load | The smoothest possible painting | | Used for | Timers, background tasks | Animations, smooth UI updates | | Passes a timestamp | No | Yes, the frame time from `performance.now()` | The same movement written both ways: ```javascript function moveWithTimeout() { box.style.left = `${parseInt(box.style.left || 0) + 1}px`; setTimeout(moveWithTimeout, 16); } function moveWithRaf() { box.style.left = `${parseInt(box.style.left || 0) + 1}px`; requestAnimationFrame(moveWithRaf); } ``` `setTimeout` does not account for browser load and can skip frames: 16 ms is only a minimum delay, not a guarantee. `requestAnimationFrame` adapts to the display frequency, including 120 Hz screens, and always gives the smoothest result. ### Cancelling a frame If a scheduled frame is no longer needed, it is cancelled by its ID: ```javascript const id = requestAnimationFrame(step); cancelAnimationFrame(id); ``` This is mandatory when a component unmounts or an animation stops early, otherwise the loop keeps running and holds references to DOM nodes that are no longer needed. ### Summary | Property | Description | | --- | --- | | **What it does** | Runs a callback before the next repaint | | **When it is called** | When the browser is ready to paint a new frame (about 60 FPS) | | **Callback argument** | `timestamp`, the frame start time | | **Returns** | A frame ID (for `cancelAnimationFrame`) | | **Stops automatically** | Yes, when the tab is inactive | | **Task type** | A separate queue of visual updates, before rendering | | **Perfect for** | Animations, smooth transforms, progress bars | ### Common mistakes - **Animating with `setInterval(fn, 16)`.** A timer is not synchronised with the screen, frames drift, and on a hidden tab the work continues for nothing. - **Measuring progress in frames instead of time.** The number of frames depends on the device, so an animation on a 120 Hz screen finishes twice as fast. Measure from `timestamp`. - **Not cancelling the frame.** Without `cancelAnimationFrame()` the loop keeps running after the element is gone and keeps it in memory. - **Mixing style reads and writes.** Reading `offsetWidth` after writing a style inside the callback forces a synchronous reflow and destroys smoothness. Read first, then write. - **Putting heavy computation in the callback.** If the frame does not fit into 16 ms, the browser drops it. Move heavy work to a Worker or spread it across frames. - **Expecting it outside the browser.** This is a DOM API, plain Node.js does not have it, and timers are used there for similar tasks.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.