Free tools Windows power users keep installed
One-click scans. No signup required.
Use Playwright when your HTML must look like a browser page: install the Python package and Chromium, load the markup, then call page.screenshot(). It executes JavaScript, applies browser CSS, and can save a full page, a single element, or image bytes. Use WeasyPrint instead when you need document-style pagination and PDF-like layout without a browser. The right choice depends on your CSS and JavaScript, not on an assumed speed or fidelity ranking.
Choose the renderer before writing code
| Requirement | Best fit | Reason and limitation |
|---|---|---|
| Browser CSS, JavaScript, web fonts, dynamic widgets | Playwright | Runs a real Chromium browser. You control viewport, device scale, waits and page state. Browser binaries add installation and deployment size. |
| Full-page website screenshot | Playwright | full_page=True captures the complete page, including content below the initial viewport. |
| One visible component | Playwright locator screenshot | Captures a stable CSS-selected element. Covered content is not visible, and a scrollable element contributes only what is currently scrolled into view. |
| In-memory image processing | Playwright without a path | Returns image bytes that you can send to storage, a queue or an imaging library. |
| Print-oriented HTML and paginated documents | WeasyPrint | Its Python API lays out and paginates HTML. Check support for the HTML/CSS you use and provide a base URL for relative assets. |
The official documentation does not provide a controlled Playwright-versus-WeasyPrint speed or visual-fidelity benchmark, so validate both against your actual document.
Generate a browser-rendered PNG with Playwright
1. Install Python and browser dependencies
Create a virtual environment, install Playwright, then download its browser binaries:
python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell: .venvScriptsActivate.ps1
pip install playwright
playwright install
The package and browser download are separate steps. In a container or deployment image, include the browser binaries and the operating-system libraries required by Chromium. The Playwright library setup guide documents synchronous and asynchronous APIs and installation.
#1 Best Overall
2. Load HTML and save a complete page
This runnable script renders a self-contained document and writes a PNG:
from playwright.sync_api import sync_playwright
html = """
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body { margin: 0; font-family: Arial, sans-serif; }
.card { width: 720px; padding: 40px; background: #f4f7fb; }
h1 { color: #12345b; }
</style>
</head>
<body>
<main class="card">
<h1>Hello from HTML</h1>
<p>This page becomes a PNG.</p>
</main>
</body>
</html>
"""
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 900, "height": 700})
page.set_content(html, wait_until="load")
page.screenshot(path="output.png", full_page=True)
browser.close()
Playwright’s screenshot API supports PNG, JPEG and WebP. Use full_page=True for the entire document; omit it to capture only the viewport. The official screenshot documentation also documents quality controls for JPEG/WebP, CSS-pixel versus device-pixel scale, transparent backgrounds where the image type permits them, and returning bytes instead of writing a file.
3. Capture a single component
Use a stable locator when you need a card, chart or header rather than the whole page:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_content('<div class="header" style="padding:30px">Header</div>')
page.locator(".header").screenshot(path="header.png")
browser.close()
The locator must resolve to a visible, stable target. Playwright scrolls the element into view, but an overlay can cover pixels and a scrollable container is captured only at its current scroll position. If the component changes size after loading, wait for a selector, a state change or a measured condition before capturing.
4. Return bytes for an in-memory pipeline
from pathlib import Path
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_content('<h1>In memory</h1>')
image_bytes = page.screenshot(type="webp", quality=85)
Path("output.webp").write_bytes(image_bytes)
browser.close()
Bytes can instead be passed directly to an object-storage client, an HTTP response or Pillow. Do not set JPEG quality when requesting PNG; quality is relevant to JPEG and WebP.
Rank #2
Control fonts, assets and dynamic content
Wait for the page you actually want
page.set_content() returns after the requested load state, but JavaScript, images and web fonts may still be changing. For a page with a known ready marker:
page.set_content(html)
page.locator("#report-ready").wait_for(state="visible")
page.screenshot(path="report.png", full_page=True)
For network-loaded assets, wait for the relevant locator or use a deliberate short delay only when there is no observable readiness signal. A fixed delay is easy to understand but can be either wasteful or too short on a busy machine.
Make output reproducible
- Pin the Playwright package and browser version in your deployment process.
- Set an explicit viewport and, when needed,
device_scale_factorso pixel dimensions do not depend on the host. - Install the same fonts everywhere; a missing font changes wrapping and therefore the image height.
- Make external assets available and stable. For HTML strings, use absolute URLs or a controlled origin for images, stylesheets and fonts.
- Freeze dynamic state such as clocks, randomized content and live data before capture.
Identical source HTML does not guarantee identical pixels across machines when browser versions, fonts, viewport, assets or dynamic state differ.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesSet a dark theme or transparent background
Create the page with the desired CSS (for example, a dark color scheme), or add a class before the screenshot:
page.add_style_tag(content="body { background: #111; color: #eee; }")
page.screenshot(path="dark.png")
Transparent output is supported for applicable image types when the page background is transparent. Ensure your CSS does not paint an opaque body background if you need alpha.
Use WeasyPrint for document-style output
WeasyPrint is a Python document renderer rather than a browser automation tool. Its HTML API accepts a string, URL, filename or file object; render() lays out and paginates the document. A minimal PNG-oriented workflow is:
from weasyprint import HTML
html = """
<html>
<body>
<h1>Invoice</h1>
<p>A print-oriented document.</p>
</body>
</html>
"""
document = HTML(string=html, base_url=".").render()
# WeasyPrint is primarily a paged-document renderer; write PDF pages
# or use its documented document/page APIs for your chosen image workflow.
document.write_pdf("invoice.pdf")
When HTML is supplied as a string, pass base_url if it contains relative images, stylesheets or fonts. Confirm that the CSS and HTML features in your document are supported by your installed WeasyPrint version. Its documentation notes that long documents or specially crafted HTML can take a long time to render, so workload and asset complexity determine performance.
If the required deliverable is specifically a raster image of a browser application, Playwright is usually the direct path. If the deliverable is a paginated report, label sheet or invoice and JavaScript is unnecessary, evaluate WeasyPrint’s layout model first.
Common failures and fixes
“Executable doesn’t exist” or browser launch errors
Install the browser binaries with playwright install in the same environment that runs the script. In minimal Linux images, install the system dependencies listed by your Playwright setup process and verify that the runtime user can execute the browser.
The screenshot is blank or missing images
- Use absolute asset URLs or set the correct
base_urlfor document renderers. - Wait for a visible, application-specific ready selector rather than capturing immediately.
- Check that the process can reach the asset host and that authentication cookies or headers are present.
- Inspect the page in headed mode while debugging to see console errors and failed requests.
Fonts or layout differ between development and production
Install and pin the same font files, browser version and viewport. Avoid relying on a developer’s local font fallback. Capture only after web fonts have loaded if text metrics matter.
An element screenshot is clipped
Confirm the locator matches the intended visible element. Remove overlays, scroll the target to the desired position, and remember that a scrollable container’s screenshot reflects its currently visible scroll area rather than all of its overflow content. Use a full-page screenshot when the entire document is required.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Long pages take too long or consume too much memory
Reduce unnecessary DOM and image size, capture a component when a component is all you need, and avoid unbounded waits. For large document workloads, measure your real inputs; no comparative speed figure is established by the cited documentation.
Performance, reliability and output decisions
- Startup: launching a browser and loading binaries has a larger footprint than a pure string-to-layout library. Reuse a browser process for batches, while creating isolated pages for separate jobs.
- Page state: deterministic waits and fixed viewport settings improve repeatability more than arbitrary sleep calls.
- Format: PNG preserves lossless detail; JPEG and WebP can reduce size and expose quality controls. Choose based on downstream storage and visual requirements.
- Security: treat untrusted HTML and URLs as untrusted input. Restrict network access, avoid exposing credentials to page scripts, and isolate browser jobs where appropriate.
- Validation: compare representative outputs from your production operating system, fonts, browser version and asset hosts before relying on pixel-level snapshots.
Or skip the browser setup
ScreenshotNeo is a hosted screenshot API and MCP server. A single GET request returns PNG, JPEG, WebP or PDF, so your Python service does not need to install or maintain browser binaries. Cookie and consent banners are accepted and removed before capture, along with 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. Its MCP tools—take_screenshot, get_page_info and capture_pdf—let Claude, Cursor and other MCP clients request captures.
Python example (see the ScreenshotNeo API documentation):
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)
The equivalent commands are:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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 full-page and element capture, 12 device presets plus custom viewports, retina scale, dark mode, lazy-image loading, custom CSS and JavaScript, clicks, waits, ad/tracker/request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture for 100 URLs per call, a usage API and an OpenAPI specification. Parameters commonly used by other screenshot APIs also work.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; all features are available on every plan. Sign up free to start without a card.
Best Value
FAQ
Can Playwright save a screenshot without creating a file?
Yes. Omit the path argument; page.screenshot() returns image bytes.
Does full-page capture include content below the fold?
Yes, set full_page=True. For a single element, capture behavior follows that element’s visibility and scroll position.
Is WeasyPrint a drop-in replacement for a JavaScript-heavy page?
No. It is intended for document layout and pagination. Test the HTML and CSS features your document requires before switching from a browser renderer.
Recommended Free Tools
Frequently Asked Questions
Can Playwright save a screenshot without creating a file?
Yes. Omit the path argument; page.screenshot() returns image bytes.
Does full-page capture include content below the fold?
Yes, set full_page=True. For a single element, capture behavior follows that element’s visibility and scroll position.
Is WeasyPrint a drop-in replacement for a JavaScript-heavy page?
No. It is intended for document layout and pagination. Test the HTML and CSS features your document requires before switching from a browser renderer.
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.

