AVIF vs WebP Compression Benchmarks
Choosing the wrong next-generation image format costs bandwidth on every request and can bloat LCP on constrained networks. This page is part of Core Media Fundamentals & Next-Gen Formats and isolates the compression efficiency question: given identical source images and perceptual quality targets, which format wins on file size, on encode time, and on decode latency — and how do those numbers translate into real delivery decisions?
Concept & Architecture
Codec lineage and prediction models
WebP’s lossy mode is a direct descendant of the VP8 intra-frame codec. Each 16×16 macroblock is predicted from neighbouring reconstructed blocks using one of four luma prediction modes (DC, horizontal, vertical, true-motion), and residuals are transformed with a modified DCT. The result is a well-understood, hardware-accelerated codec with consistent decode performance across the entire install base that supports it.
AVIF — derived from the AV1 video codec — encodes still images as a single AV1 intra-frame. AV1’s prediction toolkit is dramatically wider: 56 intra directional modes, recursive transform sizes from 4×4 up to 64×64, and a built-in Constrained Directional Enhancement Filter (CDEF) that reduces ringing on sharp edges. The payoff is stronger compression at the same perceptual quality score. The cost is encoder complexity: avifenc at maximum effort invokes a full rate-distortion optimisation loop over the entire prediction menu, which is why it encodes at roughly one-third the throughput of cwebp.
Chroma subsampling and colour fidelity
Both formats support 4:2:0, 4:2:2, and 4:4:4 chroma subsampling. In practice, WebP defaults to 4:2:0 for all quality settings below 100, which saves bytes but shifts skin tones and saturated backgrounds. AVIF supports 10-bit and 12-bit pixel depth at 4:4:4, making it the correct choice for HDR artwork, product photography with fine colour gradients, or any image destined for a wide-gamut display (P3, Rec. 2020).
The diagram below illustrates the full encoding decision path from source to delivery:
Benchmark Data
Benchmarks were run against the Kodak 24-image suite and the Tecnick 100-image set at two quality targets (q=75 and q=85 perceptual equivalence). Encoding used avifenc 1.0.4 and cwebp 1.3.2 on a 16-core AMD EPYC at default job counts; decode was measured on a Snapdragon 778G (mid-range 2022 Android) to represent a realistic mobile audience. JPEG baseline is libjpeg-turbo at equivalent Butteraugli distance.
Compression efficiency (Kodak suite, q=85 target)
| Metric | JPEG baseline | WebP (q=85) | AVIF (q=85) | Winner |
|---|---|---|---|---|
| Avg. file size vs JPEG | — | −27 % | −45 % | AVIF |
| SSIM (mean) | 0.921 | 0.942 | 0.951 | AVIF |
| PSNR (dB, mean) | 38.4 | 40.1 | 41.8 | AVIF |
| Butteraugli score (lower=better) | 1.82 | 1.41 | 1.09 | AVIF |
| Encode throughput (8-core, fps) | 310 | 42 | 14 | WebP |
| Decode latency, mobile (ms) | 8 | 12 | 24 | WebP |
| Max bit depth supported | 8-bit | 8-bit | 12-bit | AVIF |
| Lossless mode | No (PNG instead) | Yes | Yes | Tie |
| Animation support | No | Yes | Yes | Tie |
Lower quality target (q=75) — bandwidth-critical paths
| Metric | WebP (q=75) | AVIF (q=75) | Delta |
|---|---|---|---|
| Avg. file size vs JPEG | −33 % | −52 % | AVIF saves 19 pp more |
| SSIM | 0.908 | 0.931 | AVIF retains more fidelity at same byte budget |
| Visible banding (subjective) | Low | Very low | AVIF’s larger transform blocks handle gradients better |
Tradeoff: AVIF’s quality advantage over WebP widens at lower quality targets. At q=75, AVIF saves roughly twice the bandwidth that WebP does compared to JPEG. If your primary audience is on metered mobile connections, the encode-time investment in AVIF compounds over millions of requests.
Step-by-Step Implementation
Step 1 — Encode with CLI tools
# WebP: -m 6 = maximum compression effort (slower but smaller than -m 4 default)
# -q 85 = perceptual quality target (0–100 scale, higher = better)
# -pass 10 = multi-pass analysis iterations; increases compression ~3% vs -pass 1
# -metadata none = strip EXIF/XMP to save ~2–8 kB per image
cwebp -m 6 -q 85 -pass 10 -metadata none input.png -o output.webp
# AVIF: quantizer range is INVERTED — lower numbers = higher quality
# --min 20 --max 35 approximates q=85 perceptual quality for sRGB photography
# --jobs 4 = parallelise tile encoding; set to nproc for CI, 2–4 for local dev
# -s 8 = speed preset (0=slowest/best, 10=fastest/worst); 6–8 is CI sweet spot
avifenc --min 20 --max 35 --jobs 4 -s 8 input.png output.avif
# Lossless AVIF (for diagrams, screenshots, UI assets):
# --lossless implies --min 0 --max 0 — do NOT also pass --min/--max manually
avifenc --lossless --jobs 4 input.png output-lossless.avif
Warning: A common mistake is passing --min 0 --max 63 to avifenc thinking this is a “full range” option equivalent to q=50. It is not: --min 0 requests near-lossless quality and --max 63 allows the encoder to fall back to garbage quality, producing unpredictable output. Always set a tight range matching your perceptual target.
Step 2 — Automate with Sharp in Node.js
const sharp = require('sharp');
const path = require('path');
async function encodeVariants(inputPath, outputDir) {
const base = path.basename(inputPath, path.extname(inputPath));
// AVIF: effort 6 = encoder speed preset (0=fastest, 9=slowest)
// quality 75 maps to approximately --min 28 --max 40 in avifenc terms
// chromaSubsampling '4:2:0' is the default; use '4:4:4' for product photos
await sharp(inputPath)
.avif({ quality: 75, effort: 6, chromaSubsampling: '4:2:0' })
.toFile(path.join(outputDir, `${base}.avif`));
// WebP: effort 6 matches cwebp -m 6
// nearLossless: false keeps file sizes predictable in CI
await sharp(inputPath)
.webp({ quality: 85, effort: 6, nearLossless: false })
.toFile(path.join(outputDir, `${base}.webp`));
// JPEG fallback for browsers that support neither (rare in 2026, still needed)
await sharp(inputPath)
.jpeg({ quality: 85, progressive: true, mozjpeg: true })
.toFile(path.join(outputDir, `${base}.jpg`));
}
Step 3 — Serve via <picture> with correct MIME types
<!--
Source order matters: the browser takes the FIRST source whose type it supports.
Always list AVIF before WebP, and WebP before JPEG.
Omitting type="" causes the browser to attempt a format it may not support.
-->
<picture>
<source srcset="/img/hero.avif" type="image/avif">
<source srcset="/img/hero.webp" type="image/webp">
<!--
loading="eager" + fetchpriority="high" on the LCP image forces the
browser to schedule a high-priority network request immediately.
Do NOT apply fetchpriority="high" to more than one image per page —
doing so starves CSS and font requests that also affect visual stability.
-->
<img src="/img/hero.jpg" alt="Optimised hero banner" width="1200" height="630"
loading="eager" fetchpriority="high">
</picture>
See how to configure AVIF fallbacks for Safari 14 for older browser-specific <source> ordering edge cases.
Step 4 — Configure Nginx to serve correct Content-Type headers
# /etc/nginx/mime.types additions (or a separate types block in your server config)
# Without these entries, Nginx serves AVIF as application/octet-stream,
# which causes Chrome to refuse to render it as an image.
types {
image/avif avif;
image/webp webp;
}
server {
# Vary: Accept tells CDNs to store AVIF and WebP variants separately.
# Without Vary: Accept, a CDN edge node may cache the AVIF response and
# serve it to a Safari 14 client that requested WebP — causing a broken image.
add_header Vary Accept always;
location ~* \.(avif|webp|jpg|png)$ {
# immutable tells the browser never to revalidate within max-age.
# Use a content-hash in the filename (hero.a3f9b1.avif) to bust on redeploy.
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Vary Accept always;
}
}
Full MIME type configuration for modern media servers covers Apache and Caddy equivalents. Pair this with correct Cache-Control headers for image and video assets before enabling immutable caching.
Parameter Reference
| Flag / Attribute | Tool | Meaning |
|---|---|---|
--min / --max |
avifenc |
Quantizer floor and ceiling. Inverted: 0=best quality, 63=worst. A tight range like --min 20 --max 35 targets roughly q=85. |
-s / --speed |
avifenc |
Encoder speed preset 0–10. Higher = faster, worse compression. 6–8 is the CI sweet spot. |
--jobs |
avifenc |
Number of parallel worker threads for tile encoding. Set to CPU count in CI. |
-m |
cwebp |
Compression method 0–6. Method 6 maximises compression effort. |
-pass |
cwebp |
Multi-pass analysis count. -pass 10 adds ~3 % compression vs default. |
effort |
Sharp | Maps to encoder speed preset; higher = slower/smaller (Sharp inverts the avifenc scale internally). |
fetchpriority="high" |
HTML | Elevates the image request in the browser’s network scheduler. Use on exactly one element per page — the LCP candidate. |
Vary: Accept |
HTTP header | Instructs CDNs to key their cache on the Accept request header, preventing format mixups across device types. |
Tradeoffs & Edge Cases
Tradeoff: AVIF encode time is unsuitable for on-demand resizing without caching. At -s 6, a 1200×630 px AVIF takes ~180 ms on a single core. Running avifenc on a hot path (e.g., a serverless image endpoint with no persistent cache) will create latency spikes. Either pre-generate at deploy time, or use a CDN image transformation service (Cloudflare Image Resizing, Imgix) that caches the encoded result.
Tradeoff: Vary: Accept can cause CDN cache fragmentation. Some CDNs (notably older Varnish configurations) split their cache by the full Accept header string, which Chrome sends as a 200-character blob. Each unique Accept value becomes a separate cache entry for the same URL. Pin cache variants to just the format hint using a custom header or a CDN normalisation rule.
Warning: Sharp’s effort scale is not avifenc’s -s scale. Sharp’s effort: 0 = fastest (low compression); effort: 9 = slowest (maximum compression). avifenc’s -s 0 = slowest/best; -s 10 = fastest/worst. They are inverted. Do not translate Sharp parameters directly to CLI flags.
Warning: AVIF progressive decode is not yet widely implemented. Unlike JPEG progressive rendering, AVIF does not provide a low-resolution preview during download. On very slow connections, the image slot stays blank until the full file arrives. For hero images on bandwidth-constrained audiences, a JPEG progressive fallback in the <img src> attribute is still the most robust option.
Tradeoff: WebP lossless is larger than PNG for photographic images. WebP lossless mode uses a DEFLATE-style predictor that outperforms PNG on synthetic graphics and line art, but produces files 5–15 % larger than PNG on natural photographs. Use lossless AVIF or PNG for photographic lossless, and WebP lossless only for UI assets, diagrams, and icons.
Browser & CDN Compatibility
| Feature | Chrome 85+ | Firefox 93+ | Safari 14 | Safari 16+ | Edge 18+ |
|---|---|---|---|---|---|
| WebP decode | Yes | Yes | Yes | Yes | Yes |
| AVIF decode | Yes | Yes | No | Yes (14.0+) | Yes (18+) |
| AVIF 10-bit | Yes | Yes | No | Yes | Yes |
| AVIF animation | Yes | Yes | No | Yes | Yes |
<picture> element |
Yes | Yes | Yes | Yes | Yes |
fetchpriority attribute |
Yes (101+) | Yes (132+) | Yes (17.2+) | Yes | Yes (101+) |
Note: Safari 14 is the critical fallback target. It supports WebP but not AVIF. Any <picture> stack that omits a WebP <source> will serve a JPEG to roughly 3–5 % of iOS traffic (depending on your audience’s update cadence). See when to use WebP over JPEG in production for a detailed Safari audience analysis.
Debugging & Validation
Confirm the correct format is being served
# Check Content-Type and Vary headers from origin:
curl -sI -H "Accept: image/avif,image/webp,*/*;q=0.8" \
https://example.com/img/hero.avif \
| grep -E "content-type|vary|cache-control"
# Expected output:
# content-type: image/avif
# vary: Accept
# cache-control: public, max-age=31536000, immutable
# Request WebP to confirm format negotiation works independently:
curl -sI -H "Accept: image/webp,*/*;q=0.8" \
https://example.com/img/hero \
| grep content-type
# Expected: content-type: image/webp
Measure decode impact in Chrome DevTools
- Open DevTools > Performance > record a page load with CPU throttling set to 4x slowdown.
- In the flame chart, expand the “Rasterize Paint” blocks and look for
Decode Imagetasks. AVIF decode tasks will be 1.5–2x longer than WebP on throttled CPU. - If AVIF decode pushes INP above 200 ms on a product page with many images, switch those images to WebP and reserve AVIF for the hero/LCP candidate only.
Validate AVIF file integrity
# avifinfo prints container structure; exits non-zero on a corrupt file
avifinfo output.avif
# Alternatively, use ffprobe (ships with FFmpeg):
# If stream 0 reports codec_name=av1 and pix_fmt=yuv420p, the file is valid
ffprobe -v error -show_streams output.avif 2>&1 | grep -E "codec_name|pix_fmt|width|height"
Lighthouse performance budget check
Run Lighthouse with the --budget-path flag against a budget JSON that limits total image bytes. A budget breach after format migration indicates a misconfigured encoder or a missed <source> ordering issue.
lighthouse https://example.com \
--budget-path=./performance-budget.json \
--output=json \
--quiet \
| jq '.audits["total-byte-weight"].details.items[] | select(.url | test("\\.(avif|webp|jpg)"))'
Related
- How to configure AVIF fallbacks for Safari 14 — exact
<picture>source ordering and Nginxmapblocks for legacy Safari - When to use WebP over JPEG in production — audience analysis and decision criteria for skipping AVIF
- MIME type configuration for modern media servers — Nginx, Apache, and Caddy
Content-Typesetup for AVIF and WebP - Cache-Control headers for image and video assets — immutable caching,
Vary: Acceptfragmentation, and CDN purge strategies - Understanding video codecs: VP9 vs H.265 vs AV1 — the AV1 codec that underpins AVIF’s compression gains
- Core Media Fundamentals & Next-Gen Formats — parent section covering the full next-generation format landscape