Migrate from Lost Pixel
A Lost Pixel migration guide and Lost Pixel alternative. Lost Pixel is archived — move to Frontguard for a free-forever CLI, Playwright rendering, and AI visual classification.
Migrate from Lost Pixel
Lost Pixel was a popular open-source visual regression tool (~43K downloads/week at its peak) with a hosted "Lost Pixel Platform" for review.
Sunset reality: On 2026-04-22 the Lost Pixel team was acqui-hired by Figma. The open-source repository was switched to read-only the same day — the issue tracker is closed, the maintainers have left, and no new releases will ship. The hosted Lost Pixel Platform is in a wind-down period and is no longer accepting new accounts. If your team is on Lost Pixel today, you are running unmaintained software; the safe move is to migrate to a tool that's still being maintained.
Frontguard is a Lost Pixel alternative built for the same workflows — page shots, custom shots, and Storybook stories — but with a free-forever CLI, Playwright-native rendering, git-orphan-branch baselines, anti-flake multi-render consensus, and AI vision classification that Lost Pixel never had. MIT-licensed and self-hostable.
What "Read-Only" Actually Means for You
- No new releases — the last published version of
lost-pixelon npm is the final one. Future Playwright / Node / Storybook releases that break it will not be patched. - No security fixes. If a CVE lands in a transitive dependency, you're on your own to fork-and-patch or vendor it.
- No issue triage. The GitHub issue tracker is closed; bug reports are not being read.
- Pull requests are blocked. Even your own fix can't be merged upstream.
- Hosted platform sunsetting. If you depend on the Lost Pixel Platform for review UX, plan a migration window — accounts created before the acqui-hire continue to work during the wind-down, but the platform is end-of-life.
- No telemetry/uptime guarantee on the hosted dashboard. Treat it as a "best effort while it lasts" service.
The "code still works today" trap is real — Lost Pixel will keep rendering screenshots for some months — but every dependency upgrade you make to the rest of your stack moves you closer to a breakage you can't fix.
Config Mapping: lostpixel.config.ts → frontguard.config.ts
Both tools use a TypeScript config. Lost Pixel splits capture into pageShots, customShots, and storybookShots modes; Frontguard unifies everything under baseUrl + routes.
import { CustomProjectConfig } from 'lost-pixel';
export const config: CustomProjectConfig = {
pageShots: {
pages: [
{ path: '/', name: 'home' },
{ path: '/pricing', name: 'pricing' },
],
baseUrl: 'http://localhost:3000',
},
threshold: 0.1,
breakpoints: [375, 768, 1440],
lostPixelProjectId: 'abc123',
apiKey: process.env.LOST_PIXEL_API_KEY,
};export default {
baseUrl: 'http://localhost:3000',
routes: ['/', '/pricing'],
viewports: [375, 768, 1440],
browsers: ['chromium'],
threshold: 0.1,
smartRender: true,
ai: {
provider: 'openai',
model: 'gpt-4o',
},
};Notice the lostPixelProjectId and apiKey are gone — Frontguard's CLI runs entirely locally/in your CI with git-orphan-branch baselines. No platform account required.
Field-by-field mapping
| Lost Pixel | Frontguard | Notes |
|---|---|---|
pageShots.baseUrl | baseUrl | ✅ Direct |
pageShots.pages[].path | routes[].path (or string) | ✅ Direct |
pageShots.pages[].name | — (derived from path) | Frontguard names by route automatically |
threshold | threshold | ✅ Direct (0.0–1.0 fraction) |
breakpoints | viewports | ✅ Direct (widths) |
waitBeforeScreenshot | smartRender (+ waitForSelector) | ✨ Improved |
lostPixelProjectId / apiKey | — | Not needed — CLI is self-contained |
mask / elementShots masking | ignore: [{ selector }] | ✅ Maps |
pageShots → routes
pageShots is the closest analog to Frontguard's core model. Each page becomes a route.
export const config = {
pageShots: {
baseUrl: 'http://localhost:3000',
pages: [
{ path: '/', name: 'home' },
{ path: '/blog', name: 'blog', threshold: 0.05 },
{ path: '/dash', name: 'dash', mask: [{ selector: '.clock' }] },
],
},
};export default {
baseUrl: 'http://localhost:3000',
routes: [
'/',
{ path: '/blog', threshold: 0.05 },
{ path: '/dash', ignore: [{ selector: '.clock', description: 'Live clock' }] },
],
};customShots → routes (or Playwright plugin)
Lost Pixel's customShots pointed at a directory of pre-captured images or custom-rendered shots. In Frontguard you have two options:
- Simple cases: add the URL as another route.
- Interaction-heavy cases (logged-in views, hovers, multi-step flows): use the Playwright plugin, which lets you drive the page with full Playwright APIs and call
visualTest(page, name).
import { test } from '@playwright/test';
import { visualTest } from '@frontguard/playwright';
test('checkout — logged in, coupon applied', async ({ page }) => {
await page.goto('/checkout');
await page.getByRole('button', { name: 'Apply coupon' }).click();
await visualTest(page, 'checkout-coupon-applied', { ai: true });
});storybookShots → Storybook routes
Lost Pixel's storybookShots crawled a built Storybook. With Frontguard, point baseUrl at your running/static Storybook and list the iframe story URLs as routes (or auto-discover them).
export const config = {
storybookShots: {
storybookUrl: './storybook-static',
},
threshold: 0.1,
};export default {
// serve storybook-static (e.g. `npx http-server storybook-static -p 6006`)
baseUrl: 'http://localhost:6006',
threshold: 0.1,
smartRender: true,
// Auto-discover stories, or list iframe URLs explicitly:
routes: [
'/iframe.html?id=button--primary&viewMode=story',
'/iframe.html?id=button--secondary&viewMode=story',
'/iframe.html?id=card--default&viewMode=story',
],
};Story IDs come from your Storybook stories.json / index.json. You can script-generate the routes array from that file the same way Lost Pixel enumerated stories internally.
Platform Comparison
The biggest reason to migrate: Lost Pixel's hosted platform and OSS repo are no longer dependable. Frontguard's value lives in a CLI that's free forever — the optional cloud is additive, not required.
| Lost Pixel | Frontguard | |
|---|---|---|
| OSS status | 🔴 Read-only (acqui-hired by Figma 2026-04-22) | 🟢 Active, MIT |
| Core engine | CLI + hosted platform (winding down) | CLI-first (free forever) |
| Hosted review | Lost Pixel Platform (end-of-life) | Optional cloud (CLI works fully without it) |
| Baselines | local / platform | git orphan branch (auto) |
| Rendering | Playwright | Playwright-native + anti-flake consensus |
| AI classification | ❌ Never had it | 🟢 OpenAI / Anthropic (BYOK) |
| Self-hostable | partial | 🟢 Fully |
| Account required to run | platform features | ❌ None |
| Active maintainers | 0 (post-Figma) | Active |
Honest note: Frontguard is a new project with no hosted review dashboard yet (the optional cloud is in progress). If your team's workflow depends entirely on a polished hosted review UI today, evaluate that gap before committing. The CLI workflow — baseline, run, approve — is fully functional.
AI Classification — the thing Lost Pixel never had
Lost Pixel reported pixel diffs. Frontguard sends the baseline + current screenshot to a vision model and returns a classification (regression / intentional / content_update), a severity (🔴/🟡/🟢), a plain-English explanation, and an optional suggested CSS fix.
✘ /dashboard @ 375px
AI: "Sidebar overlaps main content on mobile.
flex-direction change in Dashboard.module.css:28"
Severity: 🔴 Critical (confidence: 94%)This is the single biggest upgrade over Lost Pixel: it tells you whether a diff is a real bug, an intentional redesign, or just dynamic content — drastically cutting false-positive triage. See AI Analysis.
Working Migration Example
A realistic Lost Pixel config and its Frontguard translation, end to end.
import { CustomProjectConfig } from 'lost-pixel';
export const config: CustomProjectConfig = {
pageShots: {
baseUrl: 'http://localhost:3000',
pages: [
{ path: '/', name: 'home' },
{ path: '/pricing', name: 'pricing', threshold: 0.05 },
{ path: '/dashboard', name: 'dashboard', mask: [{ selector: '.live-clock' }] },
],
waitBeforeScreenshot: 1000,
},
breakpoints: [375, 1440],
threshold: 0.1,
lostPixelProjectId: 'proj_abc',
apiKey: process.env.LOST_PIXEL_API_KEY,
};export default {
baseUrl: 'http://localhost:3000',
viewports: [375, 1440],
browsers: ['chromium'],
threshold: 0.1,
smartRender: true, // replaces waitBeforeScreenshot
routes: [
'/',
{ path: '/pricing', threshold: 0.05 },
{
path: '/dashboard',
ignore: [{ selector: '.live-clock', description: 'Updates every second' }],
},
],
ai: { provider: 'openai', model: 'gpt-4o' }, // new capability
};Migration Checklist
- Install Frontguard, remove Lost Pixel.
npm uninstall lost-pixel npm install -D @frontguard/cli npx -p @frontguard/cli frontguard init - Map
pageShots.pages→routes(path stays, dropname). - Map
breakpoints→viewports(1:1). - Map
threshold→threshold(1:1). - Replace
waitBeforeScreenshotwithsmartRender: true(+waitForSelectorif needed). - Move
mask→ignore: [{ selector }]. - Port
customShotsto routes or the Playwright plugin for interactive flows. - Point
baseUrlat a served Storybook forstorybookShotsand list story iframe URLs. - Drop
lostPixelProjectId/apiKey— not needed. - Capture baselines, run, approve.
npx -p @frontguard/cli frontguard baseline npx -p @frontguard/cli frontguard run - (Optional) Enable AI — set
FRONTGUARD_OPENAI_KEY.
Next Steps
- AI Analysis — the classification Lost Pixel never had
- Playwright setup — for
customShots-style interactive flows - Configuration reference
Migrate from BackstopJS
A complete BackstopJS migration guide. Frontguard is a modern BackstopJS alternative and BackstopJS replacement with AI analysis, Playwright rendering, and zero-config baselines.
MCP server
Use Frontguard from inside your IDE. The @frontguard/mcp server exposes list_regressions, get_suggested_fix, accept_baseline, and recent_runs over the Model Context Protocol so Claude Code, Cursor, and Copilot can answer "what regressed on this PR?" without leaving the editor.