CSS handles most animation โ but animating VALUES (counters, canvas, game state) needs JS, and rAF is the only right tool.
Why not setInterval?
setInterval(fn, 16) drifts, fires when tabs are hidden (wasting battery), and desyncs from the display. requestAnimationFrame runs exactly once per screen frame and pauses in background tabs.
The animated counter (every landing page)
function animateCount(el, target, duration = 1200) {
const start = performance.now();
function frame(now) {
const t = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - t, 3); // ease-out cubic
el.textContent = Math.round(target * eased).toLocaleString("en-IN");
if (t < 1) requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
animateCount(statEl, 240); // 0 โ 240+, smoothlyTrigger it with an IntersectionObserver when the stat scrolls into view โ the full effect on modern sites.
Delta time (for anything moving)
let last = performance.now();
function loop(now) {
const dt = (now - last) / 1000; last = now;
x += speed * dt; // pixels per SECOND โ framerate-independent
requestAnimationFrame(loop);
}Without dt, your game runs 2x faster on 120Hz displays. With it โ identical everywhere. Rule of thumb: CSS for style transitions, rAF for values, and never animate layout properties either way.
โ All Articles