How do you create a smooth Canvas 2D loop with requestAnimationFrame?

Tests render pipeline timing. Outline: queue rAF per refresh; derive delta from timestamp for frame-rate independence; recurse each frame; cancel via cancelAnimationFrame. Red flag: claiming rAF locks 60fps, ignoring timestamp, or setInterval is fine.
WHAT THIS TESTS: Whether you understand the browser rendering pipeline and can reason about frame pacing, refresh rate alignment, and frame-rate independent animation logic. Interviewers want to see that you know requestAnimationFrame is not just a timer replacement but a contract with the compositor.
A GOOD ANSWER COVERS four things in order. First, scheduling: you call requestAnimationFrame and pass a callback that the browser will invoke before the next repaint, which aligns with the display refresh rate. Second, timing: you use the timestamp argument, a DOMHighResTimeStamp representing the end time of the previous frame, to calculate delta time in milliseconds so your animation advances at a consistent speed regardless of whether the monitor is 60 Hz, 120 Hz, or 144 Hz. Third, loop structure: because requestAnimationFrame is one-shot, the callback must recursively call requestAnimationFrame again to animate the next frame, and you must store the returned request ID so you can pass it to cancelAnimationFrame when pausing, stopping, or unmounting the component. Fourth, browser behavior: you note that callbacks are paused in background tabs or hidden iframes to save battery, which is a feature, not a bug.
COMMON WRONG ANSWERS include claiming that requestAnimationFrame guarantees 60 frames per second, which ignores modern high refresh rate displays. Another red flag is ignoring the timestamp argument and instead moving objects by a fixed pixel amount per frame, which makes the animation run faster on 120 Hz or 144 Hz monitors. Saying that setInterval or setTimeout are acceptable for Canvas animation is also wrong because they fire independently of the display refresh cycle, causing missed frames, unnecessary work, and tearing.
LIKELY FOLLOW-UPS: How would you handle a tab coming back to the foreground after being hidden, given that timestamps may produce a large delta? How do you throttle or cap delta time to prevent physics explosions after a lag spike? What changes if you move this logic to an OffscreenCanvas inside a worker? How would you synchronize multiple canvases or DOM elements using the shared timestamp?
ONE CONCRETE EXAMPLE: A particle moving at 200 pixels per second. Inside the rAF callback, you compute delta as timestamp minus lastTimestamp, then update position by 200 multiplied by delta divided by 1000. You then draw the particle with Canvas 2D context fillRect, store the current timestamp as lastTimestamp, and call requestAnimationFrame again. To stop, you call cancelAnimationFrame with the ID saved from the original request.
Source: developer.mozilla.org
Read the original → developer.mozilla.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.