Browser Paint Invalidation for Looping GIF Animations
This article explains how modern browser rendering engines compute paint invalidation regions for small looping GIF animations. When a GIF loops, the engine avoids repainting the entire viewport by decoding localized frame metadata, calculating the precise dirty region across successive frames, mapping these coordinates through layout and CSS transforms, and restricting rasterization to the minimal bounding rectangle on the relevant graphics layer.
Frame Decoding and Frame Delta Computation
When a GIF animates, the browser's image decoding pipeline processes
the binary stream into distinct bitmap frames. The GIF specification
(GIF89a) allows individual frames to be smaller than the logical screen
descriptor of the image; each frame contains its own offset
(x, y) and dimensions (width, height), along
with a specific "disposal method" (such as Do Not Dispose,
Restore to Background, or Restore to Previous).
To determine the initial dirty area:
- Disposal Area: The engine tracks the area cleared by the previous frame according to its disposal method.
- New Frame Bounds: The engine reads the incoming frame's local bounding rectangle.
- Delta Union: The engine computes the mathematical union of the region vacated by the old frame and the region occupied by the new frame. This union represents the intrinsic damage rect within the image's coordinate space.
Mapping to the Layout Tree and Coordinate Spaces
Once the damage rect is determined within the raw image data, the
rendering engine maps it to the DOM and CSS layout tree. An
<img> element or background image rarely maps 1:1 to
pixel dimensions due to CSS properties such as width,
height, object-fit,
object-position, or background tiling.
- Local Box Mapping: The image's intrinsic dirty rect
is scaled and positioned relative to the element's layout box (the
LayoutObjectin Blink/Chromium ornsIFramein Gecko). - Visual Rect Transforms: The paint invalidation
subsystem projects this local rect up the layout hierarchy. It applies
CSS transforms (such as
scale,rotate, ortranslate), border-radius clipping, parent overflow clipping, and visual effects like CSS filters. - Culling: If the calculated region falls outside the current viewport or an ancestor element's scroll container clip, the engine clips or discards the invalidation entirely to prevent unnecessary processing.
Layer Compositing and Raster Invalidation
Modern browsers divide a webpage into separate composited graphics layers managed by the GPU. How the invalidation rect is handled depends on the element's layer promotion status:
- Non-Composited (Shared Layer): If the GIF shares a compositing layer with other static elements, the calculated dirty rectangle is added to that shared layer's damage list. During the paint phase, the engine records display items and triggers software or GPU rasterization only for the tiles intersecting that damage rect, leaving adjacent elements untouched.
- Composited (Dedicated Layer): If the GIF resides on
its own composited layer (promoted via properties like
will-change: transformor 3D transforms), the invalidation occurs strictly within that layer's local coordinate system. The browser updates only that layer's texture, and the GPU compositor updates the screen without triggering paint invalidation in adjacent DOM elements.
Size Thresholds and Heuristic Optimizations
Computing precise pixel-level sub-rect deltas for every single frame can sometimes introduce more CPU overhead than simply repainting the image container. For small GIFs, browser engines employ internal optimization heuristics. If the GIF's overall rendered dimensions fall below a specific size threshold, the engine frequently invalidates the entire layout bounding box of the element rather than computing granular intra-frame deltas. This trades a negligible amount of raster work for a measurable reduction in coordinate transformation and layout tree traversal overhead.