PNG vs JPEG vs WebP Screenshots: Measured Sizes

PNG vs JPEG vs WebP screenshots, measured on five page types at 1x and 2x: file size, encode time, text artifacts, and which format to pick for each job.

By Simon Chiu · Updated

Disclosure: I build RenderScreenshot, a screenshot API that outputs all three formats. Nothing on this page depends on our API: every number below comes from headless Chrome on my machine, and the script is included so you can repeat it.

Short answer: for screenshots of user interfaces, docs and dashboards, WebP at quality 80 was the smallest format on every UI, docs and dashboard page we tested, at 26% to 46% of the PNG size. JPEG saved very little on those pages, and at quality 90 it was sometimes larger than PNG. PNG is the right choice when you need exact pixels (visual diffs, pixel art, small text you will zoom into). JPEG still wins on one case: noisy, photo-like content at 1x, where it beat WebP by a small margin.

This page is part of our screenshot tool and format comparisons. The rest of it shows the data, the artifacts, and a decision table.

What we measured

Data from the October 2026 run. Tested on 2026-10-15 with Node 22.18.0, [email protected] (Chrome for Testing 154.0.8037.57, headless), macOS on an Apple M1 Pro.

  • Pages: five targets that stress encoders in different ways.
    • example.com: almost empty page, flat background, one paragraph.
    • docs: a documentation page with headings, a code block and a 1 px border table.
    • dashboard: cards, an SVG area chart, box shadows, a gradient header.
    • photo: a 1280x800 canvas with gradients, layered shapes and film-like grain (a stand-in for a hero photo).
    • og-card: a 1200x630 social card with two radial gradients and large text.
  • Encodes: PNG, JPEG at quality 80 and 90, WebP at quality 80 and 90, all produced by Chrome itself through page.screenshot().
  • Scales: device scale factor 1 and 2 (2 is what you get for "retina" output).
  • Runs: 5 captures per variant on a page that was already loaded. The time column is the median capture time, so it is mostly encode time plus the screenshot round trip. Bytes were identical across runs.

The four local pages were served from 127.0.0.1, so network speed does not affect the numbers. The full script is at the end of the page.

Results at 1x

Page PNG JPEG q80 JPEG q90 WebP q80 WebP q90
example.com 81.2 KB 59.1 KB 75.4 KB 36.0 KB 44.5 KB
docs 96.5 KB 77.9 KB 98.0 KB 43.3 KB 53.3 KB
dashboard 69.1 KB 42.8 KB 55.8 KB 19.7 KB 25.9 KB
photo 1,363.4 KB 191.6 KB 333.9 KB 207.7 KB 332.7 KB
og-card (1200x630) 380.8 KB 42.5 KB 61.3 KB 20.5 KB 30.0 KB

KB here means 1,000 bytes.

Results at 2x

Page PNG JPEG q80 JPEG q90 WebP q80 WebP q90
example.com 188.9 KB 161.4 KB 204.4 KB 85.4 KB 106.3 KB
docs 216.8 KB 200.5 KB 251.0 KB 100.2 KB 124.4 KB
dashboard 184.5 KB 111.9 KB 149.8 KB 48.1 KB 63.8 KB
photo 3,830.6 KB 489.7 KB 727.8 KB 240.6 KB 478.3 KB
og-card (2400x1260) 1,173.9 KB 98.2 KB 151.1 KB 44.0 KB 68.4 KB

Median capture time (ms)

Page Scale PNG JPEG q80 WebP q80
docs 1x 49 34 76
dashboard 1x 50 34 83
photo 1x 158 34 123
og-card 1x 155 34 67
docs 2x 90 50 207
photo 2x 852 59 316
og-card 2x 432 47 182

What the numbers say

1. JPEG is a poor fit for interface screenshots. On the docs page, JPEG at quality 90 was 98.0 KB against 96.5 KB for PNG at 1x, and 251.0 KB against 216.8 KB at 2x. You pay the lossy artifacts and get a bigger file. JPEG only pulls ahead of PNG when the page has gradients, photos or noise.

2. Gradients are where PNG falls apart. The og-card is mostly smooth color. As PNG it was 380.8 KB, about 18 times the WebP q80 file and 9 times the JPEG q80 file. If you generate social cards with gradient backgrounds and save them as PNG, you are probably shipping files several times larger than they need to be. Our OG image file size guide covers the platform limits that make this matter.

