Rendering steps: where requestAnimationFrame fits in the event loop
The browser paints between tasks, at the screen's refresh rate. requestAnimationFrame runs your code right before that paint — the right place for animation.
Comes up in: JavaScript, Web fundamentals
Learn#
Sana animates a progress bar with setTimeout(step, 16). On her laptop it looks fine. On a 120 Hz phone it stutters, and on a busy page it jumps. Her timer does not know when the screen is about to be drawn.
Between tasks, the event loop sometimes runs an extra step: update the rendering. This is when a frame is drawn.
requestAnimationFrame(callback) — rAF for short — asks the browser to run your callback in that step, right before it works out styles, layout and paint. Changes you make there appear in the very next frame.
Frames follow the screen: about every 16.7 ms at 60 Hz, 8.3 ms at 120 Hz. rAF matches the screen automatically, so the same code is smooth on both.
Not every turn of the loop draws a frame. If nothing changed, or the screen is not ready, the browser skips that rendering opportunity.
function animate(now) {
const progress = Math.min(1, (now - start) / 600); // time-based, not frame-based
bar.style.transform = `scaleX(${progress})`;
if (progress < 1) requestAnimationFrame(animate);
}
const start = performance.now();
requestAnimationFrame(animate);Call stack
- click handler
Microtasks
empty
rAF callbacks
- move box
Console
empty
A task runs: your click handler. It changes a box's position and asks for an animation frame.
Read the steps as text
- A task runs: your click handler. It changes a box's position and asks for an animation frame.
- The task ends. All microtasks run first, as always.
- It is time for a frame. The browser runs every requestAnimationFrame callback.
- Then it works out styles and layout, and paints. You see the box move.
- Back to tasks. A timer that became ready meanwhile runs now.
Predict#
Predict first
In a browser: setTimeout(() => log('timeout')); requestAnimationFrame(() => log('raf')); Promise.resolve().then(() => log('promise')); log('sync'). What is guaranteed?
sync runs first and promise runs at the microtask checkpoint — that part is fixed. But timeout is a task and raf waits for the next frame. Which comes first depends on how close the next frame is. Many people answer “always timeout first” because it usually happens on their machine; the spec does not promise it.
Interview angle#
Say it like this — about 30 seconds
“rAF callbacks run in the event loop’s rendering step: after a task and its microtasks, right before the browser computes style and layout and paints. The browser runs that step about once per screen refresh — 60 or 120 times a second — and may skip it if nothing needs drawing. So rAF is the right place for visual updates, it adapts to the refresh rate, and it pauses in background tabs. Its order relative to a timer is not fixed.”
Follow-up Why is rAF better than setTimeout for animation?
Follow-up What is requestIdleCallback for?
Follow-up Why use the timestamp passed to the rAF callback?
Practice#
On your own
Write tween(from, to, duration, onUpdate, raf). Base each value on elapsed time from the frame timestamps, not on how many frames have passed. The tests pass a fake raf at 60 Hz and at 120 Hz.
Done when
- The last value is exactly
to, and the promise resolves after it. - The animation takes
durationms at both 60 Hz and 120 Hz. - Values move steadily from
fromtowardto, counting down too.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
When do requestAnimationFrame callbacks run?
In the rendering step, after tasks and microtasks, right before style, layout and paint.
How often does the browser render?
Up to once per screen refresh (about 60 or 120 times a second), skipping frames with nothing to draw.
Should an animation move by frames or by time?
By elapsed time, using the rAF timestamp.
What happens to rAF in a background tab?
Most browsers pause it until the tab is visible again.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Tasks, then microtasks, then — when the screen is ready — a frame. rAF lets you run code at the start of that frame. Visual changes belong there.
Check yourself: Is a DOM change drawn the moment you make it?
No. It is drawn at the next rendering step, after the current task and microtasks.
How it works underneathSenior rounds
The HTML Standard’s “update the rendering” step runs resize and scroll steps, then animation frame callbacks, then style, layout and paint Source: HTML Standard: Update the rendering. Each callback receives the same timestamp for that frame, so several animations stay in step Source: MDN: Window.requestAnimationFrame().
Check yourself: What runs first in the rendering step: rAF callbacks or layout?
rAF callbacks, then style and layout, then paint.
Edge cases and trapsWhere people slip
A rAF scheduled inside a rAF callback waits for the next frame. Most browsers pause rAF in background tabs and hidden iframes, and throttle timers there too. Idle callbacks (requestIdleCallback) run only when a frame has spare time; Safari supports them only in Technology Preview behind a setting, so feature-detect and fall back to setTimeout.
Check yourself: A rAF callback calls rAF again. When does the new callback run?
In the next frame, not the current one.
In productionStaff-level answers
Animate transform and opacity where you can: they skip layout and are often handled by the compositor. Read layout values (like offsetHeight) before you write styles in a frame, not after, or the browser must lay out twice. For CSS-only motion, prefer CSS transitions or the Web Animations API; use rAF when the value depends on code.
Check yourself: Name one property you can animate without causing layout.
transform (or opacity).
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- Where does
requestAnimationFramerun relative to tasks and microtasks?