How to Capture a Website Screenshot at 390 by 844 CSS Pixels
Set a 390 × 844 CSS-pixel viewport, capture only the visible area, and use CSS screenshot scale for an image with those exact dimensions.
To save a screenshot that is exactly 390 × 844 image pixels, set the browser viewport to 390 × 844 CSS pixels before loading the page, capture only the visible viewport, and use Playwright’s scale: "css". Do not enable full-page capture. The viewport sets the page’s layout dimensions; CSS scale makes each CSS pixel map to one screenshot pixel. Playwright’s Page API documents viewport sizing and screenshot scale.
1. Capture the viewport with Playwright
This Node.js example creates a fresh browser context with the requested viewport, navigates to a page, and writes a PNG. It omits fullPage, so the capture is limited to the visible viewport.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'load' });
await page.screenshot({
path: 'screenshot.png',
scale: 'css',
});
await context.close();
} finally {
await browser.close();
}
})();
Save the code as screenshot.js. Install Playwright and its Chromium browser, then run it:
npm init -y
npm install playwright
npx playwright install chromium
node screenshot.js
The output should be 390 pixels wide by 844 pixels high. The viewport must be configured before navigation so the page lays itself out at the intended width. You can set it on the context as above, or call await page.setViewportSize({ width: 390, height: 844 }) before navigating. The Page API notes that setting the page viewport also resets the screen size; use browser-context settings when you need more control over screen and viewport properties.
Why use scale: "css"?
Playwright’s default screenshot scale is device-based. On a high-DPI configuration, device scale can produce more output pixels than CSS pixels. With scale: "css", one output image pixel corresponds to one CSS pixel, giving a 390 × 844 result for this viewport. See the screenshot options in the Page API.
2. Choose viewport or full-page capture
A viewport screenshot contains the currently visible region, so its dimensions stay 390 × 844 with CSS scale. A full-page screenshot includes content below the fold and is usually taller than 844 pixels. Leave fullPage unset or set it to false when the deliverable must have the exact viewport dimensions.
In Chrome DevTools, open Device Mode, set the viewport width and height to 390 and 844, then choose the menu option to capture a screenshot. Choose the visible viewport capture, not “Capture a full size screenshot.” Chrome’s Device Mode documentation explains viewport simulation, device pixel ratio, and the two capture extents.
3. CSS pixels are different from device pixels
The requested 390 × 844 values describe CSS pixels: the dimensions the page uses for layout. Device pixel ratio determines how CSS pixels map to physical screen pixels. A device-based screenshot can therefore have larger pixel dimensions even when the CSS viewport remains 390 × 844.
If you need a specific image size, set the viewport and use CSS scale. If you need to imitate a particular physical phone, viewport dimensions alone may not be enough. You may also need its device scale factor, screen size, touch capability, and user agent. Playwright’s emulation guide describes these device parameters. Chrome’s DevTools Protocol Page reference documents device metrics and screenshot capture.
4. Capture with Chrome DevTools manually
- Open the target page in Chrome and open DevTools.
- Turn on Device Mode.
- Set the viewport width to
390and height to844. - Use the capture menu’s viewport screenshot option.
- Check the resulting image dimensions. Device pixel ratio affects output pixels, so use Playwright with CSS scale when exact output dimensions must be repeatable.
DevTools is useful for a quick one-off capture. Playwright is a better fit when you need the same dimensions repeatedly or in an automated workflow.
5. Handle page loading and dynamic content
The example waits for the page’s load event. That is suitable for many pages, but it does not guarantee that content rendered later by JavaScript, images loaded lazily, or animations have finished. If the target has a known element that signals readiness, wait for it before capturing:
await page.goto('https://example.com', { waitUntil: 'load' });
await page.locator('[data-page-ready]').waitFor({ state: 'visible' });
await page.screenshot({ path: 'screenshot.png', scale: 'css' });
Replace [data-page-ready] with a selector that exists on the page. For a short, predictable delay, use await page.waitForTimeout(1000), but a selector is generally more reliable than guessing how long the page needs. If content appears below the fold only after scrolling, scrolling can trigger lazy loading; remember that the final screenshot still captures the current viewport unless you enable full-page capture.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The image is larger than 390 × 844 | Screenshot scale is device-based, or the browser context uses a device scale factor greater than one. | Set scale: "css". If emulating a device, check its scale factor and verify the saved image dimensions. |
| The image is taller than 844 pixels | Full-page capture is enabled. | Omit fullPage or set it to false. |
| The page layout looks like desktop | The viewport was set after navigation, or the page does not use responsive layout. | Set the viewport on the browser context before creating the page and navigating. Check the site’s responsive behavior at 390 CSS pixels. |
| The screenshot is blank or incomplete | Navigation or client-side rendering has not finished, or the page failed to load. | Wait for a page-specific selector, inspect navigation errors, and confirm the target URL is reachable from the machine running Chromium. |
| Images or content are missing | They may load lazily or after the initial page event. | Wait for the relevant content or trigger its loading before capture. Use a readiness selector where possible. |
| Playwright cannot launch Chromium | The browser binary or required system dependencies may be missing. | Run npx playwright install chromium; in a container, install the dependencies required by your environment. |
7. Performance, repeatability, and cost
A single local screenshot requires starting Chromium, loading the page, and writing an image. Reuse a browser process when capturing many pages, but create an isolated context for each page or configuration that needs separate browser state. Close pages, contexts, and the browser when finished so browser processes do not accumulate.
For dependable captures, use a stable viewport, explicit screenshot scale, and a page-specific readiness condition. Page content can vary because of network delays, personalization, animation, or consent dialogs. Save the URL and capture settings alongside automated results when you need to reproduce a particular image. Local Playwright has no per-screenshot API charge; account for the machine time and infrastructure used to run Chromium.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. It supports any viewport and returns screenshots or PDFs. Check the ScreenshotNeo API documentation for the viewport parameter syntax when you need a 390 × 844 capture; the example below shows the basic one-call request.
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 import('node:fs/promises').then(({ writeFile }) =>
writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
Cookie banners are accepted like a visitor and removed before the shot, along with known 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, and response headers report the page verdict and billing status. An MCP server lets AI agents use screenshot, page-info, and PDF tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. For an exact viewport, configure the documented viewport option and verify the returned image dimensions.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
FAQ
Will a 390 × 844 viewport always produce a 390 × 844 image?
With Playwright’s CSS screenshot scale and viewport capture, yes. Device-based scale or full-page capture changes the output dimensions.
Does this setting make the page behave exactly like a phone?
No. It sets the CSS viewport. A full device profile may also require a device scale factor, screen dimensions, touch support, and user agent.
Should I use DevTools or Playwright?
Use DevTools for a quick manual screenshot. Use Playwright for repeatable captures, automation, or a pipeline.


