Fix Playwright Screenshots That Are Stretched or Distorted
A Playwright screenshot can look stretched when its device-pixel dimensions are mistaken for CSS pixels. Check scale, deviceScaleFactor, full-page capture, and the image viewer in that order.
If your Playwright screenshot looks stretched or distorted, first compare the saved image’s pixel dimensions with the page’s CSS viewport dimensions. Playwright can save one image pixel per CSS pixel or one per device pixel. With device-pixel capture, the image may be larger than the viewport dimensions; a viewer or comparison tool that treats those dimensions as CSS pixels can make it appear incorrectly scaled. Check the screenshot’s scale, the context’s deviceScaleFactor, and whether fullPage is enabled before changing page CSS. See the official Playwright Page API.
1. Check the screenshot’s pixel dimensions first
Log the viewport, device scale factor, browser engine, and screenshot options for the failing capture. Then inspect the saved PNG’s actual pixel width and height. Compare like with like: CSS-pixel dimensions against CSS-pixel output, and device-pixel dimensions against device-pixel output.
const context = await browser.newContext({
viewport: { width: 1280, height: 720 },
deviceScaleFactor: 2,
});
const page = await context.newPage();
await page.goto('https://example.com');
console.log({
viewport: page.viewportSize(),
deviceScaleFactor: context._options?.deviceScaleFactor,
browser: browser.browserType().name(),
version: browser.version(),
});
await page.screenshot({ path: 'page.png' });
context._options is an internal property and can vary by Playwright version. Prefer logging the configuration object your test used when possible:
const captureConfig = {
viewport: { width: 1280, height: 720 },
deviceScaleFactor: 2,
screenshot: { fullPage: false, scale: 'css' },
};
console.log(captureConfig);
const context = await browser.newContext({
viewport: captureConfig.viewport,
deviceScaleFactor: captureConfig.deviceScaleFactor,
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'page.png', ...captureConfig.screenshot });
Use the configuration you actually pass to the context; do not rely on an internal property as a stable diagnostic interface. A mismatch between image dimensions and viewport dimensions is a clue, not by itself proof that the page rendered incorrectly.
2. Choose the screenshot output scale
Playwright’s screenshot scale option controls output pixel density:
scale: 'css': one output pixel per CSS pixel.scale: 'device': one output pixel per device pixel. On a high-DPI context, the resulting image can be twice as large or more in each dimension.
If the artifact should have dimensions corresponding to the CSS viewport, set scale: 'css' explicitly as a diagnostic. The official API describes this as one screenshot pixel per CSS pixel. If the image then looks right, output scale or downstream resizing was involved; that does not prove the page itself had a rendering bug.
await page.screenshot({
path: 'page.png',
fullPage: false,
scale: 'css',
});
For a viewport of 1280 by 720 CSS pixels, CSS-scale capture is intended to produce dimensions in CSS-pixel units. Device-scale capture uses device pixels, so check the configured deviceScaleFactor as well. Avoid resizing the image to a guessed size until you have confirmed which coordinate system the consumer expects.
3. Check deviceScaleFactor and viewport together
deviceScaleFactor emulates the device pixel ratio. It is separate from the viewport width and height, which describe the page’s CSS viewport. A device factor of 2 can produce a denser device-scale image while the CSS viewport remains 1280 by 720. This can be expected behavior rather than stretching.
const context = await browser.newContext({
viewport: { width: 1280, height: 720 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'page-css-scale.png', scale: 'css' });
For a controlled comparison, keep the page and viewport fixed, then vary only one factor at a time:
- Capture with
scale: 'css', then withscale: 'device'. - Keep scale fixed and compare the configured device scale factors.
- Keep both fixed and compare viewport-only with full-page capture.
- Record the browser engine and version for each artifact.
Do not compare captures with different viewport or emulated device settings and attribute every visible difference to screenshot scaling. A different viewport can change responsive layout as well as image dimensions.
4. Separate viewport capture from full-page capture
fullPage: true captures the full scrollable page rather than only the visible viewport. Its output height is therefore expected to exceed the viewport height on a long page. If only full-page screenshots look wrong, compare them with viewport-only captures under the same browser, viewport, device scale, and output scale.
// Visible viewport only
await page.screenshot({ path: 'viewport.png', scale: 'css' });
// Entire scrollable page
await page.screenshot({
path: 'full-page.png',
fullPage: true,
scale: 'css',
});
Playwright also supports a clip rectangle for capturing a region. Use it when the artifact should have a known CSS-pixel region rather than the viewport or whole page; ensure the rectangle is inside the page and compare its output using the chosen scale. See the Page screenshot options and the official Playwright screenshot tool overview.
A historical Playwright issue reported a full-page image dimension limit for WebKit on Linux on some websites, with scale: 'css' suggested as a workaround in that report. Treat it as a version- and case-specific report, not a universal current WebKit bug: issue #16727.
5. Verify whether the browser or the image consumer is distorting it
If the pixel dimensions and selected scale make sense but the image still looks wrong, inspect the page and the path between capture and display:
- Open the original PNG directly in an image viewer and check its reported pixel dimensions.
- Compare the original artifact with the version embedded in a test report, HTML page, or issue tracker. A fixed-width and fixed-height display box can distort an image if it does not preserve aspect ratio.
- Check page CSS for transforms, zoom-like effects, responsive breakpoints, and elements whose size depends on viewport or device scale.
- Compare the same page, viewport, scale, and full-page setting across the browser engine and version used in the failing test.
- Inspect image metadata and any post-processing step that crops, resizes, or recompresses the artifact.
There is no single root cause implied by “stretched screenshot.” A concrete reproduction is needed to distinguish a page layout issue, capture configuration, browser behavior, and viewer resizing.
6. Troubleshooting common symptoms
| Symptom | Likely cause to check | What to do |
|---|---|---|
| Image is larger than the viewport in both dimensions | Device-pixel output combined with a device scale factor above 1 | Check deviceScaleFactor; capture with scale: 'css' if CSS-pixel dimensions are required. |
| Image looks smaller or larger only in a report | The report or viewer is resizing the image for display | Compare the original file with the embedded artifact and check whether display width and height preserve aspect ratio. |
| Only the full-page image looks wrong | Different full-page dimensions, page length, or browser-specific capture behavior | Compare viewport-only and full-page captures with all other options held constant; record engine and version. |
| Page content itself is laid out differently | Viewport or device emulation changed responsive CSS or page behavior | Hold viewport and device settings constant; inspect computed layout and transforms. |
| Capture fails or is clipped near page limits | Page size or browser engine behavior may affect full-page capture | Reduce the capture to a reproducible page or region, compare browser engines, and consult the relevant version’s behavior. The historical WebKit report is not evidence of a universal bug. |
| CSS scale appears not to fix it | The distortion may be in page CSS, post-processing, embedding, or viewer behavior | Inspect the original file and compare a minimal capture with no post-processing. |
When asking for help, include the Playwright version, browser engine and version, operating system, viewport, device scale factor, screenshot options, saved image dimensions, and a minimal page or reproduction. That information makes it possible to investigate the specific failure without guessing.
7. Reliability, performance, and artifact cost
Use the smallest capture mode that answers the test’s question. A viewport screenshot is usually a smaller artifact than a long full-page screenshot; full-page capture is needed when content below the fold matters. Device-scale output can contain more pixels and take more storage or transfer than CSS-scale output. The exact time and file size depend on page content, browser, and environment, so measure them in your own workflow rather than assuming a fixed ratio.
For reliable visual comparisons, keep browser engine and version, viewport, device scale factor, output scale, page state, and capture timing consistent. Wait for the page state the test needs before capture, and keep post-processing consistent. If an image is used in a report, preserve its aspect ratio when displaying it and retain the original file for diagnosis.
8. Or skip the browser setup
If your goal is a screenshot artifact rather than debugging Playwright’s image scaling, ScreenshotNeo provides a website screenshot API and MCP server for developers. A single GET request returns a PNG, JPEG, WebP, or PDF. Its API docs cover the available capture parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
Sign up free for 1,000 screenshots a month, no card required.
9. FAQ
Does a screenshot with more pixels mean Playwright stretched the page?
No. It may reflect device-pixel output. Compare the saved pixel dimensions with the configured viewport, device scale factor, and screenshot scale.
Should I always use scale: 'css'?
Use it when your output should have one image pixel per CSS pixel. Device-pixel output is useful when the artifact needs the emulated device’s pixel density.
Can I diagnose this without a reproduction?
You can narrow it down by inspecting dimensions and capture settings, but identifying a page-specific CSS issue or browser regression requires a reproducible case.


