How to Fix Blurry Playwright Screenshots on High-DPI Displays
Set Playwright’s screenshot scale to match your output: device pixels for high-resolution images, or CSS pixels for compact captures and consistent baselines.
For a sharper, high-resolution Playwright page screenshot, set scale: "device". This maps output pixels to device pixels. For a smaller image with one output pixel per CSS pixel, use scale: "css". The right choice depends on how the image will be viewed or compared; changing scale is not a universal sharpening filter.
1. Choose the screenshot scale
| Option | Pixel mapping | Use it when | Trade-off |
|---|---|---|---|
device |
One output pixel per device pixel | You need a higher-resolution image for review or display | On a high-DPI display, output can be twice as large or larger than CSS-scale output, increasing file size |
css |
One output pixel per CSS pixel | You want compact output at the page’s CSS layout resolution | It has fewer pixels than device-scale output on a high-DPI display |
The Playwright page screenshot API documents device as the default for page.screenshot(). A screenshot that looks softer than expected may still be using the wrong pixel mapping for its intended use, so set the option explicitly. See the Playwright Page API.
2. Set scale in a runnable Playwright script
This Node.js example opens a page and saves a device-pixel screenshot. Install Playwright and its browser first with npm install -D playwright and npx playwright install chromium, then save this as screenshot.mjs and run node screenshot.mjs.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
try {
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'screenshot.png', scale: 'device', fullPage: true });
} finally {
await browser.close();
}
To save one pixel per CSS pixel instead, change the screenshot call to:
await page.screenshot({ path: 'screenshot.png', scale: 'css', fullPage: true });
These are page screenshot options. The viewport remains expressed in CSS pixels; scale determines how those CSS dimensions map to output pixels. A full-page screenshot can be substantially taller than the viewport, and device scale increases its pixel dimensions accordingly.
3. Make visual screenshot assertions explicit
Playwright Test screenshot assertions use a different documented default: toHaveScreenshot() defaults to css, while page.screenshot() defaults to device. When comparing a saved screenshot with a test baseline, choose the same scale explicitly to avoid comparing images with different pixel mappings.
import { test, expect } from '@playwright/test';
test('page screenshot has a stable CSS-pixel baseline', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveScreenshot('home.png', { scale: 'css' });
});
To use device-pixel output for the assertion, set scale: 'device' in the assertion options. Keep the scale consistent when creating and updating baselines as well as when reviewing direct captures. See the PageAssertions API.
4. Diagnose blur beyond scale
- Confirm the scale. Set it explicitly on the exact API producing the image:
page.screenshot()ortoHaveScreenshot(). - Inspect pixel dimensions. Compare the image’s actual width and height with the CSS viewport and the intended output. Device-scale output can be larger, especially on high-DPI displays.
- Check the source page. If text and controls are crisp but a particular image is soft, inspect whether that page asset itself has enough resolution. Capture scale cannot add detail absent from the source.
- Keep visual-test environments stable. Playwright notes that rendering can vary with operating system, browser version, settings, hardware, power source, and headless mode. Generate and compare baselines in the same environment. See Playwright visual comparisons.
- Separate resolution from rendering differences. A changed baseline or softened element is not by itself proof that scale is the cause. Check the relevant page content and environment after confirming the pixel mapping.
5. Troubleshooting
| Symptom | Likely explanation | What to do |
|---|---|---|
| The saved screenshot looks soft when enlarged | It may be CSS-scale output being viewed larger than its pixel dimensions support | Capture with scale: 'device' if you need more output pixels, then check the resulting dimensions |
| Screenshot files became unexpectedly large | Device-pixel output contains more pixels on a high-DPI display | Use scale: 'css' if CSS-resolution output meets the need; consider the added size of full-page captures |
| A direct screenshot and test baseline differ in dimensions | page.screenshot() and toHaveScreenshot() have different documented defaults |
Set the same scale explicitly for both workflows |
| Visual tests vary between machines or runs | Browser rendering can depend on the host environment and browser configuration | Generate and compare baselines in the same environment, and keep browser and operating system consistent |
| Only one image or region looks blurry | The page’s source content may not provide enough detail at the displayed size | Check the source image or asset and compare it at its native dimensions; changing screenshot scale cannot restore missing source detail |
6. Performance, reliability, and cost
device can produce more pixels than css, which means larger image output and potentially more storage or transfer. This is most noticeable with high-DPI capture and full-page screenshots. Use CSS scale for compact visual baselines when CSS-pixel output is sufficient; use device scale when the additional resolution is useful.
For reliable visual comparisons, keep the scale and capture environment stable. Playwright specifically recommends creating and comparing screenshot baselines in the same environment because rendering varies across systems and browser setups. The research sources do not establish a universal speed or file-size multiplier, so measure your own pages if output size or capture throughput is a constraint.
Or skip the browser setup
If you need a clean screenshot without configuring Playwright, ScreenshotNeo provides a website screenshot API and MCP server. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses include page-verdict and billing headers.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
FAQ
Does scale: 'device' make every screenshot sharper?
It maps output pixels to device pixels and can provide more resolution. It does not sharpen low-resolution page assets or guarantee every blur has the same cause.
Should I use CSS or device scale for visual regression tests?
Use the mapping that matches your baseline and output needs, then specify it explicitly. Consistency between baseline creation and comparison matters more than relying on differing API defaults.
Will device scale change my page layout?
The option controls screenshot pixel mapping. The viewport remains configured in CSS pixels; the resulting output may have more pixels.


