Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Playwright Java can capture the image; it does not provide the Java equivalent of Playwright Test’s toHaveScreenshot() matcher. A reliable Java workflow is therefore: capture a page or locator, load an approved baseline, compare the two images with a Java image-diff implementation, save diagnostics, and review baseline changes before accepting them.

The distinction matters because Playwright’s documented screenshot assertion runs in the JavaScript/TypeScript Playwright Test runner. The Java API documents screenshot capture and its options, but not a built-in visual assertion. The examples below keep those APIs separate and give you a complete, adaptable comparison flow.

What you need

  • A Java project with the Playwright Java library and its supported browser binaries installed.
  • A deterministic test environment: the same operating system image, browser/runtime version, viewport, device scale factor, headless setting, fonts and power state for baseline and comparison runs.
  • An approved reference image committed to your test resources or another controlled artifact store.
  • A comparison policy defined by your project. There is no universal pixel tolerance; choose one that matches your rendering stability and product risk.

Pin the Playwright Java version used by your build and check the matching API reference when upgrading. Screenshot format behavior changes over time; for example, Playwright Java release notes document WebP support for Page and Locator screenshots in version 1.62.

Capture a screenshot in Java

Page-level capture

Use a page screenshot when the test should detect changes across the complete rendered page. The result is a byte array, which you can write to disk or pass directly to an image comparator.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import com.microsoft.playwright.*;
import java.nio.file.*;

public class CapturePage {
  public static void main(String[] args) throws Exception {
    try (Playwright playwright = Playwright.create()) {
      Browser browser = playwright.chromium().launch(
          new BrowserType.LaunchOptions().setHeadless(true));
      Page page = browser.newPage(new Browser.NewPageOptions()
          .setViewportSize(1440, 900)
          .setDeviceScaleFactor(1));
      page.navigate("https://example.com");

      byte[] image = page.screenshot(new Page.ScreenshotOptions()
          .setFullPage(true)
          .setAnimations(ScreenshotAnimations.DISABLED)
          .setCaret(ScreenshotCaret.HIDDEN)
          .setPath(Paths.get("artifacts/actual.png")));
      browser.close();
    }
  }
}

setFullPage(true) captures the full scrollable page instead of only the viewport. Use a fixed viewport and scale factor so the baseline and actual image have the same dimensions.

Component or element capture

For a component test, prefer Locator.screenshot(). It scrolls the locator into view, performs actionability checks and clips the image to the element’s bounds. This prevents unrelated navigation or footer changes from failing a component comparison.

Locator card = page.locator("[data-testid='pricing-card']");
byte[] actual = card.screenshot(new Locator.ScreenshotOptions()
    .setAnimations(ScreenshotAnimations.DISABLED)
    .setCaret(ScreenshotCaret.HIDDEN)
    .setPath(Paths.get("artifacts/pricing-card.png")));

Playwright discourages the older ElementHandle.screenshot() API for this purpose; use a locator instead.

Make captures repeatable

Disable motion

Animations and transitions can produce different pixels depending on capture timing. Set setAnimations(ScreenshotAnimations.DISABLED) on every baseline and actual capture where motion is irrelevant.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Mask changing regions

Mask timestamps, rotating promotions, avatars, ads or other intentionally variable regions when they are outside the test’s purpose. Make the mask visible in code review: masking changes what the test covers.

Locator timestamp = page.locator("[data-testid='timestamp']");
Locator avatar = page.locator(".user-avatar");
page.screenshot(new Page.ScreenshotOptions()
    .setFullPage(true)
    .setAnimations(ScreenshotAnimations.DISABLED)
    .setMask(List.of(timestamp, avatar))
    .setMaskColor("#FF00FF")
    .setPath(Paths.get("artifacts/dashboard.png")));

Use the same mask list and mask color for the reference-generation and comparison runs.

Inject a stylesheet when appropriate

