Guides

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-pixel on 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.

lostpixel.config.ts
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,
};
frontguard.config.ts
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 PixelFrontguardNotes
pageShots.baseUrlbaseUrl✅ Direct
pageShots.pages[].pathroutes[].path (or string)✅ Direct
pageShots.pages[].name— (derived from path)Frontguard names by route automatically
thresholdthreshold✅ Direct (0.0–1.0 fraction)
breakpointsviewports✅ Direct (widths)
waitBeforeScreenshotsmartRender (+ waitForSelector)✨ Improved
lostPixelProjectId / apiKeyNot needed — CLI is self-contained
mask / elementShots maskingignore: [{ selector }]✅ Maps

pageShots → routes

pageShots is the closest analog to Frontguard's core model. Each page becomes a route.

lostpixel.config.ts
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' }] },
    ],
  },
};
frontguard.config.ts
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).
tests/visual.spec.ts — replaces customShots
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).

lostpixel.config.ts
export const config = {
  storybookShots: {
    storybookUrl: './storybook-static',
  },
  threshold: 0.1,
};
frontguard.config.ts
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 PixelFrontguard
OSS status🔴 Read-only (acqui-hired by Figma 2026-04-22)🟢 Active, MIT
Core engineCLI + hosted platform (winding down)CLI-first (free forever)
Hosted reviewLost Pixel Platform (end-of-life)Optional cloud (CLI works fully without it)
Baselineslocal / platformgit orphan branch (auto)
RenderingPlaywrightPlaywright-native + anti-flake consensus
AI classification❌ Never had it🟢 OpenAI / Anthropic (BYOK)
Self-hostablepartial🟢 Fully
Account required to runplatform features❌ None
Active maintainers0 (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.

lostpixel.config.ts
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,
};
frontguard.config.ts
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

  1. Install Frontguard, remove Lost Pixel.
    npm uninstall lost-pixel
    npm install -D @frontguard/cli
    npx -p @frontguard/cli frontguard init
  2. Map pageShots.pagesroutes (path stays, drop name).
  3. Map breakpointsviewports (1:1).
  4. Map thresholdthreshold (1:1).
  5. Replace waitBeforeScreenshot with smartRender: true (+ waitForSelector if needed).
  6. Move maskignore: [{ selector }].
  7. Port customShots to routes or the Playwright plugin for interactive flows.
  8. Point baseUrl at a served Storybook for storybookShots and list story iframe URLs.
  9. Drop lostPixelProjectId / apiKey — not needed.
  10. Capture baselines, run, approve.
    npx -p @frontguard/cli frontguard baseline
    npx -p @frontguard/cli frontguard run
  11. (Optional) Enable AI — set FRONTGUARD_OPENAI_KEY.

Next Steps

On this page