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.

To convert HTML to WebP in Python, render it in a browser first, then save the browser screenshot as a WebP image. Playwright can capture the current viewport, the full scrollable page, or a selected element directly to a .webp file. If you already have a raster image such as PNG, use Pillow to encode that image as WebP instead.

Choose the right conversion path

HTML is markup, not a pixel image. Converting it means producing a rendered view of the document and then encoding those pixels. Use browser automation when the output must reflect CSS, fonts, images, or JavaScript; use an image encoder only when those pixels already exist.

Approach Input Renders HTML and JavaScript? WebP output Best fit
Playwright HTML string or live page URL Yes, in a browser Direct screenshot Web pages, including dynamic layouts and full-page captures
Pillow Existing raster image No Encodes the supplied image Converting a PNG or other supported image already rendered elsewhere
pyvips Existing image or image pipeline input No webpsave API Pipeline-oriented image processing where its save controls are useful

Playwright is the practical default for a webpage because it performs both rendering and capture. Pillow and pyvips do not turn HTML into a page image by themselves. The official documentation establishes these APIs but does not provide a comparative benchmark for speed, memory use, or output size, so choose based on your input and operational needs rather than assuming one is faster.

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

Render HTML directly to WebP with Playwright

Install Playwright and its browser

Install the Python package and a browser binary. The browser installation is a separate operational prerequisite from installing the Python module.

  1. python -m pip install playwright
  2. python -m playwright install chromium
  3. Save the example below as html_to_webp.py, then run python html_to_webp.py.

Runnable synchronous example

from playwright.sync_api import sync_playwright

html = """<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body { font-family: sans-serif; margin: 32px; }
    h1 { color: #185adb; }
  </style>
</head>
<body>
  <h1>Hello from HTML</h1>
  <p>This page is rendered by Chromium and saved as WebP.</p>
</body>
</html>"""

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1280, "height": 800})
    page.set_content(html, wait_until="load")
    page.screenshot(
        path="output.webp",
        type="webp",
        full_page=True,
        quality=85,
    )
    browser.close()

This creates output.webp in the current working directory. Playwright supports PNG, JPEG, and WebP screenshots; the screenshot type may be specified with type="webp", and it is also inferred from the .webp filename. Specifying both makes the intended format explicit. A WebP quality value of 100 is lossless; lower values are lossy, trading some image fidelity for compression. The API accepts the quality setting for lossy WebP output.

Capture a live website instead of an HTML string

For a URL, navigate the page with page.goto and wait for the content your screenshot needs. Replace the page.set_content(...) line in the example with:

page.goto("https://example.com", wait_until="load")
page.screenshot(path="page.webp", type="webp", full_page=True, quality=85)

A page’s load event does not guarantee that every font, image, or client-side component has finished changing the visible layout. If those affect the pixels, wait for the relevant state before taking the screenshot. For example, wait for a known element with page.wait_for_selector(".report-ready"), or wait for a font used by the page:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
page.evaluate("document.fonts.ready")

For a page that renders data asynchronously, wait on a selector or application-specific condition rather than relying on an arbitrary short delay. If you control the HTML string, ensure it references resources the browser can access; relative asset paths may not resolve as expected when content is supplied without a site URL.

Viewport, full-page, and element screenshots

  • Viewport: omit full_page=True to capture the visible viewport. Set its dimensions when creating the page, as in viewport={"width": 1280, "height": 800}.
  • Full page: use full_page=True to capture the full scrollable page rather than only the current viewport. Very long pages can produce large images; verify that the resulting dimensions and memory use suit your pipeline.
  • One element: locate the element and call its screenshot method. For example: page.locator("article").screenshot(path="article.webp", type="webp", quality=85). The locator must match the element you intend to capture.

To write lossless WebP, set quality=100. For lossy encoding, choose a lower quality and inspect the output for text edges, gradients, and other details important to your use case. Quality is a visual trade-off, not a promise of a particular file size.

Use the asynchronous API in an asyncio application

When the surrounding Python application already uses asyncio, use Playwright’s asynchronous API rather than blocking the event loop with the synchronous interface.

import asyncio
from playwright.async_api import async_playwright

async def main():
    html = "<html><body><h1>Hello</h1></body></html>"
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": 1280, "height": 800})
        await page.set_content(html, wait_until="load")
        await page.screenshot(
            path="output.webp",
            type="webp",
            full_page=True,
            quality=85,
        )
        await browser.close()

asyncio.run(main())

Or skip the browser setup

