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:

AVIF vs WebP encoding and delivery decision flow A flowchart showing: source image enters a format decision node; HDR/wide-gamut paths route to AVIF with avifenc; standard sRGB paths evaluate browser support and route to AVIF (with WebP fallback) or WebP (with JPEG fallback); all paths converge on CDN delivery with Vary: Accept. Source image HDR / wide-gamut or 10-bit depth? Yes avifenc 4:4:4 / 10-bit No AVIF browser support confirmed? Yes AVIF primary + WebP fallback No WebP primary + JPEG fallback CDN delivery Vary: Accept

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

  1. Open DevTools > Performance > record a page load with CPU throttling set to 4x slowdown.
  2. In the flame chart, expand the “Rasterize Paint” blocks and look for Decode Image tasks. AVIF decode tasks will be 1.5–2x longer than WebP on throttled CPU.
  3. 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)"))'