Skip to content
fearchitect
Browser & Runtime Internals

Paint & Composite Optimization

Animate only transform and opacity to skip paint entirely.

By Abas TurabliReviewed

Summary

The browser rendering pipeline has four stages: style, layout, paint, and composite. Animating properties that trigger layout or paint on every frame destroys frame rate. Restricting motion to `transform` and `opacity` lets the compositor thread run animations off the main thread at 60 fps, even while JavaScript is busy.

Jump to the interview angle

Compositor thread

The browser splits rendering into a main thread (style, layout, paint) and a compositor thread (compositing layers into the final frame). The compositor thread can animate transform and opacity without touching the main thread at all — which means those animations run at 60 fps even during a JavaScript-heavy task. Any other animated property (color, width, top, left) forces the main thread to repaint before the compositor can proceed.

Animating `transform`/`opacity` jumps straight to the composite stage; geometry and color properties restart the pipeline from layout or paint.

transform vs top/left — and content-visibility

Two patterns side by side: replacing a layout-triggering animation with a compositor-only one, then using content-visibility: auto to skip paint for off-screen sections entirely.

Compositor-safe animation + content-visibilitycss
/* ❌ triggers layout on every frame */
.card-bad {
  transition: top 300ms ease, left 300ms ease;
}

/* ✓ compositor-only: no layout, no paint */
.card-good {
  transition: transform 300ms ease;
}

/* Promote an element to its own layer when you know it will animate */
.drawer {
  will-change: transform;
  /* The browser allocates a GPU texture for this element now.
     Remove will-change once the animation ends to free the memory. */
}

/* Skip painting off-screen sections entirely */
.article-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 300px; /* estimated height to hold scroll position */
}

/* CSS containment — tell the browser a subtree is independent */
.widget {
  contain: layout style paint;
  /* layout: children cannot affect outside geometry
     style:  counters/quotes don't escape
     paint:  contents are clipped and not painted outside the box */
}

transform and opacity run on the compositor thread. content-visibility: auto skips rendering off-screen sections, cutting the rendering cost of off-screen content substantially on long pages. contain narrows the scope of style/layout recalculations.

Rules for jank-free animation

  • Animate only `transform` and `opacity` — both are compositor-only properties.
  • `will-change: transform` promotes an element to its own GPU layer before animation starts.
  • `will-change` costs GPU memory; apply it just before animation and remove it after.
  • `content-visibility: auto` skips layout and paint for off-screen content sections.
  • `contain: layout style paint` isolates a subtree so changes inside it don't retrigger outside.
  • A "paint storm" occurs when a single state change invalidates paint for the whole page; containment limits the blast radius.

will-change is not free

Every element with will-change: transform gets its own GPU texture — even when idle. Slapping it on dozens of cards inflates GPU memory and can cause jank on low-end devices. Apply it dynamically (add the class on mouseenter, remove on animationend) rather than statically in a stylesheet.

Interview angle

Interviewers test whether you know which CSS properties bypass layout and paint. Name the compositor thread, explain why top/left trigger layout while transform: translate() does not, and call out will-change memory cost.

Soundbite: "Only transform and opacity are compositor-only — everything else forces a repaint at minimum."

Key terms

compositor thread
Browser thread that combines GPU layers into the final frame, independent of the main thread.
will-change
CSS hint that promotes an element to its own GPU layer before animation, at a memory cost.
content-visibility: auto
Skips layout and paint for off-screen elements; browser re-renders them when they scroll into view.
CSS contain
Property that limits how much a subtree's changes affect the rest of the document's layout and paint.
paint storm
A single DOM or style change that invalidates and repaints a large, uncontained region of the page.

Further reading

Search fearchitect

Jump to a topic, mode, or action.