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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Set the browser dimensions explicitly before navigation, verify both the WebDriver-reported window and the page’s CSS viewport, then save the screenshot. In Selenium Python, driver.set_window_size(width, height) controls the requested browser window size, while window.innerWidth and window.innerHeight tell you the dimensions that responsive page code actually sees. Those values, and the final PNG pixel dimensions, can differ by browser, operating system, headless mode, scaling, and driver implementation, so reproducible captures require checking all three.

What “consistent size” means in Selenium

A screenshot workflow has three dimensions that are easy to confuse:

  • WebDriver window dimensions: the width and height Selenium reports for the browser window.
  • CSS viewport dimensions: window.innerWidth and window.innerHeight, which determine responsive breakpoints and layout decisions inside the page.
  • Output image dimensions: the pixel width and height of the PNG written to disk.

WebDriver’s screenshot command captures the visual viewport of the top-level browsing context, not necessarily the outer window rectangle. Selenium’s API documents set_window_size(width, height) as setting dimensions in pixels, but it does not promise that a request such as 1280 × 900 produces a 1280 × 900 PNG on every browser and host. Validate the output file when exact pixels matter. See the W3C WebDriver screenshot semantics and Selenium’s Chromium Python API.

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

Complete Python example

The following script sets the size before loading the URL, prints both window and viewport measurements, waits for a document-ready state, and saves a PNG.

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait

TARGET_URL = "https://example.com"
WIDTH = 1280
HEIGHT = 900

options = webdriver.ChromeOptions()
# Add the headless option supported by the Chrome version installed in your environment.
# options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
try:
    driver.set_window_size(WIDTH, HEIGHT)

    print("WebDriver window:", driver.get_window_size())
    print("Window rect:", driver.get_window_rect())

    driver.get(TARGET_URL)
    WebDriverWait(driver, 30).until(
        lambda d: d.execute_script("return document.readyState") == "complete"
    )

    viewport = driver.execute_script(
        "return {width: window.innerWidth, height: window.innerHeight, "
        "devicePixelRatio: window.devicePixelRatio}"
    )
    print("CSS viewport:", viewport)

    driver.save_screenshot("screenshot.png")
finally:
    driver.quit()

save_screenshot(path) writes a PNG and returns a success value. Selenium also exposes get_screenshot_as_file(path) and get_screenshot_as_png() for saving or processing bytes; these methods are documented in the Remote WebDriver API.

Step-by-step procedure for repeatable captures

1. Choose the target viewport

Define the dimensions your test, visual baseline, or documentation requires, for example 1280 × 900 CSS pixels. Treat this as a project input rather than relying on a developer desktop’s current resolution.

2. Fix the environment

Record the browser and driver versions, operating system or container image, Selenium version, headless or headed mode, font installation, device scale settings, and target dimensions. Different fonts, GPU paths, browser builds, and window managers can alter line wrapping and anti-aliasing even when the nominal viewport is identical.

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

3. Resize before navigation

Call set_window_size() immediately after creating the driver and before get(). Responsive sites may choose a layout during initial loading. If you resize after navigation, reload the page so media queries and scripts recalculate under the intended conditions.

4. Confirm Selenium’s result

Use get_window_size() or get_window_rect() to see what the driver accepted. A window manager may clamp or adjust a requested size, particularly in headed sessions.

5. Confirm the page viewport

Execute JavaScript and record window.innerWidth and window.innerHeight. These are the values page CSS and JavaScript use for breakpoints. Also record window.devicePixelRatio when comparing raster output.

6. Wait for the visual state you need

document.readyState == "complete" only indicates that the document load event has completed. Images loaded lazily, client-rendered components, fonts, animations, and data requests may still change the pixels. Add an explicit wait for a meaningful selector, a network-idle strategy in your test harness, or a short, documented delay. Disable or freeze animations when visual diffs require stable frames.

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

7. Save and inspect the file

Call driver.save_screenshot("screenshot.png"). If a pipeline requires exact dimensions, inspect the PNG metadata with an image library and fail the build when the dimensions do not match the expected contract. Do not infer file size from the requested outer window size.

Why 1280 × 900 may not produce a 1280 × 900 PNG

set_window_size() addresses the browser window. In a headed session, browser chrome and operating-system decorations consume part of that rectangle, leaving a smaller content viewport. Headless implementations can apply their own window and viewport behavior. Device pixel ratio or browser scaling can map CSS pixels to a different number of physical pixels. WebDriver’s screenshot endpoint captures the visual viewport, so the resulting PNG can differ from both the outer rectangle and CSS dimensions.

For a deterministic process, store the three measurements alongside the image: get_window_size(), JavaScript viewport values, and the PNG’s actual width and height. If they differ from your contract, adjust the environment or use a browser-specific emulation method instead of silently accepting a mismatch.

When to use Chrome DevTools Protocol emulation

Chromium’s DevTools Protocol (CDP) provides direct control over device metrics. The Emulation domain documents Emulation.setDeviceMetricsOverride, which can override width, height, mobile emulation, device scale factor, and related CSS media-query values.

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.
from selenium import webdriver

