Menu
visual-regressiontestingci-cdscreenshot-diffautomation

Visual Regression Testing with a Screenshot Diff API

SnapSharp Team·April 14, 2026·7 min read

Visual regression testing answers one question: did the UI change between two points in time, and if so, where?

It is not about functionality. Unit tests and E2E tests tell you whether code works. Visual regression tells you whether it looks the same after you merged that "minor refactor" that silently changed the spacing on the checkout page.

The failure mode is specific: a developer changes a global CSS variable, CI goes green, the deploy lands, and three pages now have the wrong button padding. No test caught it because no test checked pixels.

This guide covers how to build a reliable visual regression workflow using the SnapSharp /v1/diff endpoint — pixel-level screenshot comparison via API, without standing up Playwright clusters or paying $400+ per month for a dedicated visual testing service.

How the SnapSharp Diff API works

The /v1/diff endpoint accepts two URLs or two base64-encoded images, takes screenshots of both (or compares the images directly), and returns a pixel-level diff.

Request — compare two URLs:

curl -X POST https://api.snapsharp.dev/v1/diff \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url_a": "https://example.com",
    "url_b": "https://staging.example.com",
    "width": 1440,
    "threshold": 0.05
  }'

Response:

{
  "diff_percent": 3.21,
  "diff_pixels": 29594,
  "total_pixels": 921600,
  "width": 1440,
  "height": 900,
  "threshold": 0.05,
  "diff_image_base64": "iVBORw0KGgo..."
}
FieldDescription
diff_percentPercentage of pixels that differ between the two screenshots
diff_pixelsAbsolute count of changed pixels
total_pixelsTotal pixels in the comparison (width × height)
diff_image_base64PNG with changed pixels highlighted in red — save this as an artifact

The threshold parameter controls color distance sensitivity (0–1). 0.1 is the default and works for most cases. Drop it to 0.05 for stricter comparison, raise it to 0.2 to tolerate anti-aliasing differences.

Full parameter reference: snapsharp.dev/docs/diff

Node.js example

Using @snapsharp/sdk:

import Snapsharp from "@snapsharp/sdk";
import fs from "fs";

const client = new Snapsharp({ apiKey: process.env.SNAPSHARP_API_KEY });

async function comparePages(urlA: string, urlB: string): Promise<void> {
  const result = await client.diff({
    url_a: urlA,
    url_b: urlB,
    width: 1440,
    threshold: 0.05,
  });

  console.log(`Diff: ${result.diff_percent.toFixed(2)}% (${result.diff_pixels} pixels)`);

  if (result.diff_image_base64) {
    const buffer = Buffer.from(result.diff_image_base64, "base64");
    fs.writeFileSync("diff-output.png", buffer);
    console.log("Diff image saved to diff-output.png");
  }

  // Fail the check if more than 1% of pixels changed
  if (result.diff_percent > 1.0) {
    console.error(`Visual regression detected: ${result.diff_percent.toFixed(2)}% changed`);
    process.exit(1);
  }
}

comparePages(
  "https://example.com",
  "https://staging.example.com"
);

Using fetch directly if you prefer no SDK dependency:

const response = await fetch("https://api.snapsharp.dev/v1/diff", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SNAPSHARP_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url_a: "https://example.com",
    url_b: "https://staging.example.com",
    width: 1440,
    threshold: 0.05,
  }),
});

const data = await response.json();

if (data.diff_percent > 1.0) {
  throw new Error(`Visual regression: ${data.diff_percent}% pixels changed`);
}

GitHub Actions workflow

This is the most practical integration point. Compare your production URL against the staging deployment on every pull request:

name: Visual Regression

on:
  pull_request:
    branches: [main]

