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.
#412Tighten pricing cards
Sign in/Error600
Changed1.51% / 3,393 px


Same review page as the app. Try the keys:j k movea approver reject1 2 3 4 viewsd diff overlay? all keys
Works with the tests you already have.
No token to copy on GitHub Actions. The first build on your default branch becomes the baseline.- 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.
The upload is one step in your CI, right after your tests. It hashes, sends what is new, compares and reports the check.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.
There are no invites and no seats. People can do what their role on the repository allows.- 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.
Pick a case to see the CI step and what the check shows.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/Form1280RemovedDid not run, so it shows as removed
- Checkout/Success1280RemovedDid 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/Plans1280ChangedApproved in build #409 by @octocat (carried over)
- Header/Default1280ChangedWaiting 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/Default1280ChangedApproved 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/Empty1280ChangedLooks 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.
No plan charges for snapshots, seats or builds. 10 GB is free, which covers most teams. The Usage tab shows what each project stores.Free
$0 /mo
No card needed. Enough for most teams.
- 10 GB of stored screenshots
- No charge for snapshots, builds or projects
- Everyone with write access can review
- Pull request images kept 60 days, adjustable from 7 to 365
25 GB
$15 /mo
Billed monthly
- 25 GB of stored screenshots
- Everything in Free
100 GB
$100 /mo
Billed monthly
- 100 GB of stored screenshots
- Everything in Free
500 GB
$500 /mo
Billed monthly
- 500 GB of stored screenshots
- Everything in Free
Need more than 500 GB? Write to hello@stateofpixel.com.
Price it for your suite.
Move the sliders. Every tool here bills for this many screenshots a month, except stateofpixel.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.
stateofpixel never receives your source code. It stores the screenshots your CI uploads, and access to them follows your permissions on GitHub.- 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.
The screenshots your tests wrote, up to 4 MB each. An unchanged screenshot costs one hash, not an upload.
The file path becomes the snapshot name. Each upload is checked against its hash.
The CLI diffs changed snapshots on your machine before the upload, with odiff or pixelmatch.
Metadata your tests attach, up to 4 KB per snapshot. It is shown on the build page and never changes how snapshots match.
Commit, commit message, branch, base branch, pull request number and up to 100 ancestor commits, to find the baseline.
A link from the build back to the CI run.
- 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
Commit statuses write, to set the check. Pull requests read and write, where read finds the PR number, base branch and squash merges, and write is not used for now. Contents read, which GitHub requires for the compare API we use to find the baseline commit. Metadata read, which every app has. Email addresses read, so you can sign in with GitHub. The app also asks for Checks write and Actions read, which it does not use for now.
Anyone with write access to the repository on GitHub. There are no seats, so the whole team can review.
Anything that writes PNG files: Playwright, Cypress, BackstopJS or native app screenshots. The CLI captures Storybook stories on its own. The file path becomes the snapshot name.
Screenshots render in your own CI, so they match what your tests see. The CLI ignores anti-aliasing by default and has a color threshold. A snapshot that flips back and forth on main is marked as looking flaky, with a link to the fixes. For stable renders, run capture in the Playwright Docker image and disable animations.
No. stateofpixel is GitHub only. Other CI providers work with a project token, as long as the repository is on GitHub.
Yes. The CLI, the server and the web app are MIT licensed, with their source at github.com/VaibhavAcharya/stateofpixel.
10 GB of stored screenshots. Snapshots, builds, projects and reviewers are not charged. No card needed. Pull request images are kept 60 days by default.
Cancel at the next billing date and the plan stays until the end of the billing period. Then the account moves to the Free plan with 10 GB of storage.
Coming from another tool?
Switch in one pull request. Your first build on the default branch becomes the baseline. The moving guide has the details.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