Visual Regression Testing with an API
You ship a CSS change and everything looks fine on the page you edited. But three pages away, a sidebar is now overlapping the main content. A button lost its padding. The footer shifted up by 12 pixels. Nobody notices until a customer screenshots the bug and posts it on Twitter.
Visual regression testing catches these problems automatically by comparing screenshots before and after changes. It's one of those things that feels optional until it saves you from a very visible, very embarrassing bug in production.
How Visual Regression Testing Works
The concept is simple:
- Capture a baseline — screenshot your pages in a known-good state
- Make changes — deploy your code update
- Capture new screenshots — same pages, same viewport
- Diff the images — pixel-by-pixel comparison highlights what changed
- Review the diff — intentional changes pass, unintended changes get fixed
The challenge is in the details: handling dynamic content (timestamps, ads), dealing with subpixel rendering differences, running this fast enough for CI/CD, and not drowning in false positives.
The DIY Approach
You can build basic visual diffing with Puppeteer and pixelmatch:
const puppeteer = require('puppeteer');
const { PNG } = require('pngjs');
const pixelmatch = require('pixelmatch');
async function visualDiff(url1, url2) {
const browser = await puppeteer.launch({ headless: 'new' });
const shot1 = await takeScreenshot(browser, url1);
const shot2 = await takeScreenshot(browser, url2);
const img1 = PNG.sync.read(shot1);
const img2 = PNG.sync.read(shot2);
const diff = new PNG({ width: img1.width, height: img1.height });
const numDiffPixels = pixelmatch(
img1.data, img2.data, diff.data,
img1.width, img1.height,
{ threshold: 0.1 }
);
await browser.close();
const totalPixels = img1.width * img1.height;
return {
diffPercent: ((numDiffPixels / totalPixels) * 100).toFixed(2),
diffImage: PNG.sync.write(diff)
};
}
This works for a proof of concept. In production, you'll need to handle different page heights, wait for fonts and images to load, mask dynamic regions, resize images to match, manage baseline storage, and integrate with your CI system. That's a lot of infrastructure for what should be a simple check.
The API Approach: DiffShot
DiffShot handles all the complexity. Send two URLs (or a URL and a baseline image), get back a diff with pixel-level accuracy.
curl -X POST "https://diffshot.grabshot.dev/v1/diff" \
-H "X-Api-Key: ds_your_key" \
-H "Content-Type: application/json" \
-d '{
"url1": "https://your-site.com",
"url2": "https://staging.your-site.com"
}'
Response includes:
- Diff percentage — how much of the page changed
- Diff image — visual overlay highlighting every changed pixel
- Screenshots of both pages
- Changed regions — bounding boxes around areas that differ
CI/CD Integration
# In your deployment pipeline
- name: Visual regression check
run: |
# Compare production vs staging
DIFF=$(curl -s -X POST "https://diffshot.grabshot.dev/v1/diff" \
-H "X-Api-Key: ${{ secrets.DIFFSHOT_KEY }}" \
-H "Content-Type: application/json" \
-d "{\"url1\": \"https://your-site.com\", \"url2\": \"$STAGING_URL\"}")
PERCENT=$(echo "$DIFF" | jq -r '.diffPercent')
echo "Visual diff: ${PERCENT}%"
# Fail if more than 5% of pixels changed
if (( $(echo "$PERCENT > 5" | bc -l) )); then
echo "Visual regression detected!"
exit 1
fi
Monitoring Website Changes
DiffShot isn't just for your own sites. Monitor competitor pages, track design changes on sites you depend on, or watch for unauthorized changes to your production site:
// Daily check: has our production site changed unexpectedly?
const baseline = await fetch(
'https://diffshot.grabshot.dev/v1/screenshot?url=https://your-site.com',
{ headers: { 'X-Api-Key': 'ds_key' } }
);
// Store baseline, compare tomorrow
// Alert if diff > 0% when no deploy happened
Catch Visual Bugs Before Users Do
Compare any two URLs pixel-by-pixel. 25 free diffs per month.
Try DiffShot FreeWhen to Use Visual Regression Testing
Every CSS or layout change
CSS is global by nature. A change to a utility class can ripple across dozens of pages. Visual testing catches the ripples.
Dependency updates
Updating a UI library? Font package? Icon set? Run a visual diff before and after to catch unexpected changes.
Content changes
New images, longer text, different translations can all break layouts. Visual testing catches overflow, clipping, and alignment issues that unit tests miss.
Wrapping Up
Visual regression testing is the safety net between "it works on my machine" and "it works in production." The ROI is immediate: one caught bug pays for months of testing. Whether you build your own pipeline or use DiffShot, start testing visually. Your users are already doing it -- they're just not filing bug reports.
More from GrabShot
- Find Broken Links on Your Website (LinkCheck)
- Detect Website Fonts (FontSpy)
- Best Screenshot APIs in 2026 (GrabShot)
- Extract Colors from Any Website (ColorPeek)
- Monitor Cron Jobs in 2026 (CronPing)
📧 Developer API Tips
Get practical API tutorials and tools. No spam, unsubscribe anytime.