---
title: izri/visual
description: Did the rendered UI change on the routes this diff touches — and does the change matter?
order: 50
---

# `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](/docs/reference/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](/docs/signals/overview) — weights and aggregation.
- [Plans and quotas](/docs/reference/plans-and-quotas) — the full tier matrix.
