Browse docs
Docs /signals/visual
The four signals

izri/visual

Visual answers: did the rendered interface change, and was that intentional?

It captures the class of regression the other three signals are blind to. A CSS change that shifts a button off-screen breaks no test, drifts no scope, and covers its diff perfectly. Only a picture catches it.

Tier boundary

State it plainly before anything else:

Plan What you get
Free
Hobby Capture and pixel diff, 100 snapshots/month
Team Capture, pixel diff, and semantic diff
Pro Same as Team, higher quotas

The semantic layer is paid. Capture and pixel comparison run on Hobby and above; the LLM analysis of whether a visual change is meaningful is Team and Pro. Self-hosted instances get capture and pixel diff. See plans and quotas.

Everything else in Izri — the three deterministic signals, the CLI, MCP, the Action — is free and AGPL. Visual is the one signal with a commercial boundary.

How it works

1. Capture

Snapshots are taken of the routes affected by the diff, during the test run.

2. Pixel diff

Each snapshot is compared against its baseline. Every comparison lands on one outcome:

Outcome Meaning
matched Within threshold. No meaningful change.
differed Changed beyond threshold.
no_baseline First time this snapshot has been seen. Nothing to compare against.
dimensions_changed The viewport or element size changed, so "did anything move" is ill-defined.
error This snapshot's comparison threw. Others may still have succeeded.

The 0.1% threshold

Differences below 0.1% count as matched.

This is not laziness. Anti-aliasing and sub-pixel font rendering routinely produce diffs of 0.01–0.05% between two runs of identical code. A zero-tolerance threshold would fail every build, and a signal that always fires is a signal nobody reads.

3. Semantic diff (Team and Pro)

For snapshots that differ beyond the threshold, an LLM assesses whether the change is meaningful — a deliberate redesign versus a layout break. Diffs under the threshold skip this step entirely, which is also what keeps the cost sane.

Scoring

Visual carries weight 0.15 in the composite — the lightest of the four, matching its P1 framing relative to the other three.

Because absent signals redistribute proportionally, a diff with no UI surface scores exactly as it would if the visual signal didn't exist. A backend-only PR showing izri/visual: skipped is a healthy verdict, not a gap.

no_baseline on first run

The first run against a new route has nothing to compare against, so every snapshot reports no_baseline. That is expected. The run establishes the baseline; the next run is the first one that can detect a regression.

Reading this with an agent? /docs/signals/visual.md serves the raw markdown.

Edit this page on GitHub →All docs