ScreenshotNeo

BlogHow-to

Set device scale factor for website screenshots in Playwright with Node.js

Set Playwright’s deviceScaleFactor for high-DPI screenshots, choose CSS or device output pixels, and avoid common viewport and visual-test mismatches.

By the ScreenshotNeo team4 October 20267 min read

Set deviceScaleFactor when you create a Playwright browser context. It emulates the page’s device pixel ratio (DPR). Then choose the screenshot’s scale: 'device' captures device pixels, while 'css' keeps one output pixel per CSS pixel.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const context = await browser.newContext({
      viewport: { width: 1280, height: 720 },
      deviceScaleFactor: 2,
    });
    const page = await context.newPage();
    await page.goto('https://example.com', { waitUntil: 'load' });
    await page.screenshot({ path: 'screenshot.png', scale: 'device' });
    await context.close();
  } finally {
    await browser.close();
  }
})();

Install the package with npm install playwright. If your project uses a browser that is not already installed, install the required browser with npx playwright install chromium. See the Playwright emulation guide and Page API.

1. Understand DPR, viewport, and screenshot scale

These are related settings with separate jobs:

Setting Controls Example
viewport The page’s layout viewport, in CSS pixels. { width: 1280, height: 720 }
deviceScaleFactor The emulated device pixel ratio for the browser context. Its default is 1. 2 emulates two device pixels per CSS pixel.
page.screenshot({ scale }) How CSS pixels map to output image pixels. 'css' gives one image pixel per CSS pixel; 'device' gives one per device pixel.

For a 1280 × 720 CSS-pixel viewport and factor 2, a viewport screenshot using scale: 'device' is nominally 2560 × 1440 pixels. With scale: 'css', it is 1280 × 720 pixels. The device scale factor does not change the CSS viewport dimensions.

Playwright documents 'device' as the screenshot scale default. Set it explicitly when output dimensions are part of a contract, such as an asset pipeline or image comparison. See the screenshot options.

2. Configure a Node.js screenshot

Set the context options before creating the page. This ensures the page is created with the intended emulation settings.

const { chromium } = require('playwright');

async function capture() {
  const browser = await chromium.launch();
  try {
    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: 'example-2x.png',
      fullPage: true,
      scale: 'device',
    });

    await context.close();
  } finally {
    await browser.close();
  }
}

capture().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

fullPage: true captures the full scrollable page. The output height depends on the page content, so it can be much taller than the viewport. Omit it for a viewport-only capture. Choose a navigation wait condition appropriate for the site; networkidle can wait longer or be unsuitable for pages with ongoing network activity. For pages that continue loading analytics or live updates, wait for a meaningful selector instead.

Save CSS-pixel dimensions

Use scale: 'css' when consumers expect the screenshot dimensions to match the CSS layout dimensions:

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

Use an ES module

With a project configured for ES modules, the same context option works with an import:

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const context = await browser.newContext({
    viewport: { width: 1280, height: 720 },
    deviceScaleFactor: 2,
  });
  const page = await context.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'shot.png', scale: 'device' });
  await context.close();
} finally {
  await browser.close();
}

Set it in Playwright Test

For tests, set the factor under the shared use configuration, or override it for a test scope:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    viewport: { width: 1280, height: 720 },
    deviceScaleFactor: 2,
  },
});

The test’s screenshot assertion or page.screenshot() still determines the screenshot output scale. Configure the context factor and screenshot scale intentionally rather than treating them as interchangeable.

3. Choose the right output for your use case

  • High-resolution asset or inspection: use a deliberate viewport, a higher deviceScaleFactor, and scale: 'device'.
  • Fixed CSS-pixel dimensions: use scale: 'css'; the browser can still emulate a higher DPR for page rendering.
  • Responsive layout check: choose the viewport width and height that exercise the layout. DPR affects pixel density, not the CSS viewport breakpoint.
  • Visual regression baseline: use the same viewport, factor, screenshot options, browser version, and host environment when generating and comparing images.

Higher output dimensions contain more pixels and can increase file size and processing time. If a downstream system resizes the image, capture at the dimensions it actually needs where possible.

