Back to Performance
Performance
medium
mid

How would you identify and fix a janky CSS animation on mobile devices?

Profile first: record the animation in Chrome DevTools Performance panel (with CPU throttling), look for long frames, purple Layout / green Paint bars. Jank usually means you're animating layout/paint properties. Fix by switching to `transform`/`opacity`, promoting the element with `will-change`, reducing paint area, debouncing JS-driven animation, and respecting `prefers-reduced-motion`.

6 min read·~12 min to think through

A janky animation = the browser can't finish a frame within its budget (~16.7ms at 60fps), so frames drop. The approach is measure → diagnose → fix → verify — never guess.

Step 1 — Reproduce and measure

  • Test on a real mid-range device or use DevTools CPU throttling (4–6×) and network throttling — desktop hides mobile jank.
  • Open DevTools → Performance, record the animation.
  • Look at the frames track: long frames (>16ms) are jank. Inspect them:
  • Purple bars = Layout (reflow) — you're animating a layout property.
  • Green bars = Paint — large or expensive paint areas.
  • Yellow = scripting — JS is the bottleneck.
  • The Rendering tab: enable "Paint flashing" (see what repaints) and "Layer borders."

Step 2 — Common causes of mobile jank

  1. Animating layout/paint propertieswidth, height, top, left, margin, box-shadow. Each frame triggers reflow/repaint on the main thread.
  2. Large paint areas — animating something that forces a big region (or the whole page) to repaint.
  3. Too many / too few compositor layers — none means no GPU offload; too many exhausts mobile GPU memory.
  4. JS-driven animation on the main thread, blocked by other work.
  5. Heavy effects — big blur(), complex box-shadow, many gradients — GPUs on phones are weak.
  6. Layout thrashing — reading layout (offsetHeight) inside the animation loop.

Step 3 — Fixes

  • Animate transform and opacity only — compositor-only, GPU, off-main-thread. Replace left with translateX, width with scale.
  • Promote the element: will-change: transform (or transform: translateZ(0)) — sparingly, remove after.
  • Shrink the paint area — animate a small element, not a full-width container; use contain: paint.
  • Move JS animation off the main thread — use CSS transitions/animations or the Web Animations API instead of setInterval; or use a Web Worker for the computation.
  • Reduce effect cost — smaller blur radius, simpler shadows, fewer animated nodes.
  • Use requestAnimationFrame for any JS-driven animation, never setTimeout.
  • Respect prefers-reduced-motion — disable or simplify for users who opt out (also an accessibility win).

Step 4 — Verify

Re-record. Frames should be green and under 16ms. Check on the actual device, not just throttled desktop.

Senior framing

The senior answer leads with "profile, don't guess" — name the DevTools workflow and what the colored bars mean. Then the fix is the rendering-pipeline insight: jank = reaching Layout/Paint per frame, so move to compositor-only properties. Mentioning mobile-specific realities (weak GPUs, layer-memory limits, CPU throttling to simulate) and prefers-reduced-motion shows you've actually debugged this, not just read about transform.

Follow-up questions

  • What do the purple and green bars in the Performance panel mean?
  • Why test with CPU throttling, and what multiplier?
  • How can too many compositor layers hurt mobile performance?

Common mistakes

  • Guessing at the fix instead of profiling.
  • Testing only on a fast desktop.
  • Animating layout properties and adding will-change as a 'fix' without changing the property.
  • Leaving will-change on every element permanently.

Edge cases

  • Compositor-only animations can still jank if the layer is huge (lots of texture memory).
  • iOS Safari handles will-change and layers differently than Chrome.
  • prefers-reduced-motion should be honored regardless of performance.

Real-world examples

  • Slide-in drawers, parallax, carousels, scroll-linked animations on mobile.

Related questions