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.

Playwright does not expose a print-style DPI field for screenshots. Output resolution is controlled by two settings: scale, which chooses CSS-pixel or device-pixel output, and the browser context’s deviceScaleFactor (the emulated DPR). Use scale: 'css' for one image pixel per CSS pixel, or scale: 'device' for one image pixel per device pixel. A normal page.screenshot() uses device scale by default, while screenshot assertions use CSS scale by default.

The direct answer

Set the browser context’s deviceScaleFactor and the screenshot’s scale explicitly instead of trying to set DPI:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  deviceScaleFactor: 2
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });

await page.screenshot({ path: 'css-scale.png', scale: 'css' });
await page.screenshot({ path: 'device-scale.png', scale: 'device' });

await browser.close();

With a 1280-pixel-wide viewport, the CSS-scaled file is approximately 1280 pixels wide. The device-scaled file is approximately 2560 pixels wide when the effective device scale factor is 2. Page content, full-page height, clipping, and element dimensions also affect the final dimensions.

What “resolution” means in Playwright

CSS pixels

A CSS pixel is the unit used by the page’s layout: viewport width, font sizes, margins, and element coordinates. scale: 'css' writes one output pixel for each CSS pixel. This keeps captures smaller and makes the image dimensions track the CSS viewport.

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

Device pixels

A device pixel is the raster pixel used for the emulated display. scale: 'device' writes one output pixel for each device pixel. On a high-DPI emulation, the same 1280-CSS-pixel viewport can therefore produce an image around 2560 pixels wide.

Device scale factor (DPR)

deviceScaleFactor is the browser context setting that controls the emulated ratio between CSS pixels and device pixels. Playwright documents a default value of 1; a value of 2 represents a two-times device pixel ratio for rendering and device-scaled screenshots. It is useful to think of it as DPR, but it is not a print-DPI metadata field.

Choose the output you actually need

Goal Settings Result
Small web preview or predictable layout dimensions scale: 'css'; any required deviceScaleFactor One output pixel per CSS pixel
Retina-style asset or pixel-dense capture scale: 'device' and an explicit deviceScaleFactor One output pixel per emulated device pixel; larger files at DPR 2 or 3
Visual regression baseline Use the same scale, deviceScaleFactor, viewport, and capture options for every run Comparable raster dimensions and fewer false differences
Print-oriented PDF Use Playwright’s PDF API and paper settings rather than screenshot DPI A document with page geometry; screenshot scale is not a print-resolution control

If consumers expect a fixed pixel size, scale: 'css' is usually the least surprising choice. If they expect a high-density image, select scale: 'device' and name the DPR in your build configuration.

Set resolution in a Node.js Playwright script

CSS-pixel output

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 2
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });

// 1440 CSS pixels wide, despite the DPR of 2.
await page.screenshot({
  path: 'homepage-css.png',
  scale: 'css',
  fullPage: true
});
await browser.close();

Device-pixel output

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 2
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });

// Approximately 2880 device pixels wide for the same CSS viewport.
await page.screenshot({
  path: 'homepage-device.png',
  scale: 'device',
  fullPage: true
});
await browser.close();

fullPage: true makes the capture’s height follow the document rather than only the viewport. A very long page can therefore create a large bitmap even when the viewport width is modest.

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

Capture one element

const card = page.locator('[data-testid="pricing-card"]');
await card.screenshot({
  path: 'pricing-card@2x.png',
  scale: 'device'
});

For an element screenshot, the element’s CSS bounding box replaces the viewport as the size input. Padding, borders, transforms, and fractional coordinates can make the final pixel dimensions differ by a pixel from a simple multiplication.

Do not confuse page screenshots with screenshot assertions

Playwright documents different defaults for its two APIs:

  • page.screenshot() defaults to scale: 'device'.
  • expect(page).toHaveScreenshot() defaults to scale: 'css'.

Make the choice explicit when creating and comparing visual baselines:

import { test, expect } from '@playwright/test';

test('dashboard baseline', async ({ page }) => {
  await page.goto('https://example.com/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    scale: 'css'
  });
});

If a baseline was generated with one scale and compared with another, the images can have different dimensions even though the page layout is unchanged. Keep the scale, context DPR, viewport, browser version, and relevant screenshot options identical in baseline and comparison jobs.

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

How dimensions are determined

For a normal viewport capture, the useful approximation is:

output width = CSS width × effective device scale when using scale: 'device', and output width = CSS width when using scale: 'css'. The same relationship applies to height. The approximation assumes integer coordinates and no clipping.

  • The context viewport supplies the CSS layout size.
  • deviceScaleFactor changes device-pixel density.
  • scale decides whether the file follows CSS pixels or device pixels.
  • fullPage changes height to include the document’s full scrollable content.
  • clip or an element locator limits the captured rectangle.
  • Fractional layout coordinates, transforms, borders, and browser rasterization can cause rounding at edges.

