WebP is an image format from Google that stores pictures either with lossy compression based on the VP8 video codec or with a separate lossless method, supports transparency in both modes, and is decoded by all current major browsers.
For screenshots it is often the best default: smaller than PNG, sharper than JPEG at the same size on text, and with an alpha channel when you need one. This entry is part of the screenshot glossary. Its newer relative is AVIF, which compresses further but encodes more slowly.
What it means in plain terms
One .webp extension hides two different formats:
- Lossy WebP predicts each block of pixels from its neighbours and stores the difference with fewer details, like a single frame of a video. The
qualitynumber (0 to 100) sets how many details it drops. - Lossless WebP stores every pixel exactly, with tricks such as colour caches and pixel prediction. It is a direct alternative to PNG.
A chunk inside the file tells the decoder which one it is: VP8 for lossy, VP8L for lossless. Chrome's files also start with a VP8X chunk that carries an ICC colour profile.
flowchart LR P[Screenshot pixels] --> Q{quality setting} Q -- "below 100 (lossy)" --> L["VP8: block prediction + transform<br/>small, some detail lost"] Q -- "lossless" --> X["VP8L: exact pixels<br/>replaces PNG"] L --> W[".webp file<br/>optional alpha channel"] X --> W
Measured: one capture, every quality
We captured two 1280×800 pages with Chrome's own encoder (Puppeteer type: 'webp'), and encoded the PNG once more as lossless WebP with sharp for comparison.
| Output | example.com (text on flat colour) | Gradient card (HTML, large text) |
|---|---|---|
| PNG from Chrome | 81,234 bytes | 271,696 bytes |
| WebP quality 50 | 27,822 | 10,584 |
| WebP quality 75 | 32,452 | 13,016 |
| WebP quality 90 | 44,474 | 24,028 |
| WebP quality 100 from Chrome | 64,560 | 249,552 |
| WebP lossless, sharp | 26,486 | 153,168 |
Method: 2026-10-15, Apple M1 Pro, Node 22.18.0, [email protected] with Chrome for Testing 155, [email protected]. Sizes were identical in two runs. We do not report encode times; on these images they were all below 200 ms and dominated by the capture itself.
Three findings, two of them surprising:
- On a flat text page, lossless WebP was the smallest file of all, smaller than lossy quality 50. Flat colour and repeated glyphs suit the lossless method. Before you choose a lossy quality for UI screenshots, try lossless.
- On the gradient card, lossy WebP won by a wide margin. Quality 75 was about 5% of the PNG size. Smooth gradients are where lossy compression shines.
- Chrome's quality 100 is lossless, but not small. Its file at 100 contained a
VP8Lchunk, so the pixels are exact. It was still larger than sharp's lossless file on both pages, by more than twice on the text page. Chrome's encoder favours speed. If file size matters and you want exact pixels, capture PNG and encode it once with a dedicated encoder.
Transparency also survived. A circle captured with omitBackground: true and type: 'webp' kept alpha 0 in the corners and 255 in the centre.
How to get WebP
Puppeteer
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); try { const page = await browser.newPage(); await page.setViewport({ width: 1280, height: 800 }); await page.goto('https://example.com', { waitUntil: 'load' }); // Chrome encodes WebP itself; quality applies to jpeg and webp only await page.screenshot({ path: 'example.webp', type: 'webp', quality: 75 }); console.log('wrote example.webp'); } finally { await browser.close(); }
Playwright
import { chromium } from 'playwright'; const browser = await chromium.launch(); try { const page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); await page.goto('https://example.com'); // Playwright 1.64 accepts 'webp'. Its default quality for webp is 100, which is lossless. await page.screenshot({ path: 'example-pw.webp', type: 'webp', quality: 75 }); const lossless = await page.screenshot({ type: 'webp' }); console.log('wrote example-pw.webp; lossless default is', lossless.length, 'bytes'); } finally { await browser.close(); }
On our run, the quality 75 file was 32,076 bytes and the lossless default was 65,116 bytes. Many older tutorials say Playwright cannot write WebP. That was true for older releases; with an older version, capture PNG and convert it with sharp.
Tested on 2026-10-15 with Node 22.18.0, [email protected], [email protected] and [email protected].
RenderScreenshot
webp is one of the documented output formats, with quality from 1 to 100 (default 80):
import { writeFile } from 'node:fs/promises'; import { Client, TakeOptions } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY); const image = await client.take( TakeOptions.url('https://example.com').preset('og_card').format('webp').quality(80) ); await writeFile('card.webp', image);
As a GET request: /v1/screenshot?url=https%3A%2F%2Fexample.com&preset=og_card&format=webp&quality=80. For a transparent background, send "output": { "format": "webp", "transparent": true } in a POST body. The SDK snippet was not run for this page.
Common mistakes
- Choosing a lossy quality for every screenshot. Measure lossless too, especially on UI with flat colours.
- Re-encoding a lossy file. Each pass adds artefacts. Keep a PNG source if you will encode again.
- Using
quality: 100from the browser to mean "small and exact". It is exact, but not small. - WebP for
og:imagewithout testing. Link preview crawlers vary. See WebP for OG images before you switch social cards. - Wrong content type. Serve
image/webp, or some clients will not display it.
Related terms
For a full format decision, read PNG vs JPEG vs WebP for screenshots. To request WebP captures, create a free account.