Make screenshot dimensions a versioned capture contract: set the viewport before navigation, choose CSS-pixel or device-pixel output, decide between the visible viewport and the full document, wait for a defined page state, and log the values you actually captured. Native Firefox uses --window-size; Playwright Firefox uses context viewport, deviceScaleFactor, screenshot scale, and fullPage. Leaving any of these to the host or browser defaults is the usual reason identical jobs produce different images.
What “consistent dimensions” means
First define the artifact you need. A fixed viewport screenshot has a predictable width and height in CSS pixels (or device pixels if you deliberately request that). A full-page screenshot has a fixed width but a height determined by the document at capture time. Those are different contracts: a page that grows because an image, font, or lazy section loaded cannot have the same full-page height as an earlier state.
- Viewport contract: for example, 1440×900 CSS pixels, visible area only.
- Pixel contract: CSS-pixel output or device-pixel output at a specified device pixel ratio (DPR).
- State contract: a URL, browser version, viewport, media settings, resources, and readiness condition.
Record the browser and automation-library versions with each artifact. When a comparison fails, you can then tell a configuration change from a legitimate page change.
Native Firefox headless: pin the window size
For Firefox’s command-line screenshot mode, --window-size=WIDTH,HEIGHT supplies the dimensions used by --screenshot. Use an explicit output filename and URL:
#1 Best Overall
firefox --headless --window-size=1440,900 --screenshot=page.png https://example.com
This produces a visible-viewport capture at the requested window dimensions. Keep the width and height in source control or in the job configuration rather than relying on a CI runner’s display settings.
Native Firefox checklist
- Pass both width and height; do not rely on a desktop or virtual-display default.
- Use a unique, explicit filename so an old image cannot be mistaken for a new result.
- Use the same Firefox build on every worker.
- Capture after the page reaches the state your test defines; headless mode does not make late-loading content deterministic.
Firefox DevTools :screenshot: control DPR and page mode
The Web Console screenshot helper has controls that are separate from the browser window size. Set the device-pixel ratio explicitly and choose whether the capture is the viewport or the full document:
:screenshot page.png --dpr 1 --fullpage
--dpr sets the device pixel ratio used for the image. --fullpage changes the capture to the complete scrollable page, so its height can vary with document content. The helper also supports --delay, --selector, and --filename; use those options when a defined delay, element-only artifact, or explicit path is part of your contract.
Viewport versus full page
| Requirement | Capture setting | Dimension behavior |
|---|---|---|
| Stable thumbnail or visual regression viewport | Window/viewport size, no full-page flag | Width and height follow the pinned viewport and pixel scale |
| Entire article or landing page | Full-page capture | Width follows the viewport; height follows the scrollable document at capture time |
| One component | Selector capture where supported | Dimensions follow the selected element’s box |
Do not compare a full-page image’s height with a viewport image’s height and call the difference a regression. Compare like-for-like modes.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePlaywright Firefox: set the context before navigation
Playwright contexts default to a 1280×720 viewport. Setting viewport: null delegates sizing to the host window, which makes CI results dependent on the runner. Create a context with explicit dimensions and DPR before opening or navigating the page:
const { firefox } = require('playwright');
(async () => {
const url = 'https://example.com';
const browser = await firefox.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1
});
const page = await context.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({
path: 'page.png',
fullPage: false,
scale: 'css'
});
await browser.close();
})();
Set a viewport with browser.newContext (or page.setViewportSize) before navigation. Responsive breakpoints are evaluated during layout, so changing the viewport after the page has loaded can produce a different DOM and image than setting it from the start.
Choose the screenshot scale
scale: 'css'writes one output pixel per CSS pixel. A 1440-pixel CSS width therefore remains 1440 pixels wide.scale: 'device'writes device pixels. With a DPR greater than one, the bitmap can be wider and taller even though the CSS viewport is unchanged.
Use scale: 'device' only when a high-DPI artifact is required. Otherwise pair deviceScaleFactor: 1 with scale: 'css' for the simplest dimensions.
Full-page Playwright capture
Set fullPage: true only when the required artifact is the complete scrollable document:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
await page.screenshot({
path: 'document.png',
fullPage: true,
scale: 'css'
});
The width still comes from the context viewport, but the height is the document height at capture time. Lazy images, expanding accordions, web fonts, animation, and ads can all change that height.
Measure what Firefox actually captured
Immediately before the screenshot, log layout and pixel values. This distinguishes a wrong configuration from a page that changed:
Rank #3
const metrics = await page.evaluate(() => ({
innerWidth: window.innerWidth,
innerHeight: window.innerHeight,
scrollWidth: document.documentElement.scrollWidth,
scrollHeight: document.documentElement.scrollHeight,
devicePixelRatio: window.devicePixelRatio
}));
console.log(JSON.stringify(metrics));
For a fixed viewport contract, assert innerWidth and innerHeight. For a full-page contract, also record scrollWidth and scrollHeight. The final PNG dimensions should be interpreted together with DPR and screenshot scale; a CSS viewport and a device-pixel bitmap are not the same unit.
Make page readiness deterministic
networkidle is useful, but it is not a universal definition of “finished.” A page can continue changing after network activity quiets. Define the state you intend to capture:
Recommended Free Tools
- Navigate with the pinned context.
- Wait for a page-specific selector that indicates the main content is rendered, or wait for an explicitly chosen delay when no reliable selector exists.
- Disable or freeze animations if visual comparisons require it.
- Ensure lazy content has been triggered before a full-page shot.
- Log the metrics, then capture.
Use the same fonts, browser build, locale, timezone, geolocation, and authentication data across workers when those inputs affect layout. A different font fallback or localized string can change line wrapping and therefore full-page height even when the viewport is identical.
Troubleshooting inconsistent screenshots
“I asked for 1920×1080, but the image is different.”
Check whether you set a native Firefox --window-size, a Playwright context viewport, and the screenshot scale. In Playwright, verify that no code later calls setViewportSize and that the context is not using viewport: null. Log the metrics shown above and inspect the image’s pixel dimensions.
“Playwright Firefox ignores my dimensions.”
Most often the dimensions were applied after navigation, or the screenshot is full-page and you are judging its height. Create the context with the viewport before page.goto, set fullPage: false for a viewport artifact, and use scale: 'css' when you expect CSS-pixel dimensions.
“The PNG is twice as large on CI.”
A DPR of 2 or scale: 'device' can double each linear dimension and substantially increase pixel count. Pin deviceScaleFactor: 1 and scale: 'css', or document the intended high-DPI contract. Also check that a CI wrapper has not selected a different host window.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errors“Only the height changes between runs.”
Confirm whether one run used full-page mode. If both did, inspect lazy images, web fonts, animations, expanding content, and resource failures. Wait for a stable selector or explicitly load the content before measuring scrollHeight.
“The screenshot looks like an older run.”
Use an explicit filename and clean the output directory, or write to a run-specific path. In DevTools, specify --filename. A stale file can hide a corrected command.
“Different workers disagree.”
Compare Firefox and Playwright versions, viewport, DPR, scale, full-page flag, locale, timezone, fonts, and page data. A single unpinned worker setting is enough to break pixel comparisons.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance, reliability, and cost decisions
- Viewport shots are usually cheaper to process and easier to compare because their dimensions are fixed.
- Full-page shots require more memory and encoding time as the document grows; impose a sensible page-length policy and investigate unexpectedly tall pages.
- Device scale increases pixel count. Use it for retina deliverables, not as an accidental default.
- Stable inputs matter as much as flags: pin browser binaries, fonts, test data, and readiness conditions.
Store the capture contract beside the test: URL, viewport, DPR, screenshot scale, full-page setting, wait condition, browser version, and expected pixel dimensions. When a failure occurs, the recorded values make it actionable rather than mysterious.
Best Value
Or skip the browser setup
ScreenshotNeo provides a single website-screenshot API call when you do not want to maintain Firefox processes and viewport plumbing. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or 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. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Use the ScreenshotNeo documentation for all options and authentication. 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)
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}`);
ScreenshotNeo includes viewport and device presets, retina scale, full-page capture with lazy images loaded, CSS-selector element capture, custom CSS and JavaScript, waits, request blocking, headers, cookies, user agents, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs, bulk capture, usage reporting, and PDF output. Every feature is on every plan. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to start.
Quick decision guide
| Choose | When it fits | Key settings |
|---|---|---|
| Native Firefox CLI | You need a small, shell-driven capture | --headless --window-size --screenshot |
| Playwright Firefox | You need waits, selectors, interactions, or test integration | Context viewport, DPR, scale, fullPage |
| ScreenshotNeo | You want an API or MCP workflow without browser infrastructure | API parameters, documented wait and viewport options |
Frequently Asked Questions
Can I guarantee identical full-page heights forever?
No. Full-page height depends on the document state, including content, fonts, lazy resources, and responsive layout. You can make the inputs and readiness condition deterministic, then detect legitimate content changes with recorded measurements.
Should visual tests use CSS or device pixels?
Use CSS-pixel output when the test compares layout across environments. Use device-pixel output only when the deliverable specifically requires high-DPI resolution, and pin the DPR.
Is a virtual display required for headless Firefox?
The native headless command and Playwright headless mode are designed to run without a visible desktop. A virtual display should not be used as an implicit source of viewport dimensions; set the dimensions explicitly.
Quick Recap
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.

