---
title: "CSS Animation Performance: What to Animate"
description: "Learn why some CSS animations run at 60fps and others stutter. See which properties are GPU-composited, why transforms are cheap, and how to fix jank."
url: https://frontendhelpers.com/css-animation-performance
updated: 2026-10-12
---

# CSS animation performance: what to animate

Why transform and opacity run at 60fps while width and top stutter — and how to fix animations that jank.

Guide · By [AurumCodeWorks](https://frontendhelpers.com/about) · 4 min read · Updated 19 July 2026

## The rendering pipeline, briefly

Understanding animation performance requires knowing what the browser does each frame. There are three stages: **layout** (calculating where every element goes), **paint** (filling in pixels — colours, text, shadows), and **composite** (assembling the painted layers into the final image).

The cost difference between them is enormous. Changing a property that affects layout forces the browser to redo all three stages, for potentially the entire page. Changing a composite-only property skips straight to the last stage — and that stage runs on the GPU, off the main thread.

At 60fps the browser has about **16.7 milliseconds** per frame for everything. Layout on a complex page can consume that alone, which is why the property you choose matters more than any other optimisation.

## The cheap properties: transform and opacity

Only two properties reliably skip layout and paint entirely: `transform` and `opacity`. Everything you want visually is usually achievable through them.

Instead of animating `width` and `height`, use `transform: scale()`. Instead of `top` and `left`, use `transform: translate()`. Instead of toggling `display`, transition `opacity` — paired with `visibility` so the hidden element leaves the tab order, as covered in our [menu animations](https://frontendhelpers.com/css-menu-animations).

This is why every effect in the [animation library](https://frontendhelpers.com/css-animations) animates transform or opacity: they're the two the compositor can handle alone.

## The expensive ones

**Layout-triggering properties** are the worst: `width`, `height`, `top`, `left`, `margin`, `padding`, `font-size`. Each frame forces a full recalculation, and because layout can cascade, changing one element's size can reflow its siblings and ancestors too.

**Paint-triggering properties** are cheaper but not free: `background-color`, `box-shadow`, `border-radius`, `color`. They skip layout but still repaint the affected area — usually fine for a small element, noticeable on a large one. Animating a big `box-shadow` is a classic cause of hover jank.

`filter` is a special case: GPU-accelerated but genuinely costly at scale, especially `blur()` on large elements.

## Diagnosing a janky animation

Open DevTools' **Performance** panel, record while the animation runs, and look at the flame chart. Purple blocks are layout, green is paint. If you see either repeating every frame during your animation, you've found the cause.

Chrome's **Rendering** panel is faster for a quick check: enable "Paint flashing" and green highlights show repainted regions. An animation that flashes a large area every frame is doing far too much work.

## will-change, and why to be careful

`will-change: transform` tells the browser to promote an element to its own compositor layer in advance, which can smooth an animation that stutters on its first frame.

It's frequently misused. Each promoted layer consumes GPU memory, and applying `will-change` broadly — or leaving it on permanently — can degrade performance rather than improve it. Apply it shortly before the animation starts and remove it afterwards, and only when you've measured an actual problem.

## Practical rules

Animate `transform` and `opacity` by default. Keep interface feedback between 150 and 300ms, and entrances between 300 and 600ms. Limit how many elements animate simultaneously, since even cheap animations add up. Always honour `prefers-reduced-motion`, which is an accessibility requirement rather than a performance one but belongs in the same habit.

And remember that CSS animations beat JavaScript ones for simple UI motion not just on bundle size, but because the browser can optimise them ahead of time and run them off the main thread — meaning they keep running smoothly even while your JavaScript is busy.

## Frequently asked questions

### Which CSS properties are cheapest to animate?

transform and opacity. They are handled by the compositor on the GPU, skipping layout and paint entirely, so they stay smooth even when the main thread is busy.

### Why is my CSS animation stuttering?

You are most likely animating a layout property such as width, height, top or margin, which forces the browser to recalculate layout every frame. Switch to transform: scale() or translate() instead.

### Should I use will-change to speed up animations?

Only when you have measured a real problem. Each promoted layer uses GPU memory, and applying will-change broadly or leaving it on permanently can make performance worse rather than better.

## Keep going

- [72 CSS animations](https://frontendhelpers.com/css-animations): Copy-paste effects that all animate compositor-friendly properties.
 
- [Core Web Vitals](https://frontendhelpers.com/core-web-vitals-guide): How LCP, INP and CLS are measured and what fixes them.
 
- [32 JavaScript tricks](https://frontendhelpers.com/js-tips): Debouncing, rAF throttling and other performance patterns.