A screenshot stylesheet can hide volatile elements or freeze a visual state. Do not use it to conceal a regression that the test is meant to catch.

String freezeMotion = "* { animation: none !important; "
    + "transition: none !important; caret-color: transparent !important; }";
page.screenshot(new Page.ScreenshotOptions()
    .setStyleSheet(freezeMotion)
    .setAnimations(ScreenshotAnimations.DISABLED)
    .setPath(Paths.get("artifacts/stable.png")));

Other documented controls include timeout, format, quality, scale and clipping. Choose a lossless format for baselines and use exactly the same format on both sides. PNG is a straightforward default; WebP can be selected with a .webp path or explicit type, with quality 100 documented as lossless and lower quality as lossy.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Compare the actual image with a baseline

The following self-contained comparator demonstrates the process without claiming a Playwright matcher. It checks dimensions, counts pixels whose channel difference exceeds a chosen threshold, and writes a diagnostic diff image. Treat the threshold and allowed differing-pixel count as project policy, not universal recommendations.

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;

public final class ImageDiff {
  public static Result compare(File expectedFile, File actualFile,
                               int channelThreshold, int maxDifferentPixels,
                               File diffFile) throws Exception {
    BufferedImage expected = ImageIO.read(expectedFile);
    BufferedImage actual = ImageIO.read(actualFile);
    if (expected == null || actual == null) throw new IllegalArgumentException("Unreadable image");
    if (expected.getWidth() != actual.getWidth()
        || expected.getHeight() != actual.getHeight()) {
      throw new AssertionError("Image dimensions differ: expected "
          + expected.getWidth() + "x" + expected.getHeight() + ", actual "
          + actual.getWidth() + "x" + actual.getHeight());
    }
    BufferedImage diff = new BufferedImage(expected.getWidth(), expected.getHeight(),
        BufferedImage.TYPE_INT_ARGB);
    int different = 0;
    for (int y = 0; y < expected.getHeight(); y++) {
      for (int x = 0; x < expected.getWidth(); x++) {
        int a = expected.getRGB(x, y), b = actual.getRGB(x, y);
        int ar = (a >>> 16) & 255, ag = (a >>> 8) & 255, ab = a & 255;
        int br = (b >>> 16) & 255, bg = (b >>> 8) & 255, bb = b & 255;
        boolean changed = Math.max(Math.max(Math.abs(ar - br), Math.abs(ag - bg)),
                                   Math.abs(ab - bb)) > channelThreshold;
        diff.setRGB(x, y, changed ? 0xFFFF0000 : 0x00000000);
        if (changed) different++;
      }
    }
    ImageIO.write(diff, "png", diffFile);
    return new Result(different, different <= maxDifferentPixels);
  }
  public record Result(int differentPixels, boolean passed) {}
}

Call it after Playwright writes the actual image:

ImageDiff.Result result = ImageDiff.compare(
    new File("src/test/resources/baselines/home.png"),
    new File("artifacts/home-actual.png"),
    8,                 // project-defined per-channel threshold
    0,                 // project-defined differing-pixel allowance
    new File("artifacts/home-diff.png"));
if (!result.passed()) {
  throw new AssertionError("Visual mismatch: " + result.differentPixels()
      + " differing pixels; see artifacts/home-diff.png");
}

For production test suites, you may select a maintained Java image-diff library instead. Evaluate its maintenance, color-space behavior, alpha handling and diagnostic output yourself; the reviewed Playwright Java documentation does not establish a particular third-party comparator or a correct tolerance.

A complete baseline test pattern

  1. Navigate to the exact route and wait for the intended UI state, such as a locator becoming visible.
  2. Apply the same viewport, scale, animation, mask, stylesheet, timeout and image format used when the baseline was created.
  3. Capture a page or locator image to an “actual” artifact.
  4. Load the reviewed baseline and compare it.
  5. On failure, retain the actual and diff images in CI artifacts.
  6. Review the visual change. Replace the baseline only when the change is intentional and approved.