jobs:
  visual-diff:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Run visual diff
        env:
          SNAPSHARP_API_KEY: ${{ secrets.SNAPSHARP_API_KEY }}
          STAGING_URL: ${{ vars.STAGING_URL }}
          PROD_URL: ${{ vars.PROD_URL }}
        run: |
          node -e "
          const run = async () => {
            const res = await fetch('https://api.snapsharp.dev/v1/diff', {
              method: 'POST',
              headers: {
                'Authorization': \`Bearer \${process.env.SNAPSHARP_API_KEY}\`,
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                url_a: process.env.PROD_URL,
                url_b: process.env.STAGING_URL,
                width: 1440,
                threshold: 0.05
              })
            });
            const data = await res.json();
            console.log(\`Diff: \${data.diff_percent.toFixed(2)}%\`);
            const fs = await import('fs');
            if (data.diff_image_base64) {
              fs.writeFileSync('diff.png', Buffer.from(data.diff_image_base64, 'base64'));
            }
            if (data.diff_percent > 1.0) {
              console.error('Visual regression detected');
              process.exit(1);
            }
          };
          run().catch(e => { console.error(e); process.exit(1); });
          "

      - name: Upload diff artifact
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: visual-diff
          path: diff.png

The diff image uploads as a workflow artifact regardless of pass/fail. Reviewers can download it directly from the Actions run to see exactly which pixels changed.

Comparing pre-captured images

If you already have screenshots from a previous run (stored as CI artifacts, in S3, or on disk), you can compare them directly without hitting the URLs again. This is useful when the staging environment is torn down after a deploy:

# Encode both images to base64
IMAGE_A=$(base64 -i before.png)
IMAGE_B=$(base64 -i after.png)

curl -X POST https://api.snapsharp.dev/v1/diff \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"image_a\": \"$IMAGE_A\",
    \"image_b\": \"$IMAGE_B\",
    \"threshold\": 0.1
  }" | jq '.diff_percent'

Use cases beyond deployment checks

Before/after deployment check — compare production before and after a deploy. If diff_percent exceeds your threshold, roll back automatically or alert the team.

Cross-browser comparison — take screenshots via two different user_agent values or device presets, then diff them. Catch layout bugs that only appear in specific browsers without running a full cross-browser test suite.

Dark mode regression — the same page rendered with and without the dark_mode: true parameter. Verify that dark mode styles are applied correctly after a theme refactor.

const [light, dark] = await Promise.all([
  fetch("https://api.snapsharp.dev/v1/screenshot", {
    method: "POST",
    headers: { "Authorization": `Bearer ${process.env.SNAPSHARP_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ url: "https://example.com", format: "png", width: 1440 }),
  }),
  fetch("https://api.snapsharp.dev/v1/screenshot", {
    method: "POST",
    headers: { "Authorization": `Bearer ${process.env.SNAPSHARP_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ url: "https://example.com", format: "png", width: 1440, dark_mode: true }),
  }),
]);

// Then diff the two base64 responses via /v1/diff with image_a + image_b

A/B test visual diff — quantify exactly how different two variants are. diff_percent and diff_pixels give you a concrete number to include in experiment reports. A layout change that shifts 30% of pixels is a very different experiment than one that changes 2%.

SnapSharp vs Percy and Chromatic

Percy and Chromatic are purpose-built visual regression platforms with excellent GitHub integrations, approval workflows, and team features. They are the right tool for large frontend teams with a designer-in-the-loop review process.

The cost is significant. Percy's Team plan starts at around $400/month for unlimited snapshots. Chromatic charges per snapshot, and a typical SaaS product with 50 routes and 3 viewports will accumulate costs fast.

SnapSharp is a different trade-off: you own the diff logic, you own the threshold, you own where results are stored. The /v1/diff endpoint returns a number and an image. What you do with them is up to you — fail the CI check, post to Slack, write to a database, upload to S3.

The SnapSharp Growth plan at $49/month includes 25,000 API requests per month and access to the diff endpoint. For teams running visual checks on every pull request, that covers a large number of comparisons without per-snapshot pricing surprises.

The trade-off: you do not get Percy's review UI or Chromatic's snapshot history browser. You build those integrations yourself, or you use the SnapSharp monitoring dashboard for scheduled visual diff checks. If you need a full approval workflow, Percy is the better fit. If you need a reliable diff API you can wire into any pipeline, SnapSharp works.

What the diff endpoint requires

The /v1/diff endpoint requires the Growth plan or higher. It is not available on Free or Starter. You can verify your current plan and request count on the usage dashboard.

Parameters summary:

ParameterTypeRequiredDefault
url_astringOne pair required
url_bstringOne pair required
image_astring (base64)One pair required
image_bstring (base64)One pair required
thresholdnumber (0–1)No0.1
widthnumberNo1280
heightnumberNo720

You must provide either url_a + url_b or image_a + image_b. Mixing the two is not supported.

Summary

Visual regression testing does not require a $400/month platform to be useful. The core primitive is simple: take two screenshots, compare pixels, get a percentage and a diff image. Build that into your CI pipeline and you catch a large class of unintentional UI changes before they reach users.

The SnapSharp /v1/diff endpoint gives you that primitive as an API call. Integrate it wherever you already run tests — GitHub Actions, GitLab CI, a pre-deploy script, or a scheduled job that compares your marketing pages week over week.

Read the diff API docs to see the full parameter reference, or create a free account and run your first comparison in under five minutes.

Visual Regression Testing with a Screenshot Diff API — SnapSharp Blog