Catch UI regressions before they merge.
Every pull request gets a visual check. Your CI takes the screenshots, a person approves each change.
Free up to 10 GB. No card. Playwright, Storybook or any folder of PNGs.
j k movea approver reject1 2 3 4 viewsd diff overlay? all keys
#412Tighten pricing cards
Sign in/Error600
1.51% / 3,393 px


Works with the tests you already have.
- 1Install the GitHub AppPick the repositories. Each one becomes a project.
- 2Add the reporter or one CI stepPlaywright, Storybook or any folder of PNGs.
- 3Open a pull requestThe check links to the review page. Approve, and it turns green.
Using a coding agent? The quickstart has a prompt that sets it up for you.
import { defineConfig } from "@playwright/test";
export default defineConfig({
reporter: [["list"], ["stateofpixel/playwright"]],
});import { test } from "@playwright/test";
import { snapshot } from "stateofpixel/playwright";
test("empty cart", async ({ page }) => {
await page.goto("/cart");
await snapshot(page, "Checkout/Empty cart");
});snapshot() waits for fonts, disables animations and hides the caret. The reporter uploads when the run ends, once per shard. Read the Playwright guide
Nothing to wait for.
One pull request, one CI run
stateofpixel
One CLI step after your tests. Your runner does the work.
0.0 smedian upload on our own CI, from hashing to the check
- Hash every PNGSHA-256 of each screenshot, on your runner.
- Upload new imagesHashes we have seen get no upload.
- CompareThe CLI diffs each change against the baseline.
- Report the checkThe pull request gets a check with the review link.
Hosted rendering
Tools that take your build and render it in their own browsers.
- Upload the build
- Wait in a queue
- Render in their browsers
- Compare
- Report the check
Access follows GitHub.
- Remove someone from the repository and they lose access here too.
- Private builds show only to people who can read the repository on GitHub.
| Role on GitHub | Builds and baselines | Approve and reject | Project settings | Plan and billing |
|---|---|---|---|---|
| Owner | Yes | Yes | Yes | Yes |
| Admin | Yes | Yes | Yes | No |
| Write | Yes | Yes | No | No |
| Read | Yes | No | No | No |
- OwnerBuilds and baselines, approve and reject, project settings, plan and billing
- AdminBuilds and baselines, approve and reject, project settings
- WriteBuilds and baselines, approve and reject
- ReadBuilds and baselines
How it handles sharding, merges and outages.
While CI runs
Sharded suites
Each shard uploads its part, and the check reports once, after the last one. Use --shard 2/4, or --shard auto with a finalize step.
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npx playwright test --shard ${{ matrix.shard }}/4
- run: npx stateofpixel upload screenshots --shard ${{ matrix.shard }}/4- stateofpixel Waiting for screenshots (2 of 4 shards)Details
While CI runs
Several suites
Storybook and Playwright in one repository each get their own baselines and their own check, like stateofpixel/storybook.
steps:
- run: npx playwright test
- run: npx stateofpixel upload screenshots --build-name e2e
- run: npx storybook build
- run: npx stateofpixel storybook storybook-static --build-name storybook- stateofpixel/e2e No visual changesDetails
- stateofpixel/storybook 3 changes to reviewDetails
While CI runs
Partial runs
Pass --subset when only some tests ran, and missing snapshots are not reported as removed. The Playwright reporter does this on its own when a test fails.
npx stateofpixel upload screenshots- Checkout/Form1280Did not run, so it shows as removed
- Checkout/Success1280Did not run, so it shows as removed
When the branch changes
New pushes and rebases
An image approved once on a pull request stays approved on the next push. A rejected image comes back as pending, with a note.
git rebase main
git push --force-with-lease- Pricing/Plans1280Approved in build #409 by @octocat (carried over)
- Header/Default1280Waiting for review, rejected in build #409
When the branch changes
Squash and rebase merges
The build on main finds the pull request it came from, and marks changes that were never approved there.
on:
push:
branches: [main]
pull_request:From PR #412. Its last build is #418.
- Footer/Default1280Approved automatically, not reviewed on PR
When the branch changes
Flaky screenshots
A snapshot that flips back and forth on main, or differs between two builds of the same commit, is marked as looking flaky on the review page.
npx stateofpixel compare screenshots baseline --threshold 0.2
# Writes stateofpixel-report/index.html- Invoices/Empty1280Looks flaky: flipped 3 times in 10 builds.
Limits and outages
Large suites
Up to 20,000 snapshots a build. An unchanged screenshot costs one hash, not an upload.
stateofpixel build #411 feat/header vs main (#405)
1,500 snapshots 1,488 unchanged 10 changed 2 added 1 removed
uploaded 22 images (1.3 MB) in 2.1 s
review: https://stateofpixel.com/acme/web-app/builds/411- stateofpixel 12 changes to reviewDetails
Limits and outages
Our outages
If stateofpixel is down or rate limited, the upload warns and exits 0. Pass --strict to fail instead.
npx stateofpixel upload screenshotsLimits and outages
Running out of storage
The CLI warns at 80%. After 14 days over the limit, new images are not stored and the check passes with a note, so CI keeps passing.
Pay for storage, nothing else.
Free
$0 /mo
No card needed. Enough for most teams.
10 GB of stored screenshots
25 GB
$15 /mo
Billed monthly
25 GB of stored screenshots
Upgrade from the account's Billing page.
100 GB
$100 /mo
Billed monthly
100 GB of stored screenshots
Upgrade from the account's Billing page.
500 GB
$500 /mo
Billed monthly
500 GB of stored screenshots
Upgrade from the account's Billing page.
Need more than 500 GB? Write to hello@stateofpixel.com.
Price it for your suite.
Screenshots a month360,000
$16,800 a year less than the cheapest of them.
Screenshots are stories x viewports x browsers x builds. For stateofpixel, 5% of screenshots change per build at 80 KB each, and pull request images are kept 60 days. The cheapest list plan for each tool, before tax, from Argos pricing, Chromatic pricing, Percy pricing on 27 September 2026. Argos charges $0.0015 for extra Storybook screenshots. TurboSnap can lower Chromatic's count. Percy prices are month to month; yearly billing is lower.
What leaves your CI.
- Source codeNever sent, and never run on our side.
- Tests and browsersScreenshots render in your own CI, so they match what your tests see.
- SecretsOn GitHub Actions the CLI signs in with OIDC, so there is no secret to store.
- Stores each image oncePer account, keyed by its SHA-256 hash.
- Signs private image linksThey expire after one to two hours.
- Follows GitHub accessPrivate builds show only to people who can read the repository.
- Sets the checkThe commit status links to the review page.
Outages and billing
Talk to VaibhavA 30 minute call, for anything this page does not answer.
Book a callOther questions
Coming from another tool?
Start free.
Pay when you pass 10 GB.
- No card to start
- No charge for screenshots, builds or reviewers
- One upload step in your workflow
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
id-token: write
jobs:
visual:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- run: npx playwright test
- run: npx stateofpixel upload screenshots