Compare / Lost Pixel
Moving from Lost Pixel to Playwright and Screenery
The Lost Pixel team announced on 22 April 2026 that they are joining Figma and sunsetting the product. The repository was archived the same day, and the announcement names no migration path. This guide moves each kind of Lost Pixel shot to Playwright and publishes the result with Screenery.
Where Lost Pixel stands, checked October 2026
- The GitHub repository is archived and read-only. Its last push was on 22 April 2026.
- The lost-pixel package still had about 35,500 npm downloads in the week of 4 to 10 October 2026. The engine still runs, but it gets no fixes.
- The announcement gives no shutdown date and no export for the hosted Platform.
What Lost Pixel did well
Lost Pixel tested several kinds of UI in one run, configured in lostpixel.config.ts:
- storybookShots, ladleShots and histoireShots: every story in a built Storybook, Ladle or Histoire.
- pageShots: a list of paths on a running app.
- customShots: a folder of images your own Playwright or Cypress tests wrote.
In open-source mode it compared .lostpixel/current with a committed .lostpixel/baseline. The hosted Platform added a review screen and approvals. It was free for open source and easy to start with, and it is a loss.
Side by side
| Lost Pixel | Playwright + Screenery | |
|---|---|---|
| Stories | Built in for Storybook, Ladle, Histoire | No Storybook integration. A Playwright test can visit each story |
| Pages | pageShots in the config | A Playwright test |
| Custom shots | A folder of images | A folder of images, pushed as it is |
| Baseline | .lostpixel/baseline, or the Platform | A committed folder, or the version each push replaces on Screenery |
| CI auth | Platform API key | GitHub OIDC, no stored secret. Other CI: SCREENERY_TOKEN |
| Published URLs | None | @latest follows the default branch |
| Status | Archived April 2026 | Active |
Step 1: publish what you have
If you still run the Lost Pixel engine in open-source mode, its output is a folder of PNGs. Check whether Screenery accepts the names with a dry run, which contacts nobody:
npx screenery push ./.lostpixel/current --project acme/dashboard --dry-run \ --commit-timestamp 2026-10-01T00:00:00Z
Story shots are named in kebab case, such as example-button--primary, and usually pass. Shots with breakpoints, or with a browser name, can get a label such as __[w320px]. Brackets are not allowed in Screenery names, so the dry run names those files and a real push would refuse them. Capital letters in a page shot's name fail too. See Names.
If the dry run passes, add the Action after the Lost Pixel step. Lost Pixel stays your gate for now, and Screenery publishes what it accepted:
permissions:
id-token: write
contents: read
pull-requests: write
steps:
# … check out, install, build, run Lost Pixel …
- uses: screenery/push@v1
with:
project: acme/dashboard
path: .lostpixel/current
This is a bridge. The steps below remove the archived engine.
Step 2: custom shots
customShots pointed Lost Pixel at a folder your tests already filled. Point Screenery at the same folder and drop that block from the config:
- uses: screenery/push@v1
with:
project: acme/dashboard
path: screenshots
If those are Playwright goldens from toHaveScreenshot, read Playwright goldens and Argos folders first.
Step 3: page shots
A pageShots list becomes a loop in a Playwright test. Set baseURL in playwright.config.ts to the baseUrl you gave Lost Pixel.
// tests/pages.spec.ts
import { test } from '@playwright/test';
const pages = [
{ path: '/', name: 'home' },
{ path: '/pricing', name: 'pricing' },
];
for (const { path, name } of pages) {
for (const width of [320, 1280]) {
test(`${name} at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 800 });
await page.goto(path);
await page.screenshot({ path: `test-results/${name}/w${width}.png`, fullPage: true });
});
}
}
Lost Pixel's breakpoints become the width loop, and the width goes last in the path, so home/w320 and home/w1280 sit together as one screen. waitForSelector becomes await page.locator(selector).waitFor(), and mask becomes the mask option of page.screenshot().
Step 4: stories
Screenery has no Storybook, Ladle or Histoire integration. If stories are most of what you test, look at Chromatic or Argos, which both have one.
If you only need a few stories published, plain Playwright can visit them. A built Storybook lists its stories in index.json, and each one renders alone at iframe.html?id=…:
// tests/stories.spec.ts
import { readFileSync } from 'node:fs';
import { test } from '@playwright/test';
const index = JSON.parse(readFileSync('storybook-static/index.json', 'utf8'));
const stories = Object.values(index.entries).filter((entry: any) => entry.type === 'story');
for (const story of stories as Array<{ id: string }>) {
test(story.id, async ({ page }) => {
await page.goto(`http://localhost:6006/iframe.html?id=${story.id}&viewMode=story`);
await page.screenshot({ path: `test-results/stories/${story.id}.png` });
});
}
Serve storybook-static on port 6006 first, for example with Playwright's webServer option. Story ids are already lowercase, so the names pass.
Step 5: the gate
Lost Pixel's failOnDifference becomes Screenery's diff gate. Create a baseline from your current screenshots, on the same image CI uses, and commit it:
npx playwright test npx screenery diff ./test-results --baseline ./screenery-baseline --update git add screenery-baseline
Then point the Action at it. A visual change fails the job, and nothing is published:
- uses: screenery/push@v1
with:
project: acme/dashboard
path: test-results
diff-baseline: ./screenery-baseline
If you would rather not commit images, set failAbove in screenery.config.json. Each push is then graded against the version it replaces on Screenery. See Visual testing. When everything runs, remove lost-pixel and lostpixel.config.ts.
You also gain something Lost Pixel never had: each screenshot's @latest URL follows your default branch, so it can go straight into a README or docs page. See Getting started.
Other destinations
Screenery is not the only answer. Argos is open source and close to Lost Pixel's model, with a review app. Chromatic is strongest for Storybook. Percy renders in several browsers. See all comparisons.
Sources
Checked October 2026.