ScreenshotNeo

BlogGuides

Why Does My HTML to Image Render Differently from the Browser?

HTML-to-image output depends on the renderer, viewport, device scale and page state. Match those inputs first to find why it differs from your browser.

By the ScreenshotNeo team4 October 20267 min read

HTML-to-image output is produced under a renderer and capture configuration that may differ from your interactive browser. The first things to match are the browser engine and version, viewport, device emulation, device-pixel ratio, screenshot scale, and page state. Without the renderer and its settings, there is no single cause to diagnose.

Start with the same page and a known browser engine. Set the same viewport dimensions, emulate the same device context, and compare captures at CSS-pixel and device-pixel scale. Then make sure both captures happen after the page has reached the same stable state.

What makes two captures differ?

Variable What it affects What to compare
Browser engine and version How browser features and layout are rendered Engine name and comparable version in both environments
Viewport Responsive breakpoints, line wrapping, and visible area Viewport width and height in CSS pixels
Device emulation Screen dimensions, mobile behavior, touch, user agent, and scale factor Emulated screen and device settings
Device-pixel ratio and zoom Relationship between CSS pixels and physical pixels window.devicePixelRatio and browser zoom
Screenshot scale Output pixel dimensions and apparent sharpness CSS-pixel scale versus device-pixel scale
Capture region Whether output covers the viewport or the full page Viewport capture versus full-page capture
Page state Animation, asynchronous content, and changing elements Wait conditions and whether the page is stable at capture time

These are diagnostic variables, not settings that every HTML-to-image tool necessarily exposes. Playwright documents screenshot scale and capture region, and its browser contexts expose viewport and device emulation controls. MDN explains that devicePixelRatio is the ratio of physical pixels to CSS pixels, and that page zoom can affect it. See the Playwright screenshot API, Playwright emulation guide, and MDN devicePixelRatio reference.

Reproduce the comparison with Playwright

If your converter’s renderer is unclear, use Playwright to create a controlled reference capture. The example below uses Chromium, fixes the viewport, waits for a page element, and writes one viewport screenshot at CSS-pixel scale and another at device-pixel scale. Install Playwright and its browser first:

npm install -D playwright
npx playwright install chromium
// compare.mjs
import { chromium } from 'playwright';

const url = process.argv[2] ?? 'https://example.com';
const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 2,
});
const page = await context.newPage();

try {
  await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 });
  await page.screenshot({ path: 'capture-css.png', scale: 'css' });
  await page.screenshot({ path: 'capture-device.png', scale: 'device' });
  console.log({
    viewport: page.viewportSize(),
    devicePixelRatio: await page.evaluate(() => window.devicePixelRatio),
  });
} finally {
  await browser.close();
}

Run it with node compare.mjs https://your-page.example. At a 1440 by 900 CSS-pixel viewport, the CSS-scale image uses one image pixel per CSS pixel. With device scale factor 2, device-scale output can be twice as wide and tall. That changes the pixel dimensions and sharpness; it does not by itself mean the page layout is wrong. Playwright documents the scale behavior in its screenshot options.

Capture full page or a specific element

To compare a full-page result, set fullPage: true. To capture a particular element, use a locator screenshot. Keep the capture region consistent when comparing tools:

await page.screenshot({ path: 'full-page.png', fullPage: true, scale: 'css' });
await page.locator('main').screenshot({ path: 'main.png', scale: 'css' });

A full-page image will naturally have different dimensions from a viewport screenshot. An element screenshot is cropped to the element, so compare it with the same element and not with the whole browser viewport.

Hold page state steady

For pages with animation or changing content, wait for a meaningful condition rather than relying only on elapsed time. If the page has a stable selector that signals readiness:

await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.locator('[data-render-ready="true"]').waitFor({ state: 'visible' });
await page.screenshot({ path: 'ready.png', scale: 'css' });

For visual comparisons, Playwright screenshot assertions also support pixel and color difference tolerances and injected styles. You can use styles to suppress a known animation or hide a timestamp that changes on every load. Do this only when the style is part of the comparison setup, since injected CSS can also change the appearance being investigated. See Playwright visual comparisons.

