bi-09 — Analysis

Required invariants

  1. Paint produces a recording, not pixels. Display items are drawing commands; raster happens later, elsewhere, possibly on another thread or process.
  2. A paint chunk is a run of display items sharing one property tree state. That definition is what makes property-tree-only updates cheap.
  3. Paint order is not DOM order. Stacking contexts are atomic; z-index is scoped to them.
  4. Cached paint output is reused when nothing that affects it changed — at display-item and at subsequence granularity.
  5. Hit testing walks the painted representation in reverse paint order, and the compositor needs its own copy of hit-test data to answer input without the main thread.

The mechanism behind the advice

"Animate transform and opacity" is usually justified by "the GPU is fast." That explanation is wrong and it mispredicts.

The correct chain:

transform/opacity change  →  a property-tree node value changes
                          →  display list unchanged, so no re-record
                          →  tiles unchanged, so no re-raster
                          →  compositor produces a frame from existing tiles
                          →  the main thread is not involved

Stages were skipped; a processor was not made faster. This predicts the exceptions correctly: filter: blur() is also composited and still expensive, because the effect itself costs GPU work per frame. A hardware explanation cannot tell you that.

Failure modes

BreakConsequence
Bake transforms into display itemsthe cheap animation path becomes impossible
Paint in DOM orderoverlapping content renders wrong
Skip paint invalidation for a propertystale pixels
will-change on thousands of elementsmemory blow-up; the "optimisation" loses
Independent snapping of adjacent boxes1px seams, blurry borders

Sub-pixel layout, integer pixels

Layout uses LayoutUnit (fixed point, 1/64 px); paint must produce device pixels. Integer layout would accumulate error — 100 boxes each rounded up 0.4px is a 40px drift — so positions stay precise and snapping happens at paint time.

That is why "why is this blurry" is a paint-time question about the effective transform, not a CSS question, and why a fractional ancestor transform can make a whole subtree blurry mid-animation and crisp at rest.

The recurring structure

Mark-then-walk-then-invalidate appears here (PrePaintTreeWalk), in style (bi-07), and in layout (bi-08). Three subsystems, one pattern: record cheaply during mutation, resolve precisely once per frame. It is what makes the pipeline incremental, and it is why interleaving a read into a write loop is catastrophic rather than merely slow.