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.
Related
- Signals overview — weights and aggregation.
- Plans and quotas — the full tier matrix.
Reading this with an agent? /docs/signals/visual.md serves the raw markdown.
Edit this page on GitHub →All docs