AVIF vs WebP vs JPEG: real numbers at equal quality
Most “AVIF is 50% smaller than JPEG” claims compare a JPEG at quality 75 to an AVIF at some other setting. That is not a comparison — a -q 75 in one encoder is not the same picture as -q 75 in another. The only honest question is: at a fixed perceptual quality, how many bytes does each format need?
We measured it. Not on a handful of hero images, but across 1,900+ real photographs, with the actual encoders, scored by a perceptual metric rather than a guessed quality number.
How we measured it
- Images: the photo set from our public benchmark — 1,998 real photographs, mostly Wikimedia Commons originals, spanning portraits, landscapes, texture, low light, and fine detail. Not a curated best case.
- Encoders, each the standard tool: JPEG via libjpeg-turbo, WebP via cwebp (libwebp), AVIF via avifenc (libaom), and JPEG XL via cjxl (libjxl) as a bonus.
- The metric: ssimulacra2, a perceptual quality score where higher means closer to the original. It correlates with what the eye sees far better than PSNR or a bare quality number. 90+ is indistinguishable from the source; ~85 is high quality; ~80 is good.
- The method: for every image, we encode each format across a full quality grid, score each output with ssimulacra2, and then interpolate the number of bytes that format needs to hit a target score. This is the fair comparison — every format is held to the same perceived quality, and we read off the price in bytes. We report the median across the images where all formats actually reach the target.
That last point matters: comparing cwebp -q 80 to avifenc -q 80 is meaningless. Comparing “the WebP that looks as good as the AVIF” is the whole game.
The numbers
At ssimulacra2 85 (high quality, hard to tell from the original) — the 548 photos where all four formats reach it:
| Format | Median per photo | vs JPEG |
|---|---|---|
| JPEG (libjpeg-turbo) | 833 KB | — |
| WebP | 708 KB | −16% |
| AVIF | 605 KB | −28% |
| JPEG XL | 553 KB | −33% |
At the same quality, AVIF is 16% smaller than WebP, and both beat JPEG comfortably.
At ssimulacra2 80 (good quality, a common web target) — across 1,884 photos:
| Format | Median per photo | vs JPEG |
|---|---|---|
| JPEG | 510 KB | — |
| WebP | 471 KB | −11% |
| AVIF | 368 KB | −29% |
| JPEG XL | 351 KB | −32% |
AVIF’s lead widens at 80 (−29% vs JPEG, −21% vs WebP): it holds detail at lower bitrates where JPEG and WebP start to fall apart.
These figures line up with the independent literature — AVIF landing roughly 25–30% under JPEG at matched quality is a well-replicated result. What this run adds is that it is measured perceptually, at equal quality, on a large and un-cherry-picked photo set.
The caveats, stated plainly
- The JPEG baseline is libjpeg-turbo, the JPEG almost everything ships today. Modern JPEG encoders (mozjpeg, jpegli) close part of the gap while staying fully JPEG-compatible — if you can only serve JPEG, switch to one of those before anything else.
- WebP is quality-capped in practice. Its lossy encoder tops out: on high-detail photos it struggles to reach ssimulacra2 90 at all. That is a real limit, not a tuning choice.
- JPEG XL is the smallest here but you can’t ship it to browsers. No major browser enables it by default in 2026. It’s included as the technical ceiling, not a deployment recommendation.
- AVIF costs encode time. avifenc is far slower than cwebp or cjpeg. That’s fine for stored/CDN assets, less so for on-the-fly single-request encoding — which is exactly why a quality-targeted service caches.
What this means in practice
Serve AVIF with a WebP and JPEG fallback, and target a perceptual score, not a quality number. A fixed -q ships inconsistent quality across images — some over-compressed and visibly bad, some wastefully large. Searching each image’s encoder quality to hit a target ssimulacra2 score is what gives you both the smallest bytes and a quality you can actually promise.
That is what Patu does: it searches encoder quality per image to hit a perceptual target, and returns AVIF and WebP with a JPEG fallback, behind a CDN. The numbers above are why the default is AVIF-first.
curl -X POST "https://patu.dev/v1/compress?formats=avif,webp,fallback&target=85" \
-H "X-Api-Key: $PATU_KEY" \
--data-binary @photo.jpg -o photo.avif
You give it a target quality; it gives you the smallest file at that quality, in the formats a browser can actually use.
Method and data: the Patu benchmark harness over the photo category; ssimulacra2 via the rust-av implementation; encoders libjpeg-turbo, libwebp, libaom, libjxl at their released versions. Sizes are the median across photos where every format reaches the target score.