Skip to Content
New: codec-png-native and codec-png-wasm bring the blazediff-png codec to Node and WebAssembly, faster than spng and jSquash. Read more β†’
GuidesAnti-Aliasing and 1px Shifts

Stop Anti-Aliasing and 1px Shifts From Failing CI

Turn on anti-aliasing detection first. It finds pixels that differ only because an edge was smoothed differently and excludes them from the diff count, without loosening anything else. That alone clears most of the churn teams get after a CSS framework upgrade. What is left - a block of text that actually moved one pixel - is a different problem, and thresholds are the wrong fix for it.

One option, on by default

Every diff surface uses the same option name and the same default, so a count from one core means the same thing as a count from another:

PackageOptionDefaultAnti-aliased pixels are
@blazediff/core (JS)antialiasingtrueignored
@blazediff/core-wasmantialiasingtrueignored
@blazediff/core-native (Rust, N-API)antialiasingtrueignored
blazediff-cli, blazediff (CLI)--no-antialiasingoffignored
blazediff (Python)antialiasingTrueignored
@blazediff/agent-onignored

antialiasing: true means β€œdetect them, so they can be skipped”. Set it to false only when you want every changed pixel counted.

// JS core import { diff } from "@blazediff/core"; const result = diff(img1, img2, { output }); // Native core: the same option and the same default import { diff as nativeDiff } from "@blazediff/core-native"; const native = await nativeDiff("baseline.png", "current.png", { output: "diff.png" });
# Detection is on by default; this counts anti-aliased pixels too blazediff-cli baseline.png current.png -o diff.png --no-antialiasing

What detection actually does

For each differing pixel, BlazeDiff looks at its eight neighbors and asks whether the pixel sits on a smoothed edge:

  1. Count neighbors with no brightness difference. More than two and this is a flat region, not an edge. Not anti-aliasing.
  2. Find the darkest and the brightest neighbor. If there is no gradient at all, it is not anti-aliasing.
  3. Check that one end of that gradient is solid color. The pixel counts as anti-aliased if either the darkest or the brightest neighbor has three or more identical neighbors of its own, in both images.

That last condition is what makes it safe. An anti-aliased pixel is a blend between two solid regions, so at least one end of its gradient must be solid in the baseline and the current image. A pixel where the surrounding content genuinely changed fails that test and still counts.

The check runs in both directions, baseline against current and current against baseline. The algorithm is Vysniauskas’s anti-aliased pixel and intensity slope detector (2009), the same one pixelmatch uses.

A pixel that passes counts as 0 and is painted yellow in the diff image. A pixel that fails counts as 1 and is painted red.

So the diff image still shows you every AA pixel. They just do not fail the build. Open a diff and look at the color: yellow is ignored anti-aliasing, red is a counted difference.

Detection costs time - it inspects the neighborhood of every differing pixel. On a clean run with no differences, that cost is zero, because there are no differing pixels to inspect.

After a CSS framework upgrade

The specific case where teams lose hours: a Tailwind or design-system bump changes line height or letter spacing by a fraction, every text block re-flows by a sub-pixel, and hundreds of snapshots fail with nothing visibly wrong.

Work through it in this order:

  1. Turn on anti-aliasing detection. Removes edge-smoothing noise.

  2. Look at one diff image. If it is mostly yellow, step 1 solved it. If it is red text outlines, the text genuinely moved.

  3. Decide once, not per snapshot. If the shift is real and intended, it is one intentional change across the whole suite, not 300 reviews:

    blazediff-agent check --judge agent --json # confirm what changed blazediff-agent rewrite --failed --json # accept all of it
  4. Check the diff in the PR. Baselines are PNGs in git, so the re-baseline is reviewable as a single commit.

When the content actually shifted one pixel

Anti-aliasing detection will not help here, because the pixels really are different. Three options, worst to best:

Raise the threshold. Fast, blunt, and hides real regressions in the same band. If you do it, use a percentage so it scales with viewport size:

await expect(screenshot).toMatchImageSnapshot({ method: "core-native", failureThreshold: 0.1, failureThresholdType: "percent", });

Use a structural metric. GMSD scores edge structure rather than counting pixels, so a uniform sub-pixel shift barely moves it while a component that changed shape does:

blazediff-cli gmsd baseline.png current.png # gate around 0.05

Let an agent decide. A one-pixel shift in a footer and a one-pixel shift that breaks a button’s alignment are the same number of pixels and completely different problems. That distinction needs judgment, not a threshold:

blazediff-agent check --judge agent --json

The heuristic pass classifies each failure as regression-likely, intentional-likely, noise-likely, or ambiguous, and only ambiguous reaches the agent. Most runs hand over nothing.

Tuning the color threshold

threshold is a different knob from anti-aliasing. It sets how far apart two pixels must be, in perceptual color distance, before they count as different at all.

ValueBehavior
0.0Exact match only
0.05Strict
0.1Default, balanced
0.2Lenient

Raising threshold makes every pixel more forgiving, including the ones you care about. Prefer anti-aliasing detection, which is targeted, over a higher threshold, which is not.

Quick reference

SymptomFix
Diff image is mostly yellowAlready handled. Anti-aliasing is ignored
Diff image is red text outlines everywhereText moved. Re-baseline once, deliberately
A few red pixels on curved edgesTurn on anti-aliasing detection
Every pixel differs slightlyCross-OS rendering, not anti-aliasing
Fails on 4K, passes on 1280pxUse a percentage threshold, not a count

Next

Last updated on