Do not infer dimensions from the monitor running the test. Playwright uses the emulated context settings, not the physical display’s DPI.

Practical DPR recipes

One CSS pixel per image pixel

const context = await browser.newContext({
  viewport: { width: 1280, height: 800 },
  deviceScaleFactor: 1
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: '1280x800.png', scale: 'css' });

Two-times output for a retina asset

const context = await browser.newContext({
  viewport: { width: 800, height: 600 },
  deviceScaleFactor: 2
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: '1600x1200.png', scale: 'device' });

Three-times output

const context = await browser.newContext({
  viewport: { width: 400, height: 300 },
  deviceScaleFactor: 3
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: '1200x900.png', scale: 'device' });

Use high DPR selectively. A three-times full-page capture can consume substantially more memory and storage than a CSS-scaled image, and it does not add information that was never rendered by the page.

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

Python equivalent

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context(
        viewport={"width": 1280, "height": 720},
        device_scale_factor=2,
    )
    page = context.new_page()
    page.goto("https://example.com", wait_until="networkidle")
    page.screenshot(path="css.png", scale="css")
    page.screenshot(path="device.png", scale="device")
    browser.close()

The Python API uses the same concepts; only the naming style changes from JavaScript’s deviceScaleFactor to device_scale_factor.

DPI terminology and print output

“DPI” commonly describes physical print density, while Playwright’s screenshot reference describes pixel mapping and device scale. A PNG that is 2400 pixels wide does not automatically specify how many inches it should occupy on paper. If your requirement is a print document, use PDF paper size, margins, orientation, and page ranges. If your requirement is a web image, specify its pixel dimensions, scale mode, and DPR.

Performance, storage, and reliability

  • Prefer CSS scale for thumbnails, API responses, and visual tests where large rasters provide no benefit.
  • Use device scale for assets that will be displayed at a higher density, and cap the DPR to the largest consumer requirement.
  • Full-page and high-DPR captures multiply work in both dimensions; capture a selector or clip when the whole document is unnecessary.
  • Record the viewport, DPR, scale, browser version, and URL with each baseline so a later mismatch is diagnosable.
  • Wait for the same page state before each capture. Fonts, animations, lazy content, and late network responses can change pixels independently of resolution.

Troubleshooting common resolution problems

“My screenshot is twice as wide as the viewport”

The page screenshot is probably using its device default with deviceScaleFactor: 2. Set scale: 'css' for viewport-sized output, or set the DPR to 1 if device-pixel output is not needed.

Rank #4
Sale
Stunning Digital Photography
  • Used Book in Good Condition

“The assertion image is smaller than the page screenshot”

Assertions default to CSS scale while page.screenshot() defaults to device scale. Pass the same explicit scale value to both APIs and regenerate the baseline.

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

“Changing DPI did nothing”

There is no independent screenshot DPI option to change. Check that you changed the context’s deviceScaleFactor and selected scale: 'device'; CSS scale intentionally remains tied to CSS dimensions.

“The image is blurry at high DPR”

Higher output dimensions cannot restore detail absent from the source. Check the source image’s intrinsic size, CSS upscaling, font loading, and any post-capture resizing. A larger bitmap is not the same as sharper source content.

“Baselines fail only on CI”

Compare the CI and local viewport, DPR, scale, browser version, fonts, and page state. Make all resolution settings explicit and avoid relying on API defaults that differ between screenshot and assertion calls.

“A full-page file is unexpectedly huge”

Full-page height and device scale multiply the pixel count. Use CSS scale, a bounded clip, or an element screenshot when a complete document is not required.

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.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF output, while its capture pipeline accepts cookie and consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, 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.

See the full parameter list in the ScreenshotNeo documentation. The API supports full-page and selector captures, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, pre-capture clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.

cURL

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo also includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can a screenshot contain a DPI value for an image editor?

Playwright’s documented controls determine pixel dimensions and CSS/device mapping. If a downstream editor needs a physical print density, set that metadata in a separate image-processing step or create a PDF with explicit paper settings.

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

Should I use one DPR for every browser project?

Choose the DPR that matches the asset or test contract. A single documented value is useful for reproducibility, but different deliverables can legitimately use CSS scale, DPR 2, or another explicit configuration.

Does device scale factor change responsive breakpoints?

Responsive layout breakpoints are evaluated primarily against the CSS viewport. Changing the device scale factor changes raster density; changing the viewport changes the CSS layout dimensions.

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.