Keep references in source control or an equivalently reviewed artifact store. The Playwright visual-comparison guide describes the same lifecycle—an initial reference followed by comparisons and deliberate updates—but its snapshot-update commands belong to Playwright Test, not Java.

Page versus locator, and strict versus tolerant comparison

Choice Use it when Trade-off
Page screenshot You need coverage of layout, navigation, typography and page-wide interactions. Unrelated changes anywhere on the page can fail the test.
Locator screenshot You are validating a reusable component or isolated region. It will not detect regressions outside that locator.
Strict comparison The rendering environment is tightly controlled and every pixel matters. More sensitive to anti-aliasing and tiny environmental changes.
Tolerant comparison Small rendering noise is expected and the comparator’s behavior is understood. Can hide a real defect if the allowance is too broad.

Document why a threshold exists, which regions are masked, and who approves baseline updates. Do not copy JavaScript runner options such as maxDiffPixels into Java code as though they were Playwright Java APIs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshooting visual mismatches

Every pixel differs

Check that both files use the same format, viewport, device scale factor, browser version, color scheme, fonts and headless mode. A PNG-versus-lossy-WebP mismatch is not a useful regression signal.

Only text edges differ

Font availability, operating-system rendering, browser revision or device scale is usually responsible. Run baseline and comparison in the same container or machine image and install identical fonts.

The page is captured before content settles

Wait for a meaningful locator or application-ready state rather than relying only on a fixed sleep. If data is asynchronous, stub it or use deterministic fixtures where your test architecture permits.

Animated content causes intermittent failures

Disable animations and transitions, mask clocks or rotating content, or inject a screenshot stylesheet. Keep the decision explicit so the test’s coverage remains understandable.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dimensions do not match

A full-page image and viewport image are different tests. Confirm setFullPage, viewport dimensions, device scale and locator bounds are identical between runs.

You expected toHaveScreenshot() in Java

That matcher is documented for the Playwright Test runner and its JavaScript/TypeScript examples. In Java, capture with Page.screenshot() or Locator.screenshot(), then invoke your selected image comparison implementation.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and cost considerations

  • Locator captures are generally smaller and faster to inspect than full-page images, while page captures provide broader coverage.
  • Full-page screenshots of long pages consume more memory and produce larger CI artifacts. Capture only the scope that answers the test question.
  • Saving actual and diff files only on failure reduces routine artifact storage.
  • Reuse a browser process across tests when isolation requirements allow it, but create fresh contexts when cookies, locale or permissions could affect pixels.
  • Run visual tests on a stable power source and fixed headless configuration. Playwright documents rendering variation from host OS, version, settings, hardware, power source and headless mode.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you need a rendered URL without maintaining Playwright browser setup. One GET request returns PNG, JPEG, WebP or PDF. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for options such as full-page capture, CSS-selector elements, dark mode, device presets, retina scale, custom CSS or JavaScript, click and wait conditions, request blocking, headers, cookies, geolocation, caching, signed links, asynchronous webhooks and bulk capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Can I compare screenshots without Playwright Test?

Yes. Playwright Java captures the bytes; a Java image comparator or test library performs the comparison. The matcher documented for Playwright Test is not a Java API.

Should baselines be PNG or WebP?

Use a lossless format consistently. PNG is the simplest choice; Java release notes document WebP support, with quality 100 described as lossless.

Should I mask dynamic content?

Mask it when that content is outside the behavior being tested. Record the mask and keep a separate test if the dynamic content itself matters.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I compare screenshots without Playwright Test?

Yes. Capture with Playwright Java and compare the resulting bytes with a Java image-diff implementation or test library.

What tolerance should I use?

There is no universal value. Choose and document one after stabilizing the rendering environment and deciding which differences matter.

Is Locator.screenshot preferred for components?

Yes. It isolates the component and is the documented modern API; ElementHandle.screenshot is discouraged.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.