4. Use a device preset carefully

Playwright device descriptors can provide a device’s viewport and emulation settings. If you spread a descriptor and then set a custom viewport, the explicit viewport takes precedence:

const { chromium, devices } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const iphone = devices['iPhone 13'];
    const context = await browser.newContext({
      ...iphone,
      viewport: { width: 1280, height: 720 },
      deviceScaleFactor: 2,
    });
    const page = await context.newPage();
    await page.goto('https://example.com');
    await page.screenshot({ path: 'device-view.png', scale: 'device' });
    await context.close();
  } finally {
    await browser.close();
  }
})();

Use the preset’s viewport when you want that device’s layout dimensions. Override it only when you intend to test a different CSS viewport. A high DPR alone does not make a desktop viewport behave like a phone; viewport and other device emulation settings matter too. The emulation guide covers device descriptors and context settings.

5. Keep visual comparisons reproducible

Playwright notes that rendering can vary with the operating system, browser version, settings, hardware, power source, and headless mode. For screenshot assertions, generate baselines and comparisons in the same rendering environment. Keep the viewport, deviceScaleFactor, screenshot scale, and other emulation settings unchanged unless the test is specifically checking a change in them. See Playwright visual comparisons.

A DPR change can alter output dimensions and rasterization, so it may make many pixels differ even when the page’s CSS layout is unchanged. Treat a change to the factor or screenshot scale as a baseline-affecting test configuration change.

6. Troubleshooting

Symptom Likely cause Fix
Image dimensions are not doubled. The screenshot uses scale: 'css', or the context factor is still 1. Set deviceScaleFactor: 2 on the context and use scale: 'device' when device-pixel output is wanted.
Image is unexpectedly large. scale: 'device' multiplies CSS dimensions by the DPR, and fullPage can add a tall page dimension. Use scale: 'css', lower the factor, or capture only the viewport if those dimensions meet the requirement.
Changing the factor has no effect. The setting was applied after creating the page/context, or the screenshot is explicitly CSS-scaled. Create a new browser context with the intended factor before creating pages; check the screenshot’s scale.
Visual snapshot tests fail after a configuration change. The generated image’s pixel dimensions or rasterization changed. Restore the baseline configuration or deliberately regenerate baselines in the same stable environment as future comparisons.
Mobile layout does not appear. A high device scale factor does not change the CSS viewport width or apply all device emulation settings. Set the intended viewport or use an appropriate device descriptor, then inspect the effective context settings.
Navigation or screenshot hangs. The page may keep network connections open, or navigation may be waiting on an unsuitable condition. Use a suitable navigation condition and wait for a page-specific selector or event instead of requiring network idle.
Browser launch reports a missing executable. The selected Playwright browser binary is not installed in the environment. Install the browser required by the project with npx playwright install chromium (or the relevant engine).

7. Or skip the browser setup

If you need a screenshot without managing a Playwright browser, [ScreenshotNeo](https://screenshotneo.com) provides a website screenshot API. Its retina scale option controls high-resolution output; see the ScreenshotNeo API docs for the available 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,
)
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}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
  • Cookie banners are accepted and removed before the shot, along with supported newsletter popups and chat widgets; each cleanup step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses indicate the page verdict and billing status in headers.
  • An MCP server lets AI agents use screenshot, page information, and PDF capture tools.
  • The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.

8. FAQ

Does deviceScaleFactor change the CSS layout?

It emulates the device pixel ratio. The viewport remains the CSS layout size you configured; use viewport settings to test a different layout width or height.

Should I use scale: ‘css’ or scale: ‘device’?

Use 'css' for one output pixel per CSS pixel. Use 'device' when you want the emulated device-pixel resolution.

Can I change deviceScaleFactor for one page in a context?

It is a browser context setting. Create a context with the desired factor before creating pages that need it; use separate contexts when a run needs different emulation configurations.

What should I pin for stable screenshot tests?

Keep the browser and host rendering environment, viewport, device scale factor, screenshot scale, and relevant page state consistent with the baseline.