Percy alternatives
Visual regression testing without the SaaS dependency.
What Percy does
Percy integrates into your CI pipeline and captures screenshots of your application on every pull request. It compares each screenshot against an approved baseline and highlights pixel-level differences in a web UI where reviewers approve or reject changes. When changes are accepted, the baseline advances.
Percy supports Storybook, Playwright, Cypress, and custom SDK integrations. It manages browser infrastructure, baseline storage, and the review workflow. For teams that need a polished managed experience, it removes significant setup work at the cost of a SaaS dependency on BrowserStack infrastructure.
Where Percy falls short
- Usage-based pricing — Percy offers a free tier of 5,000 screenshots/month; usage above that scales in cost. For active CI pipelines with many PRs and pages, costs accumulate quickly.
- SaaS dependency — screenshots are sent to BrowserStack servers for comparison and storage. If Percy is unavailable, your CI pipeline stalls.
- No self-hosting — there is no option to run Percy on your own infrastructure.
- Framework coupling— Percy's SDKs target specific testing frameworks. Capturing arbitrary URLs or HTML templates outside of those frameworks requires extra integration work.
The self-hosted alternative
Visual regression testing has two steps: capture and compare. Percy bundles both into a managed service. You can build the same pipeline from open-source components at zero cost:
- Capture:
@openkova/cli— screenshots any URL or HTML file using headless Chromium, no API key required - Compare:
pixelmatch(JavaScript) orodiff(Rust, 40–100× faster for large images) — pixel-level comparison with configurable thresholds - Storage: git LFS, S3, or any object store for baseline images
- Review: GitHub Actions annotations, uploaded PR artifacts, or a static HTML diff report generated from the comparison output
Comparison
| Feature | Percy | Openkova + pixelmatch |
|---|---|---|
| Screenshot capture | ✓ managed | ✓ self-hosted |
| Pixel comparison | ✓ managed | ✓ pixelmatch / odiff |
| Baseline management | ✓ Percy UI | ✓ git / S3 |
| CI integration | ✓ SDK + Percy CI | ✓ kova CLI in any CI |
| Storybook support | ✓ first-class | ✓ via kova screenshot |
| Review UI | ✓ Percy web app | ∼ GitHub artifacts / PR comments |
| Data leaves your servers | ✓ | ✗ |
| Self-hostable | ✗ | ✓ |
| Pricing | Free (5k/mo), then usage-based | Free |
| License | Proprietary | MIT |
A minimal CI setup
Here is a GitHub Actions workflow that captures screenshots of your staging app, compares them against stored baselines, and fails the build if pages changed beyond a pixel threshold:
# .github/workflows/visual-regression.yml
name: Visual Regression
on: [pull_request]
jobs:
screenshot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Chromium
run: sudo apt-get install -y chromium-browser
- name: Install kova
run: npm install -g @openkova/cli
- name: Capture screenshots
run: |
kova screenshot ${{ steps.deploy.outputs.url }} \
--full-page --output screenshots/home.png
kova screenshot ${{ steps.deploy.outputs.url }}/about \
--full-page --output screenshots/about.png
env:
CHROMIUM_PATH: /usr/bin/chromium-browser
- name: Compare against baselines
run: node scripts/compare.js
- name: Upload diff artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: visual-diffs
path: diffs/The comparison script uses pixelmatch to flag changes above a threshold:
// scripts/compare.js
const { PNG } = require('pngjs');
const pixelmatch = require('pixelmatch');
const fs = require('fs');
const pages = ['home', 'about'];
let failed = false;
for (const page of pages) {
const baseline = PNG.sync.read(fs.readFileSync(`baselines/${page}.png`));
const current = PNG.sync.read(fs.readFileSync(`screenshots/${page}.png`));
const { width, height } = baseline;
const diff = new PNG({ width, height });
const changed = pixelmatch(
baseline.data, current.data, diff.data,
width, height,
{ threshold: 0.1 }
);
const pct = (changed / (width * height) * 100).toFixed(2);
console.log(`${page}: ${changed} pixels changed (${pct}%)`);
if (changed > 100) {
fs.mkdirSync('diffs', { recursive: true });
fs.writeFileSync(`diffs/${page}.png`, PNG.sync.write(diff));
failed = true;
}
}
if (failed) process.exit(1);Frequently asked questions
Is there a free alternative to Percy for visual regression testing?
Yes. Use @openkova/cli for screenshot capture and pixelmatch or odifffor pixel comparison. Both are MIT-licensed and free. The main trade-off is you manage the baseline storage and review workflow yourself instead of using Percy's managed UI.
What is Percy used for?
Percy captures screenshots of your app in CI, compares them against an approved baseline, and highlights pixel-level differences. Developers use it to catch unintended visual changes before they merge into the main branch.
Can I run visual regression testing locally without a SaaS tool?
Yes. kova screenshot runs on your machine using your local Chrome. Run it before and after a code change, then compare the PNGs with pixelmatch to see exactly what shifted.
Does Openkova integrate with GitHub Actions?
Yes. Install chromium-browser and @openkova/cli in your runner, then call kova screenshot or kova crawl as a build step. Upload PNGs as artifacts or diff them against a stored baseline.
Read the full free setup guide: Visual Regression Testing Without Percy — or see how to screenshot any page in CI/CD with @openkova/cli.