3. WebP is not a universal win. On the noisy photo page at 1x, WebP q80 (207.7 KB) was slightly larger than JPEG q80 (191.6 KB), and at q90 the two were equal. At 2x the picture flips: WebP q80 was less than half the JPEG size. Grain and fine noise are expensive for WebP's lossy mode at low resolution, so test with your real images.

4. Encode time follows file complexity. JPEG was the fastest encode in every case (33 to 66 ms). PNG was fast on flat pages and slow on the gradient and photo pages (852 ms for the 2x photo). Chrome's WebP encoder was consistently slower than JPEG, roughly 2 to 5 times. For one screenshot this does not matter. In a queue that renders thousands of 2x captures, it is CPU you pay for.

5. Retina doubles more than the pixels. Going from 1x to 2x multiplied file size by about 2.2x to 2.7x for PNG and WebP on the UI pages. Before you default to scale 2, check whether the consumer will downscale it anyway.

What the artifacts look like

Byte counts hide quality. These crops are the code block from the docs page at 1x, enlarged 3x with nearest-neighbour scaling so you see real pixels. Each crop was saved as lossless WebP so this page adds no new artifacts.

PNG crop of a monospace code block at 3x zoom: letter edges are clean and the gray background is a single flat color
PNG, 96.5 KB for the full page. Exact pixels: clean glyph edges, flat background.
JPEG quality 80 crop of the same code block: faint ringing and blocky noise around each character and on the background
JPEG q80, 77.9 KB for the full page. Ringing around characters and faint 8x8 block noise in the flat gray.
WebP quality 80 crop of the same code block: characters are slightly soft with color fringing, and the background stays smooth
WebP q80, 43.3 KB for the full page. Softer edges with slight color fringing, but a cleaner background than JPEG.

At normal size, most people will not see the difference between PNG and WebP q80 on this page. At 3x zoom, both lossy formats damage thin strokes. That is why the decision depends on what happens to the image next.

Decision table

Your use Pick Why
Visual regression testing, pixel diffs PNG Lossy encoding adds artifacts that shift when the encoder or its settings change, which creates false diffs.
Docs, UI and dashboard screenshots on your own site WebP q80 Smallest file on every UI page we tested; artifacts are hard to see at 1:1.
Open Graph and social cards PNG or JPEG, or WebP if you control the consumer The safe choice for third-party crawlers is a widely supported format. See WebP support for OG images. If you use PNG, avoid large gradients or switch to JPEG.
Photo-heavy pages (hero images, galleries) at 1x JPEG q80 Beat WebP on our noisy photo page at 1x and encodes fastest.
Photo-heavy pages at 2x WebP q80 Half the JPEG size in our run.
Transparent background (logos, components) PNG or WebP JPEG has no alpha channel.
Thumbnails in a list or grid WebP q80, resized Small and fast to load; zoom-level detail does not matter.
Archiving or legal evidence PNG (or PDF) You want the exact pixels the page showed.

Format gotchas in headless browsers

  • Puppeteer accepts type: 'png' | 'jpeg' | 'webp'. Passing quality with PNG throws an error, so only set it for JPEG and WebP. The Puppeteer screenshot formats guide covers the other options.
  • Playwright page.screenshot() only writes PNG and JPEG. To get WebP you must convert afterwards (for example with sharp) or capture through a CDP session.
  • Selenium save_screenshot() and takeScreenshot() always return PNG. Any other format means a conversion step or a DevTools command.
  • Transparency needs two things: a format with alpha and a page with no background. In Puppeteer that is omitBackground: true with PNG or WebP.
  • Quality scales are not comparable across encoders. JPEG q80 in Chrome is not JPEG q80 in libjpeg-turbo, ImageMagick or sharp. Re-measure if you change the encoder.

Run the measurement yourself

Save as formats.mjs next to a corpus/ folder with your own test pages, then run npm i [email protected] and node formats.mjs. It writes formats.csv and one file per variant.

// Measure screenshot bytes and capture time for PNG, JPEG and WebP with Puppeteer.
import puppeteer from 'puppeteer';
import { mkdir, writeFile } from 'node:fs/promises';

const RUNS = 5;
const pages = [
  { name: 'example.com', url: 'https://example.com', width: 1280, height: 800 },
  // Add your own URLs here: a docs page, a dashboard, a photo-heavy page, a social card.
];
const variants = [
  { type: 'png' },
  { type: 'jpeg', quality: 80 },
  { type: 'jpeg', quality: 90 },
  { type: 'webp', quality: 80 },
  { type: 'webp', quality: 90 },
];
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];