If you want a hosted screenshot without installing and operating a browser yourself, ScreenshotNeo is a website screenshot API and MCP server. A GET request takes a URL and returns a screenshot or PDF. For a WebP screenshot, make this request (replace the example URL with the page you need):

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request parameters. Cookie banners, newsletter popups, and chat widgets are removed before the screenshot; those cleanup steps can each be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots.

Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.

Convert an existing image to WebP with Pillow

If another tool has already rendered the page to PNG, Pillow can encode the raster file as WebP. It does not interpret HTML, CSS, or JavaScript; it only converts the image you give it.

  1. Install Pillow with python -m pip install Pillow.
  2. Save the following as png_to_webp.py.
  3. Run python png_to_webp.py after placing rendered.png in the same directory, or change the input path.
from PIL import Image

with Image.open("rendered.png") as im:
    im.save("output.webp", "WEBP", quality=85, method=6)

Pillow’s WebP save options include lossless, quality, alpha_quality, method, and exact. The documented quality range for lossy encoding is 0–100. Use lossless=True when preserving image data is more important than minimizing the encoded file; for lossy output, select quality based on visual inspection. Preserve transparency when the source has an alpha channel and your output requires it, then check the resulting image in the target viewer or application.

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

Use pyvips for a pipeline-oriented WebP save

pyvips exposes a webpsave operation with controls including quality (Q), lossless, near_lossless, effort, and target_size. It is an option when images already flow through a pyvips pipeline and those encoding controls suit the job. The documented controls do not establish that pyvips is faster or uses less memory than the alternatives in a particular workload, so measure with your own image sizes and deployment environment before making a capacity decision.

Troubleshoot common conversion failures

Playwright cannot launch Chromium

The Python package can be installed even when its browser binary is missing. Run python -m playwright install chromium in the same environment used by the script. In container or restricted server environments, also check the browser’s system-level runtime requirements and permissions.

The output is blank or missing late content

The page may not have rendered its content when the screenshot ran. Confirm navigation or set_content completed, then wait for the specific selector, font, or application state that affects the image. Inspect the page content or take a temporary screenshot to determine whether the problem is rendering or file encoding.

Images, fonts, or styles are absent

Check that resource URLs are reachable from the browser and that the page has had time to load them. HTML passed to set_content may lack the base URL needed to resolve relative paths. Use absolute resource URLs or navigate to a page whose origin and paths provide the expected context.

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

The screenshot is only the visible section

Set full_page=True for the full scrollable document. If you intended only the viewport, confirm the viewport size instead. For a specific region, use a locator’s screenshot method and verify that the selector identifies the intended element.

The WebP file is unexpectedly large or looks degraded

Lossy quality settings affect image fidelity and output size, but there is no universal quality value that guarantees a size or appearance. Compare a few quality settings against the actual page and inspect small text, edges, and transparency. Use lossless output when lossy artifacts are unacceptable; expect that the result may be larger.

Pillow reports an unsupported WebP operation

Confirm that Pillow is installed in the Python environment running the script and that the input can be opened. Pillow’s documentation supports WebP read and write, but the specific installed build and environment determine which capabilities are available. Check the installed package and update it through your normal dependency process if needed.

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

Performance, reliability, and cost considerations

Running Playwright means provisioning browser binaries and the resources needed to launch them. For repeated captures, avoid launching a fresh browser for every individual page when your application can safely reuse a browser process; ensure pages and browser contexts are closed so state and resources do not accumulate. The appropriate concurrency depends on page complexity and available machine resources. No authoritative benchmark establishes a universal throughput or memory figure for these approaches.

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

For reliable output, make readiness conditions explicit, use stable selectors, and handle navigation or capture exceptions in the calling application. Decide whether a timed-out page should fail, retry, or be recorded as incomplete. Treat the screenshot as an artifact of a particular viewport, device scale, page state, browser version, and resource availability; changing those inputs can change the pixels.

Self-hosted Playwright avoids an API charge per capture but requires browser installation, runtime capacity, and operational handling. A hosted API shifts browser operation to the service and may charge according to its plan rules. For ScreenshotNeo, only clean shots are billed; its response includes X-Page-Verdict and X-Billed headers. Compare the approach that fits your need for control, deployment simplicity, and billing visibility.

Frequently asked questions

Can I convert HTML to WebP without saving a PNG first?

Yes. Playwright can render the HTML and write the screenshot directly as WebP; an intermediate PNG is not required.

Does Pillow convert an HTML file to WebP?

No. Pillow encodes raster images. Render the HTML first with a browser such as Playwright, then use Pillow only if you need to convert or further process the resulting image.

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

Which Python API should I use in an asyncio project?

Use Playwright’s asynchronous Python API when the application already runs an asyncio event loop. A synchronous script can use the sync API.

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.