Why animate with requestAnimationFrame instead of setInterval? Run the demo to count frames rendered in ~500ms.
Whether you know rAF syncs to the display refresh and pauses in background tabs.
requestAnimationFrame(callback) schedules the callback to run BEFORE the browser's next repaint, syncing your work to the display's refresh rate (typically ~60fps, but 120fps on high-refresh screens — so never hard-code 16ms). The callback receives a high-resolution timestamp (a DOMHighResTimeStamp) you use to compute elapsed time and make motion frame-rate INDEPENDENT (move by velocity × delta, not a fixed step). Advantages over setInterval/setTimeout for animation: (1) it's aligned to paint, so you never draw more often than the screen updates or land mid-frame (which causes tearing/jank); (2) the browser PAUSES it in background tabs and when the element isn't visible, saving CPU and battery — timers keep firing and waste work; (3) callbacks are batched, so multiple animations share one frame. To animate continuously, call requestAnimationFrame again from inside the callback (a self-scheduling loop); cancel with cancelAnimationFrame(id). For the actual visual change, still mutate transform/opacity so the compositor can handle it. rAF is also the right place to batch DOM reads/writes to avoid layout thrashing. Use it for JS-driven animation and smooth scroll effects; prefer CSS transitions/animations when the movement is declarative.
JS-driven animation, smooth scroll/parallax, and batching DOM writes — anything that should track the refresh rate.