options = webdriver.ChromeOptions()
# options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
    driver.execute_cdp_cmd(
        "Emulation.setDeviceMetricsOverride",
        {
            "width": 1280,
            "height": 900,
            "deviceScaleFactor": 1,
            "mobile": False,
        },
    )
    driver.get("https://example.com")
    print(driver.execute_script(
        "return [window.innerWidth, window.innerHeight, window.devicePixelRatio]"
    ))
    driver.save_screenshot("cdp-screenshot.png")
finally:
    driver.quit()

This is Chromium-specific and less portable than standard WebDriver sizing. Use it when you need device metrics or scale-factor control and can accept a Chrome dependency. Do not present CDP settings as a cross-browser Selenium recipe.

Full-page and element screenshots: size caveats

Standard WebDriver screenshots represent the current visual viewport. A full-page image may require browser-specific support, scrolling and stitching, or a separate capture tool. Stitching introduces seams, sticky-header duplication, lazy-loading races, and layout changes between scroll positions. If you only need one component, locate it and use Selenium’s element screenshot method where supported; its dimensions are the element’s rendered box, not the browser window.

For either approach, make the page state deterministic first: wait for images, set a consistent viewport, hide transient overlays, and freeze animations. Record whether the capture is viewport, element, or stitched full-page so later comparisons use equivalent outputs.

Common failures and fixes

The requested size is ignored or changed

Cause: a window manager, remote display, or headless implementation altered the outer rectangle.

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.

Fix: print get_window_size() and get_window_rect(), then check JavaScript viewport values. Run in a controlled container or use CDP metrics for Chromium when exact viewport control is required.

The layout is mobile despite a desktop window

Cause: the CSS viewport is narrower than the requested window, mobile emulation is enabled, or a device scale setting changed media-query behavior.

Fix: inspect window.innerWidth, window.matchMedia(...), user-agent settings, and CDP emulation state. Set the intended metrics before navigation and reload.

The PNG dimensions differ between machines

Cause: device pixel ratio, browser scaling, OS display settings, fonts, or browser builds differ.

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

Fix: pin the browser/container, install identical fonts, record devicePixelRatio, and validate the file dimensions. A fixed WebDriver size alone cannot guarantee bit-for-bit identity across hosts.

The screenshot is blank or incomplete

Cause: capture occurred before client rendering, lazy images, fonts, or API data finished; a navigation or timeout may also have left an error page.

Fix: wait on a specific content selector, verify the URL and document state, wait for image completion where relevant, and capture again. Save browser logs and a diagnostic screenshot on failure.

Overlays or cookie dialogs cover the page

Cause: consent banners, newsletters, chat widgets, or test fixtures are part of the rendered page.

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

Fix: dismiss them through the UI or hide known selectors in test setup. Keep that behavior explicit so the same rules run in every environment.

Visual diffs change even with matching measurements

Cause: animations, rotating content, current timestamps, ads, network responses, font loading, or nondeterministic data.

Fix: stub dynamic APIs, disable animations, use fixed test data, wait for fonts and images, and compare captures from the same browser build and environment.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Practical reliability and cost considerations

  • Use a single source of truth: define width, height, browser mode, and scale settings in configuration rather than scattered constants.
  • Log measurements: include window size, viewport size, device pixel ratio, URL, browser version, and timestamp with each artifact.
  • Fail loudly: treat a viewport or PNG-dimension mismatch as a test failure when exact output matters.
  • Control timing: prefer selector-based readiness checks over arbitrary long sleeps, adding a bounded timeout for failures.
  • Keep screenshots private: pages may contain credentials, personal data, or tokens in URLs; secure artifact storage and redact sensitive query strings.
  • Plan for remote execution: remote WebDriver nodes can have different fonts, display servers, and browser versions than local development machines.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server when you want a rendered URL without maintaining Selenium, Chrome, drivers, or a display server. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. 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.

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

One GET request returns PNG, JPEG, WebP, or a PDF. The API supports full-page captures with lazy images loaded, CSS-selector element shots, dark mode, device presets or custom viewports, retina scale, PDF paper and page controls, HTML/CSS input, custom JavaScript and CSS, clicks, selector waits, delays, network-idle waits, request and resource 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. Its parameter names are compatible with those used by many screenshot APIs, which can simplify migration.

Use the ScreenshotNeo documentation for the full option list. Minimal calls are:

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 provides 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, with every feature on every plan. Create a free ScreenshotNeo account.

Frequently Asked Questions

Should I call set_window_size before or after driver.get()?

Call it before navigation so the initial responsive layout is evaluated at the intended dimensions. If you resize afterward, reload before capturing.

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

Can Selenium guarantee identical pixels on different computers?

No. Matching requested dimensions does not normalize browser builds, fonts, operating systems, device scale, timing, or dynamic content. Pin the environment and validate the viewport and PNG.

What is the difference between get_window_size() and window.innerWidth?

get_window_size() reports the WebDriver browser window dimensions. window.innerWidth reports the page’s CSS viewport, which is the value responsive layout code uses.

When is CDP preferable to ordinary Selenium sizing?

Use CDP’s Emulation.setDeviceMetricsOverride when Chromium-only device metrics, mobile emulation, or device-scale control is required. It is not a portable cross-browser command.

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.

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