- Diagnose jank using the Performance panel. Pinpoint main thread blocking and expensive layout calculations.
- Prevent layout thrashing by batching read and write operations. Avoid alternating rapid reads and writes.
- Achieve smooth animation CSS by promoting elements and utilizing `transform` and `opacity`.
- Always check for forced synchronous layouts. These halt rendering and degrade frame rate.
What is 'Jank'? Understanding the Difference Between Slow and Janky Animation
Jank is not just slow animation. Slow animation means the animation takes a long time to finish. Jank means the animation stutters or drops frames, failing to maintain a consistent 60 frames per second (fps) rate.
A smooth animation must hit the target frame rate consistently. Jank occurs when the browser cannot complete its rendering tasks, layout, paint, composite, within the allotted time budget for a single frame, typically 16.6 milliseconds.
Diagnosing the root cause of jank requires more than visual inspection. You must determine if the bottleneck comes from CPU-intensive JavaScript computation or GPU-intensive rendering work.
Diagnosis Deep Dive: Using DevTools to Identify Performance Killers (The Paint Storm Guide)
The browser's Performance panel is your primary tool for diagnosing jank. Do not rely only on visual playback.
Record an interaction that causes the jank. Review the resulting flame chart. Look specifically for long, continuous blocks of activity labeled "Scripting," "Layout," or "Recalculate Style."
A large, sustained block of "Scripting" time suggests heavy main thread blocking. This often results from complex calculations or inefficient JavaScript loops. A long block of "Layout" time points directly to expensive DOM manipulation, signaling potential layout thrashing.
The "Paint" phase is also critical. If paint takes too long, the browser must redraw large portions of the screen. This indicates complex visual changes that need optimization.
The Culprits: Solving Layout Thrashing, Forced Synchronous Layouts, and Main Thread Blocking
The most common causes of jank relate to how the browser calculates geometry and redraws elements.
Layout Thrashing
Layout thrashing happens when you rapidly alternate between reading geometric properties (like `offsetWidth` or `getComputedStyle()`) and writing properties that affect geometry (like setting `width` or `top`). When the browser reads these values, it must immediately recalculate the layout. This creates multiple, redundant, and costly reflows. Layout thrashing kills performance.
Forced Synchronous Layouts
This is a severe performance bottleneck. It occurs when JavaScript forces the browser to calculate the layout immediately, even if no style change triggered it. This often happens when reading a geometric property right after modifying a property that should trigger a reflow. This pause halts all rendering work, causing noticeable stuttering.
Main Thread Blocking
Any task that prevents the main thread from executing its rendering pipeline is blocking. This includes heavy computation, large DOM manipulations, or synchronous I/O. The goal of any animation performance fix is to keep the main thread free for the rendering cycle.
Beyond CSS: Advanced Strategies for Achieving 60fps Smoothness (will-change, Compositing, and Hardware Acceleration)
When CSS alone struggles to provide a smooth animation CSS experience, you must leverage the browser's rendering pipeline.
Use Transforms and Opacity
Always animate using `transform` (e.g., `translate()`, `scale()`, `rotate()`) and `opacity`. These properties are handled by the compositor and do not trigger expensive layout or paint recalculations. They allow the GPU to manage visual changes independently of the main thread.
Hardware Acceleration and Compositing
To encourage the browser to offload rendering to the GPU, use CSS properties that promote an element to its own compositing layer. While sometimes overkill, applying `will-change: transform;` can hint to the browser that the element will animate, allowing it to prepare the layer in advance. Use this property carefully, as over-applying it increases memory consumption.
Batching Reads and Writes
To prevent layout thrashing, collect all your DOM reads (e.g., dimensions) before performing any writes (e.g., changing styles). Batching all writes together, and then batching all reads together, optimizes complex interactions.
Q: Should I use requestAnimationFrame instead of setTimeout?
A: Yes. `requestAnimationFrame` synchronizes your code execution with the browser's repaint cycle. It ensures your animation updates fire exactly when the browser is ready to paint the next frame. This is essential for maintaining a stable frame rate.
Q: What is the difference between reflow and repaint?
A: Reflow (or layout) is the calculation of an element's geometry and position after a change. Repaint is the process of redrawing visual aspects like background color or shadows. Repaint happens even if the element's geometry does not change.