392 words
2 minutes
Use Playwright to Monitor Web Application

Playwright can be used to automate visual monitoring by capturing screenshots of key pages on a schedule.

A practical workflow is:

  1. Capture baseline screenshots for important pages and user flows
  2. Capture fresh screenshots in each monitoring run
  3. Compare pixel differences between baseline and current images
  4. Trigger an alert when the difference exceeds a configured threshold

This approach helps detect unintended UI regressions quickly and provides image evidence to speed up triage. Code:

import { chromium } from "playwright";
import pixelmatch from "pixelmatch";
import { PNG } from "pngjs";
import fs from "fs";
import path from "path";
const BASELINE_DIR = "./screenshots/baseline";
const DIFF_DIR = "./screenshots/diff";
const THRESHOLD = 0.03; // Alert when pixel difference exceeds 3%
interface CheckResult {
passed: boolean;
diffPercent: number;
diffImagePath: string;
}
async function captureScreenshot(url: string, name: string): Promise<Buffer> {
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
// Wait for network idle so async content has finished loading
await page.goto(url, { waitUntil: "networkidle" });
// Wait an extra 2 seconds so animations can finish
await page.waitForTimeout(2000);
const screenshot = await page.screenshot({ fullPage: false });
await browser.close();
return screenshot;
}
function compareScreenshots(
current: Buffer,
name: string
): CheckResult {
const currentPng = PNG.sync.read(current);
const baselinePath = path.join(BASELINE_DIR, `${name}.png`);
const diffPath = path.join(DIFF_DIR, `${name}.png`);
if (!fs.existsSync(baselinePath)) {
// First run: save as baseline image
fs.writeFileSync(baselinePath, current);
console.log(`[${name}] Baseline image created`);
return { passed: true, diffPercent: 0, diffImagePath: "" };
}
const baselinePng = PNG.sync.read(fs.readFileSync(baselinePath));
const { width, height } = currentPng;
const diff = new PNG({ width, height });
const diffPixels = pixelmatch(
baselinePng.data,
currentPng.data,
diff.data,
width,
height,
{ threshold: 0.1 }
);
const diffPercent = diffPixels / (width * height);
if (diffPercent > THRESHOLD) {
fs.mkdirSync(DIFF_DIR, { recursive: true });
fs.writeFileSync(diffPath, PNG.sync.write(diff));
return { passed: false, diffPercent, diffImagePath: diffPath };
}
return { passed: true, diffPercent, diffImagePath: "" };
}
async function patrol(pages: Array<{ url: string; name: string }>) {
for (const { url, name } of pages) {
const screenshot = await captureScreenshot(url, name);
const result = compareScreenshots(screenshot, name);
if (!result.passed) {
sendAlert({
page: name,
diffPercent: (result.diffPercent * 100).toFixed(2),
diffImage: result.diffImagePath,
});
// Keep a current screenshot when an issue is detected
fs.writeFileSync(
path.join(DIFF_DIR, `${name}_current.png`),
screenshot
);
}
}
}
function sendAlert(data: Record<string, string>) {
// Replace with your own notification channel: Slack, Teams, or webhook
console.log(`⚠️ Page anomaly alert:`, JSON.stringify(data, null, 2));
}
Use Playwright to Monitor Web Application
https://astro-nyc.pages.dev/posts/use-playwright-to-monitor-web-application/
Author
Hari Seldon
Published at
2026-07-28
License
CC BY-NC-SA 4.0