---
title: "aadhar.sh/garage/av2: AV2, before anyone can see it"
description: "AV2 scored against the AVIF this site ships, at matched bytes: it wins on the sparsest thumbnails, loses on dense detail, and breaks behind a picture fallback."
path: "/garage/av2"
section: "garage"
kind: "content"
updated: "2026-09-28"
source: "https://aadhar.sh/garage/av2"
---

> Site index: https://aadhar.sh/llms.txt
> Section index: https://aadhar.sh/garage/llms.txt
> This is the Markdown twin of a page on aadhar.sh. The HTML at the source
> URL below is the original, and is hand-written and unminified on purpose.

# AV2, before anyone can see it

[AV2][av2] is the successor to [AV1][av1], the codec inside every [AVIF][avif] image, and its reference encoder [shipped 1.0.0 in May](https://github.com/AOMediaCodec/avm). AOM's own [all-intra report](https://arxiv.org/abs/2605.15800) puts it 18 to 24% ahead of AV1 on stills. That number comes from video test sequences scored with [PSNR][psnr], [SSIM][ssim] and [VMAF][vmaf]. I wanted the answer for photographs, scored the way this site already scores its own images, at the exact byte budgets it ships. No browser can decode an AV2 image yet, so the figures below are decodes I made offline, and one section brings its own decoder to draw AV2 in your browser anyway.

## Your browser, right now

These are three copies of the same 128 px crop. The first is AV1, the codec inside the AVIF files browsers decode today. The second is AV2 in the same container, at 2,030 bytes against AV1's 2,097. The third is how you'd try to ship AV2 safely: a `<picture>` source typed `image/avif`, with a [WebP][webp] fallback.

![AV1 control](https://aadhar.sh/garage/av2/demo-av1.avif)

AV1 in AVIF

![AV2 in AVIF](https://aadhar.sh/garage/av2/demo-av2.avif)

AV2 in AVIF

![AV2 with a WebP fallback](https://aadhar.sh/garage/av2/demo-fallback.webp)

AV2, with a fallback

Enable JavaScript and this line reports what each image did.

In Chrome 154 and Canary 156 the third image is broken, and the WebP never loads. Of everything on this page, that's the finding most likely to cost somebody an afternoon, so it comes first.

## Decoding it here instead

Your browser couldn't draw that second file, but this page can. The button below fetches AOM's reference decoder, AVM 1.0.0, compiled to [WebAssembly][webassembly], and runs it on a background thread. It's about 550 KB over the wire, which is why it waits for a click. It draws exactly the pixels the native build does: I compared every decoded sample on 16 files, from 8-bit monochrome to 10-bit 4:4:4, and all of them matched.

Nothing has been fetched yet.

The 128 px AV2 file from the second slot above

Below are four of the 600 px thumbnails this site ships, from thin to dense. Each AV1 image is the AVIF file the site actually serves, drawn by your browser's own decoder. Each AV2 image is the largest AVM 1.0.0 encode at or under that file's size, cut from the same source and drawn by the WebAssembly decoder. Click one to flip between the two.

![The shipped AV1 thumbnail](https://aadhar.sh/i/XT507488.f028baef.avif)

0.38 bits per pixel, the sparse end. ssimulacra2 against the source: AV2 81.63, AV1 80.11.

![The shipped AV1 thumbnail](https://aadhar.sh/i/XT507525.c937a50e.avif)

0.63 bits per pixel, near the tier's median of 0.67. ssimulacra2: AV2 81.27, AV1 80.70.

![The shipped AV1 thumbnail](https://aadhar.sh/i/XT508319.bf0268a0.avif)

1.17 bits per pixel. ssimulacra2: AV2 79.44, AV1 74.70. This one is an exception: between 0.8 and 1.2 bits per pixel, the table further down counts 2 AV2 wins in 10 calls.

![The shipped AV1 thumbnail](https://aadhar.sh/i/XT507399.9b833151.avif)

2.03 bits per pixel, the densest photo I could reproduce. ssimulacra2: AV2 75.90, AV1 76.76, the loss the table predicts above 1.2.

Every conforming AV1 decoder produces the same samples, and this AV2 decoder matches AVM's own, so what changes when you flip is the encode. Four photos I tried were left out: re-encoding them didn't reproduce the shipped file byte for byte, so I couldn't show that their AV1 and AV2 files started from the same pixels.

## Why the fallback doesn't catch it

A `<picture>` element chooses a source by its *declared type*, before any byte decodes. Chrome supports `image/avif`, so it commits to the source, fetches it, and finds an image it can't read. Nothing sends it back to the `<img>`, because the fallback only covers types a browser has never heard of.

The file explains the rest. [libavif][libavif] writes AV2 inside the same box structure as AV1, and the brand still says `avif`, `mif1` and `miaf`. Only the image item changes, from `av01` to `av02`, with an `av2C` config box beside it. A careful decoder like `avifdec` skips the item it doesn't know and reports Primary item not found. A browser draws a broken image.

So an AV2 image needs a name a browser can decline, the way `image/avif` gave AV1 stills a name that browsers without AV1 could skip. Until one exists, serving AV2 under the AVIF name breaks the image in every browser that supports AVIF, which is every current release of Chrome, Firefox, Safari and Edge.

## At the bytes AVIF actually costs

**Corrected 2026-09-28.** The first version of this section scored only the 16 detail crops further down, found AV2 losing every call, and called that the answer for photographs. It was the answer for the densest detail in each photo. On the thumbnails this site actually ships, AV2 wins.

**Corrected 2026-10-07.** That note overreached too. The 38 tiles below average 0.46 bits per pixel, and the 255 tiles this site ships at 600 px have a median of 0.67. The probe reads only JPEG originals, and the 43 it had to draw from are a sparse run shot from August to December 2025. Bucketed the way the table further down pools them, 82 of the 255 shipped tiles sit under half a bit per pixel, where AV2 won 23 of 24 calls. The other 173 sit above it, where AV2 won 6 of 30. So AV2 wins on the sparse third of this site's thumbnails, and the other two thirds sit where it loses.

The thumbnails are 38 tiles at 600 px, cut the way the site's own pipeline cuts them: the whole frame, squared and reduced about 8x in [linear light][linear-light] by `zenc square`. They come from Fuji photos that none of the tuning below ever saw. Each tile's budget is the file size of the AVIF tier this site ships (`avifenc -q 63 -d 10 --speed 2 --yuv 444`), and AV2 is encoded onto that budget, file against file. Both are scored against the source with the two perceptual metrics this site ships by: [ssimulacra2](https://github.com/cloudinary/ssimulacra2), where higher is better, and [butteraugli](https://github.com/google/butteraugli), where lower is better.

Matched bytes is the whole method, and AV2 makes it harder than usual. libavif's quality scale is far too coarse for it: on one crop, `-q 49` wrote 6,865 bytes of AV2 and `-q 50` wrote 9,608, a 40% step. So the probe steps AV2's own [quantizer][quantization] instead, one notch at a time.

38 tiles, AV2 speed 6 · Δ s2, mean · Δ s2, median · s2 wins · butteraugli wins

the shipped AVIF (AV1, speed 2) · reference · reference

AV2, libavif default (tune ssim) · +0.77 · +0.65 · 28 of 38 · 24 of 38

AV2 with per-segment QM (see below) · +0.89 · +0.90 · 30 of 38 · 28 of 38

**With no tuning at all, AV2 wins 28 of 38 tiles by ssimulacra2 and 24 by butteraugli.** The average margin is 0.77 points. That is modest, but it comes against an AV1 encoder with a still-image tune, while AV2's encoder has only the video defaults. It costs about 14 times the encode time, single-threaded, on a 600 px tile.

## The crops that disagree

The first version of this page ran on the 16 detail crops from [/pixel-peeper](https://aadhar.sh/pixel-peeper), each 320 px, from an X-T50. Each is the single most detailed 320 px window in its photo, at full resolution, picked to be the hardest thing an encoder sees. Each crop gets two budgets. The first is the file size of the shipped AVIF tier, and the second is the size of the JPEG fallback tier, filled with AVIF instead.

AV2 encoder · Δ s2, train · Δ s2, holdout · wins of 32, s2 / bu · one 320 px encode

the shipped AVIF (AV1, speed 2) · reference · reference · 0.45 s

AV2 speed 6, libavif default (tune ssim) · -1.75 · -2.16 · 0 / 7 · 5.4 s

AV2 speed 6, tune psnr · -2.04 · -2.81 · 0 / 14 · 5.4 s

AV2 speed 3, libavif default (tune ssim) · -1.03 · -1.53 · 4 / 11 · 18.3 s

cross-check: raw avmenc, cpu-used 6 (tune psnr) · -1.46 · -2.32 · 0 / 18

cross-check: raw avmenc, cpu-used 3 (tune psnr) · -0.81 · -1.66 · 6 / 21

Each Δ is AV2 minus the shipped AVIF in ssimulacra2, averaged over 16 crop and budget pairs. Train is the 8 crops /pixel-peeper uses and holdout is 8 it never sees, so a result has to survive a set nobody tuned on. The two cross-check rows drive AV2's raw encoder through my own colour conversion, which shares no code with libavif above the codec. They score AV2 about half a point better than libavif does at the same tune. I haven't traced that gap, and neither instrument puts AV2 ahead on the crops.

**On the crops, at speed 6, ssimulacra2 prefers the shipped AV1 on all 32 calls.** Speed 3 beats speed 6 on every one of them and closes about a third of the gap, to 1.03 and 1.53, winning 4 of 32. It costs 41 times the encode time of the AVIF this site ships, and none of the speeds I could afford to run closes the whole gap.

These rows let AV2's encoder choose its own superblock size, and at 320 px it picks 64. Pinning the 128 that a real 600 px tile gets moves speed 6 to -1.57 on train and -2.12 on holdout.

## What decides it: bits per pixel

The tiles and the crops sit at different densities. At the AVIF tier's budget these 38 tiles average 0.46 bits per pixel and a crop 1.08, about 2.3 times as many, because a crop is all detail and a tile is mostly sky, skin and shadow at thumbnail scale. Pool the two, sort by bits per pixel, and AV2's standing follows one curve. The last column counts this site's 255 shipped 600 px tiles in each band.

bits per pixel · calls · Δ s2 · AV2 wins · shipped tiles

under 0.3 · 12 tiles · +1.60 · 12 of 12 · 30 of 255

0.3 to 0.5 · 12 tiles · +0.70 · 11 of 12 · 52 of 255

0.5 to 0.8 · 11 tiles, 4 crops · -0.47 · 4 of 15 · 71 of 255

0.8 to 1.2 · 2 tiles, 8 crops · -0.94 · 2 of 10 · 86 of 255

over 1.2 · 1 tile, 4 crops · -2.36 · 0 of 5 · 16 of 255

AV2 wins where the budget is thin and loses where every pixel carries detail, and the flip sits near half a bit per pixel. The trend holds within the tiles alone (a correlation of -0.33 between bits per pixel and AV2's margin), so it is more than a difference between two ways of cutting images. A third of this site's shipped thumbnails live at the thin end: 82 of 255 sit under half a bit per pixel, and the median, 0.67, sits past the flip. A pixel-peeping crop lives at the thick end, which is where the next section finds the loss.

## Where AV2 loses

At the dense end, the loss has a shape. My first guess was that AV2 smooths away sensor grain, and I nearly published it. It's wrong. Measured on the decodes, AV2 reproduces the source's fine, pixel-scale texture more faithfully than AV1 on 21 of the 32 calls. The correlation between each decode's fine detail and the source's rises from 0.773 to 0.811.

What AV2 gets wrong is the coarse picture. Average each image over 8 by 8 pixel blocks, which is roughly what you'd see from across the room, and compare those averages with the source's. AV2's error is higher than AV1's on **32 of 32** calls, by 50% in brightness (0.26 to 0.39 on an 8-bit scale) and 64% in colour (0.25 to 0.41). It holds on the 8 calls where the two files sit within 1% of the same size, so it isn't a byte shortfall.

So this encoder moves its bits from the broad strokes to the fine ones: slightly truer texture, slightly wronger tone and colour in flat areas. ssimulacra2 scores the picture at several scales and weighs colour heavily, and it reads that trade as a loss.

![Source crop from XT509535, lossless](https://aadhar.sh/garage/av2/XT509535-src.webp)

![The shipped AV1 encode of the same crop, decoded](https://aadhar.sh/garage/av2/XT509535-av1.webp)

![The AV2 encode at the same budget, decoded](https://aadhar.sh/garage/av2/XT509535-av2.webp)

**XT509535**, at the AVIF tier's budget. AV1 11,166 B: ssimulacra2 71.00, butteraugli 2.204. AV2 10,824 B (speed 3, qp 107): ssimulacra2 65.91, butteraugli 2.419. Fine texture tracks the source at 0.57 for AV1 and 0.70 for AV2; block-scale error runs 0.26 against 0.40 in brightness and 0.23 against 0.38 in colour.

![Source crop from XT509540, lossless](https://aadhar.sh/garage/av2/XT509540-src.webp)

![The shipped AV1 encode of the same crop, decoded](https://aadhar.sh/garage/av2/XT509540-av1.webp)

![The AV2 encode at the same budget, decoded](https://aadhar.sh/garage/av2/XT509540-av2.webp)

**XT509540**, at the AVIF tier's budget. AV1 8,906 B: ssimulacra2 73.54, butteraugli 2.006. AV2 8,308 B (speed 3, qp 105): ssimulacra2 69.70, butteraugli 2.561. Fine texture tracks the source at 0.50 for AV1 and 0.59 for AV2; block-scale error runs 0.26 against 0.45 in brightness and 0.21 against 0.45 in colour.

![Source crop from XT509278, lossless](https://aadhar.sh/garage/av2/XT509278-src.webp)

![The shipped AV1 encode of the same crop, decoded](https://aadhar.sh/garage/av2/XT509278-av1.webp)

![The AV2 encode at the same budget, decoded](https://aadhar.sh/garage/av2/XT509278-av2.webp)

**XT509278**, at the JPEG fallback's budget. AV1 20,866 B: ssimulacra2 82.94, butteraugli 1.824. AV2 20,561 B (speed 3, qp 103): ssimulacra2 82.99, butteraugli 1.568. Fine texture tracks the source at 0.80 for AV1 and 0.87 for AV2; block-scale error runs 0.24 against 0.37 in brightness and 0.26 against 0.40 in colour.

Each figure shows the 200 px region where the AV1 and AV2 decodes differ most, at 2x, in lossless WebP, because re-encoding a decode would add artifacts of its own. Click or tap a figure to flip between the three in place, which is the easiest way to see a codec difference. Each AV2 file is the largest that fits at or under the AV1 file, so AV2 never gets the extra bytes.

## The tune you didn't choose

butteraugli's verdict turned out to depend on a setting nobody asked for. libavif tells AV2's encoder to tune for SSIM unless you pick a tune yourself, and AV2's own command-line encoder defaults to PSNR. At speed 6, switching libavif to `tune=psnr` costs AV2 about half a point of ssimulacra2 and doubles its butteraugli wins, from 7 of 32 to 14.

AV2's encoder accepts those two tunes and refuses a third: `tune=iq`, the image-quality tune libaom added for stills in 3.13, which the shipped AVIF uses. So the comparison pits a codec with a tune built for photographs against one that has only the video defaults. That's a fair picture of today, and a reason to expect the gap to narrow.

## Where the literature's number comes from

AOM's report measures something real and different. Its AV2 beats an AV1 anchor by 17.82% on PSNR-Y and 23.58% on VMAF in all-intra mode, which codes every frame as a still. The anchor is a branch of libaom, the test material is video sequences, and the metrics are PSNR-HVS, PSNR, SSIM, MS-SSIM, CIEDE2000 and VMAF. Neither ssimulacra2 nor butteraugli appears, and I couldn't find the encoder speeds in it.

It also reports cost: on average AV2 took 33 times longer to encode than the anchor and 3.86 times longer to decode. The encode column above agrees in kind. Through libavif, one 320 px thumbnail takes 5.4 seconds at speed 6 and 18.3 at speed 3, against 0.45 for the AVIF this site ships. AV2's raw encoder at cpu-used 1 was still running after 8 minutes when I stopped it.

So the two results don't contradict each other. By the metrics video is judged by, AV2 is the better codec. On photographs, its first encoder turns that into a win by a photo metric on the sparsest thumbnails, and gives it back as the detail rises. AV1 froze its bitstream in June 2018, and Chrome shipped AVIF in August 2020, after faster encoders had arrived. I expect AV2 to need the same run-up before the dense end turns too.

## Lossless

AV2's lossless mode round-trips all 16 crops bit for bit. It's also 29% larger than AV1's, which surprised me, because AOM lists improved lossless coding among AV2's new tools.

16 crops, lossless · total bytes · vs PNG

PNG (the source) · 3,436,441 · 0%

AV2 (`avifenc -c avm -l`) · 3,048,575 · -11.3%

AV1 (`avifenc -c aom -l`) · 2,363,430 · -31.2%

[JPEG XL][jpeg-xl] (`cjxl -d 0 -e 9`) · 1,571,053 · -54.3%

Both AVIF files used the same setup: 8-bit 4:4:4, full range, and the identity matrix, so the colour conversion isn't the difference. The slower speed 3 made one crop slightly bigger, 161,336 bytes against 160,357. I think libavif's experimental AV2 path is the likelier cause than the format, but I haven't proved it. JPEG XL's lead is the older, bigger story, and nothing here changes it.

## What the reference encoder leaves on the table

AVM is a reference encoder, and a reference encoder leaves tuning to whoever comes next. I ported four ideas from the encoders that tune AV1 for stills, and one that only AV2 can express. Each is a patch that is off by default, and the untouched reference decoder reads its output pixel for pixel. Each was scored on the crops, where AV2 needs the help most.

patch · what it does · Δ s2 on crops · verdict

base-delta-q · exposes the sequence-level DC and chroma offsets AVM hard-codes to zero · -0.06 (holdout, chroma +4) · about zero

variance-boost · libaom and SVT-AV1's Variance Boost: finer quantizers for flat superblocks · -0.54 (train) · loses

jpegli-mask · jpegli's adaptive-quantization field, mapped onto AV2's quantizer · -0.25 (train) · loses

qm-curves · libaom's all-intra quantization-matrix curve · -0.47 (holdout) · loses

qm-per-segment · two matrix sets per frame, split by texture · +0.14 (holdout) · wins

Only the last one pays, and it is the one AV1 had no syntax for. AV1 signals one quantization-matrix level per plane for the whole frame. AV2 can carry up to four sets and let each segment pick one. The all-intra curve helps smooth crops and costs 2 to 3.5 points on busy ones, so one frame-wide matrix is the wrong granularity. Splitting the frame with jpegli's masking field, a flatter matrix for textured areas and the curve for smooth ones, turns the curve's 0.47 loss into a 0.14 gain on the holdout crops, winning 11 of 16. On the tiles it adds 0.12 over plain AV2 and wins 28 of 38.

The same build compiles AVM's own AVX2 kernels for Apple silicon through [SIMDe](https://github.com/simd-everywhere/simde). AVM ships almost no NEON code, so on a Mac most of its hot loops ran in plain C. The output is byte-identical to the plain build, and a 600 px encode runs 1.35 times as fast.

## Run it yourself

Everything above comes from three committed tools. `tools/photos/libavif-avm/build.sh` builds libavif at a pinned commit with its experimental AV2 codec switched on. No tagged libavif release carries AVM 1.0.0 yet, so the pin is a commit. `--tuned` adds the five patches and the SIMDe kernels, and refuses to finish unless the result matches the pristine build byte for byte with every option off. `av2-tile-probe.ts` runs the tile comparison and `codec-knob-probe.ts --codec avm` runs the crops. `tools/photos/av2-wasm/build.sh` compiles the decoder this page runs, and `samples.ts` beside it writes the four AV2 thumbnails, refusing any photo whose shipped AVIF it can't reproduce byte for byte.

```
tools/photos/libavif-avm/build.sh --tuned
bun tools/photos/av2-tile-probe.ts --src <a folder of originals>
bun tools/photos/codec-knob-probe.ts --codec avm --variants base,tunepsnr
tools/photos/av2-wasm/build.sh
```

With every scheduling and caching concern stripped out, the probe is four steps per crop. Encode the shipped AVIF and take its size as the budget. Find the two AV2 quantizer settings whose files straddle that size. Decode both and score them against the source. Interpolate the score to the exact budget. The rest of the file is caching, crop selection and parallelism, which only change how long it takes.

## What would change this

The result is small, and it cuts both ways. It covers 38 tiles, sparser than the tier they sample, and 16 crops from one camera. It runs the reference encoder rather than a production one, in a container libavif itself labels experimental, and scores with two metrics that both come from the JPEG XL project. A production AV2 encoder is the likeliest thing to move the dense end, the way SVT-AV1 and later libaom releases did for AV1, and a still-image tune is the first place to look. The thin end is already a win, and it covers a third of this site's thumbnails. To ship that third, AV2 waits on a browser that decodes it and a name that browser can decline. When either arrives, both probes re-run in an afternoon.

watching · the understanding check is part of the page, not a gate

[av2]: https://en.wikipedia.org/wiki/AV2 "The video codec that follows AV1, from the Alliance for Open Media. Like AV1, it can also compress single still images."
[av1]: https://en.wikipedia.org/wiki/AV1 "A royalty-free video codec from the Alliance for Open Media. AVIF images are single AV1 frames stored in an image file."
[avif]: https://en.wikipedia.org/wiki/AVIF "An image format that stores a picture compressed with the AV1 video codec. Every current major browser can display it."
[psnr]: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio "Peak signal-to-noise ratio: a simple score, in decibels, of how far a compressed image's pixels drift from the original. Higher means closer."
[ssim]: https://en.wikipedia.org/wiki/Structural_similarity_index_measure "Structural similarity: a score up to 1 that compares local brightness, contrast and structure between two images. Closer to 1 means more alike."
[vmaf]: https://en.wikipedia.org/wiki/Video_Multimethod_Assessment_Fusion "Netflix's video quality score, which combines several measurements into one 0 to 100 number that predicts how viewers would rate the picture."
[webp]: https://en.wikipedia.org/wiki/WebP "Google's image format for the web. It usually compresses better than JPEG and not as well as AVIF."
[webassembly]: https://developer.mozilla.org/en-US/docs/WebAssembly "A compact binary format for code that browsers and other runtimes run at near-native speed, usually compiled from languages like Rust or C++."
[libavif]: https://github.com/AOMediaCodec/libavif "The reference library for reading and writing AVIF files. Its avifenc and avifdec tools wrap whichever AV1 or AV2 codec it was built with."
[linear-light]: https://en.wikipedia.org/wiki/Gamma_correction "Pixel values proportional to the actual amount of light. Ordinary image files store gamma-encoded values, so averaging those directly gets brightness wrong."
[quantization]: https://en.wikipedia.org/wiki/Quantization_(image_processing) "The lossy step in compression: rounding values to a coarser set of levels. The quantizer setting decides how coarse, trading file size against detail."
[jpeg-xl]: https://en.wikipedia.org/wiki/JPEG_XL "A newer image format from the JPEG committee. It compresses well, decodes in parallel, and can repack an existing JPEG losslessly into a smaller file."

Source: https://aadhar.sh/garage/av2