await mkdir('out', { recursive: true });
const browser = await puppeteer.launch();
const rows = [['page', 'scale', 'format', 'quality', 'bytes', 'median_ms']];
try {
  const page = await browser.newPage();
  for (const p of pages) {
    for (const scale of [1, 2]) {
      await page.setViewport({ width: p.width, height: p.height, deviceScaleFactor: scale });
      await page.goto(p.url, { waitUntil: 'networkidle0' });
      for (const v of variants) {
        const times = [];
        let buf;
        for (let i = 0; i < RUNS; i++) {
          const t0 = performance.now();
          // PNG rejects a quality option, so only pass it for lossy formats.
          buf = await page.screenshot({ type: v.type, ...(v.quality ? { quality: v.quality } : {}) });
          times.push(performance.now() - t0);
        }
        const ext = v.type === 'jpeg' ? 'jpg' : v.type;
        await writeFile(`out/${p.name}@${scale}x-${v.type}${v.quality ?? ''}.${ext}`, buf);
        rows.push([p.name, scale, v.type, v.quality ?? '', buf.length, median(times).toFixed(0)]);
      }
    }
  }
} finally {
  await browser.close();
}
await writeFile('formats.csv', rows.map((r) => r.join(',')).join('\n') + '\n');
console.log(`wrote ${rows.length - 1} rows to formats.csv`);

Two notes on reading your results. First, keep the page loaded while you loop over formats, otherwise you measure navigation, not encoding. Second, run it on the hardware you deploy to: a small cloud VM will show larger encode times than a laptop, and the gap between JPEG and WebP grows.

The same choice with one API request

If you capture through RenderScreenshot, the format and quality are request parameters, and the response headers tell you the output size, so you can run the same comparison on your own URLs without a browser fleet.

# WebP at quality 80 for a docs or dashboard page
curl -o page.webp \
  -H "Authorization: Bearer $RS_API_KEY" \
  "https://api.renderscreenshot.com/v1/screenshot?url=https%3A%2F%2Fexample.com&format=webp&quality=80"

# JPEG for a photo-heavy page, PNG for a pixel-exact capture
curl -o hero.jpg -H "Authorization: Bearer $RS_API_KEY" \
  "https://api.renderscreenshot.com/v1/screenshot?url=https%3A%2F%2Fexample.com&format=jpeg&quality=80"
curl -o exact.png -H "Authorization: Bearer $RS_API_KEY" \
  "https://api.renderscreenshot.com/v1/screenshot?url=https%3A%2F%2Fexample.com&format=png"

With the Node SDK the same choice is one builder call:

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').width(1280).height(800).format('webp').quality(80)
);
await writeFile('page.webp', image);

Presets such as og_card default to PNG. To get a smaller social card, override the format: preset=og_card&format=jpeg&quality=85. The response formats reference lists the content type for each format.

Summary

  • Interface screenshots: WebP q80 first, PNG when pixels must be exact, JPEG almost never.
  • Gradient-heavy social cards: never default to PNG without checking the size.
  • Photo-like content: JPEG at 1x, WebP at 2x, and measure with your real images.
  • Encode cost: JPEG is cheapest, WebP took 2 to 5 times longer than JPEG in Chrome, PNG cost depends on content.

To run this comparison against your own pages with no browser to manage, create a free API key and swap format= in the request above.

Last tested: 2026-10-15. Re-run planned for January 2027 or on the next major Chrome release, whichever comes first.

Frequently asked questions

Is WebP always smaller than JPEG for screenshots?
No. In our test, WebP at quality 80 was smaller than JPEG at quality 80 on every UI and text page, but on a noisy photo-like page at 1x it was about 8% larger. At 2x the same photo page was about half the JPEG size.
Why is my PNG screenshot so large?
PNG is lossless, so smooth gradients, photos and noise cost a lot of bytes. Our 1200x630 gradient social card was 380 KB as PNG and 20 KB as WebP at quality 80.
Which format should I use for Open Graph images?
Use PNG or JPEG when you need the widest crawler support, and keep the file small. Use WebP when you control the consumer, for example your own site or app.
Can a JPEG screenshot have a transparent background?
No. JPEG has no alpha channel. Use PNG or WebP with a transparent background option.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.