Skip to content

Why screenshots drift

Take the same screenshot of the same unchanged page on two machines and the files will differ. This page explains why, and why shotlist reports the difference rather than trying to eliminate it.

Where the differences come from

Text rasterization

Turning a glyph outline into pixels involves hinting, subpixel positioning and antialiasing, and the rules differ between operating systems and between Chromium versions. The same word at the same size lands on slightly different pixels. This affects every screenshot, because interfaces are mostly text.

Available fonts

A font stack ending in sans-serif resolves to whatever the machine has. That is a different typeface on a Linux runner than on a designer's laptop, which is not a subtle pixel difference but a different picture. The same applies to callout labels, which is why shotlist warns when the typeface it measured is not the one you asked for.

Timing

Animations, lazily loaded images and data that arrives over the network all make the moment of capture significant. site.reducedMotion is on by default for this reason, and site.ready and site.settle exist to move the capture to a defined point rather than an arbitrary one.

The content itself

Clocks, relative timestamps, generated identifiers, random avatars and live totals differ on every run by design. This is the one category you can address directly, with masks or ignored regions.

Why not just normalize it away

A comparison that ignored enough to be immune to all of this would have to ignore text rendering almost entirely, and text is where the meaningful changes are. A button whose label changed, a heading that moved, a number that is now wrong: those are text differences, and a tool tuned to forgive text differences forgives them too.

The two tuning knobs shotlist offers are deliberately narrow. tolerance forgives a small shift in a single channel, which covers antialiasing. threshold forgives a small proportion of the image, which covers a scattering of edge pixels. Neither forgives a region.

Recording the machine instead

Since the differences cannot be eliminated, shotlist records where the committed images came from. --install writes shotlist.baseline.json beside the configuration, holding the shotlist, Playwright and Chromium versions and the platform.

When --check runs somewhere that does not match, it says so before the results:

! this is not the machine the committed images were taken on:
    chromium: 141.0.0.0 → 139.0.0.0
    platform: darwin → linux
  Differences below may be that, rather than the site.

This does not make the comparison more accurate. It changes how you read the result, which is the part that was actually going wrong: a reviewer who sees a 4% difference and no context assumes a regression, and a reviewer who has seen this warning three times starts ignoring real ones.

The same information is in the drift field of the JSON report, so an automated job can distinguish a re-render from a regression without reading prose.

What this means in practice

  • Take and publish the committed screenshots on the same platform your continuous integration uses. A container is the reliable way to do this.
  • Pin the versions that matter. Chromium arrives with Playwright, so pinning Playwright pins the renderer.
  • Treat a percentage as a prompt to look, not a verdict. Use --diff, which is why it exists.
  • Handle changing content with masks or ignored regions rather than with tolerances.