Diagnose the mismatch in a fixed order

  1. Record the renderer. Write down the conversion tool, browser engine, and version. Confirm whether it uses a browser engine and whether that can be configured.
  2. Match the viewport. Set identical width and height in CSS pixels. Responsive layouts can switch breakpoints when even one dimension differs.
  3. Match emulation. Compare screen size, device scale factor, mobile and touch settings, and user agent where the tool exposes them. Playwright documents these context settings in its emulation guide.
  4. Check zoom and pixel ratio. Read window.devicePixelRatio in the visible browser and the capture context. MDN notes that page zoom changes this value; browser zoom and device scale can therefore invalidate a sharpness comparison.
  5. Compare output scale and dimensions. Confirm whether each capture uses CSS pixels or device pixels. Also check whether one is full-page and the other viewport-only.
  6. Stabilize the page. Wait for the relevant content or selector, and account for animations and changing data. Repeat the capture to see whether the mismatch is reproducible.
  7. Inspect resources and fonts. If dimensions match but typography or layout still differs, check which fonts and other resources loaded in each environment, then inspect the converter’s resource-loading behavior and launch settings.

Keep a small record for each comparison: URL or HTML input, engine/version, viewport, emulation settings, zoom or pixel ratio, scale, capture region, and readiness condition. Changing one variable at a time makes the cause easier to isolate.

Common symptoms and fixes

Symptom Likely explanation What to try
Image is larger in pixels but looks like the same layout Device-pixel scale or a larger device scale factor Capture at CSS scale, or compare dimensions after accounting for device scale.
Text wraps differently or components move Viewport, emulation, zoom, or responsive breakpoint differs Match viewport and device settings; check page zoom and devicePixelRatio.
Only the bottom of the page is missing One capture is viewport-only and the other is full-page Use the same capture region in both tools.
Repeated captures vary Animation, dynamic content, or a page that was captured before it was ready Wait for a stable selector or state; use repeatable screenshot comparison controls if available.
Layout matches but text looks different Different renderer version or different fonts/resources loaded Check engine/version and loaded font and resource availability.
Tool rejects a screenshot option The converter may not expose the same API or option names Check that tool’s documentation; Playwright options are not universal.
Network-idle wait times out The page may keep connections active or never reach network idle Wait for a specific element or use a deliberate delay suited to the page instead of treating network idle as a universal readiness signal.

Performance, reliability, and cost

For reliable comparisons, make the capture conditions explicit and repeatable. A fixed viewport, known engine, stable readiness condition, and chosen scale make results easier to reproduce. Full-page screenshots can produce larger files and require more page content to be laid out; use viewport or element captures when those match the question you are debugging.

When using a hosted conversion service, check its renderer and configuration options, output format and scale controls, timeout behavior, and pricing model before building a workflow around it. This research does not establish a universal performance or cost ranking for HTML-to-image tools. A service may also handle browser setup and capture infrastructure for you, but its output still reflects its rendering environment and settings.

Or skip the browser setup

ScreenshotNeo turns a URL into an image with one GET request. Its capture options include viewport sizing and retina scale, and the API documents the request options at ScreenshotNeo API documentation. For example, save a WebP capture of a page:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Learn more at ScreenshotNeo, then sign up for 1,000 free screenshots a month, no card required.

FAQ

Does a sharper image mean the layout is more accurate?

No. Sharpness often reflects output scale and device-pixel ratio. Verify layout at the same CSS viewport and compare pixel dimensions separately.

Should I always wait for network idle?

No. Some pages keep network activity open. Waiting for a page-specific ready element can be more dependable for that page.

Can I compare captures from different browser engines?

You can, but engine differences become another variable. For diagnosis, first compare captures from the same engine and version, then test the other engine if needed.

What details should I include when asking for help?

Provide the converter name, renderer and version if known, viewport, scale, output dimensions, page zoom or pixel ratio, capture region, and a minimal reproducible URL or HTML example.