At 60fps, each animation frame has a 16ms budget. Any JavaScript that runs synchronously inside a frame — including a setInterval callback or a DOM layout read followed by a write — can consume that budget entirely, causing the browser to skip the frame and produce visible stutter. ISO 25010:2011 time-behaviour captures the responsiveness requirement; the user-visible failure is an animation that stutters at irregular intervals, particularly during scroll or on lower-powered devices.
Info because frame-budget violations produce cosmetic jank rather than functional failures — they are worth fixing for polish and accessibility but do not prevent users from completing tasks.
Replace setInterval-driven animations with CSS transitions or requestAnimationFrame. For values that animate over time, CSS transform and opacity are GPU-composited and do not trigger layout.
/* components/animated-card.module.css — GPU-composited animation */
.card {
transition: transform 0.25s ease, opacity 0.25s ease;
will-change: transform; /* hint GPU layer promotion */
}
.card:hover {
transform: translateY(-4px);
}
For JS-driven values, schedule work with requestAnimationFrame rather than setInterval to stay synchronized with the browser's paint cycle.
ID: performance-deep-dive.regression-prevention.animation-frame-budget
Severity: info
What to look for: Count all animation implementations. Enumerate which use requestAnimationFrame or CSS transforms vs. setInterval or property-based animations. Examine animation and scroll handling code. Check for use of requestAnimationFrame. Look for CSS transforms and transitions vs. property-based animations (JS-driven).
Pass criteria: Animations use requestAnimationFrame or CSS transforms (GPU-accelerated). Frame rate stays at 60fps (16ms per frame). No jank during scrolling or animations. At least 90% of animations should use GPU-accelerated techniques (CSS transforms or requestAnimationFrame).
Fail criteria: Animations use setInterval or property-based JS animations that block main thread. Frame rate drops below 60fps.
Skip (N/A) when: The application has no animations or scroll interactions.
Cross-reference: For paint profiling of animations, see paint-profiling-clean.
Detail on fail: "Scroll listeners change layout properties per frame — main thread blocked, frame rate drops to 30fps" or "Animations use setInterval instead of requestAnimationFrame"
Remediation: Use hardware-accelerated animations with CSS transforms or requestAnimationFrame.
/* components/animated-card.module.css — GPU-accelerated */
.card { transition: transform 0.3s ease; }
.card:hover { transform: translateY(-4px); }