Address
aadhar.shGarageAV2, before anyone can see it

AV2, before anyone can see it

AV2 is the successor to AV1, the codec inside every AVIF image, and its reference encoder shipped 1.0.0 in May. AOM's own all-intra report puts it 18 to 24% ahead of AV1 on stills. That number comes from video test sequences scored with PSNR, SSIM and 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 everything below is a picture your browser couldn't have drawn.

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 fallback.

AV1 control
AV1 in AVIF
AV2 in AVIF
AV2 in AVIF
AV2 with a WebP fallback
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.

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 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

The comparison runs on the 16 detail crops from /pixel-peeper, each 320 px, from an X-T50. Each crop gets two budgets. The first is the file size of the AVIF tier this site ships (avifenc -q 63 -d 10 --speed 2 --yuv 444). The second is the size of the JPEG fallback tier, filled with AVIF instead. AV2 is encoded onto each budget, file against file. Both are scored against the source with the two perceptual metrics this site ships by: ssimulacra2, where higher is better, and 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 instead, one notch at a time.

AV2 encoderΔ s2, trainΔ s2, holdoutwins of 32, s2 / buone 320 px encode
the shipped AVIF (AV1, speed 2)referencereference0.45 s
AV2 speed 6, libavif default (tune ssim)-1.75-2.160 / 75.4 s
AV2 speed 6, tune psnr-2.04-2.810 / 145.4 s
AV2 speed 3, libavif default (tune ssim)-1.03-1.534 / 1118.3 s
cross-check: raw avmenc, cpu-used 6 (tune psnr)-1.46-2.320 / 18
cross-check: raw avmenc, cpu-used 3 (tune psnr)-0.81-1.666 / 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 average.

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.

Where AV2 loses

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, losslessThe shipped AV1 encode of the same crop, decodedThe AV2 encode at the same budget, decoded
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, losslessThe shipped AV1 encode of the same crop, decodedThe AV2 encode at the same budget, decoded
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, losslessThe shipped AV1 encode of the same crop, decodedThe AV2 encode at the same budget, decoded
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, at speeds a person will wait for, its first encoder hasn't turned that into a win by a photo metric. 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.

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, losslesstotal bytesvs PNG
PNG (the source)3,436,4410%
AV2 (avifenc -c avm -l)3,048,575-11.3%
AV1 (avifenc -c aom -l)2,363,430-31.2%
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, and I haven't proved it. JPEG XL's lead is the older, bigger story, and nothing here changes it.

Run it yourself

Everything above comes from two 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. codec-knob-probe.ts --codec avm runs the comparison.

tools/photos/libavif-avm/build.sh
bun tools/photos/codec-knob-probe.ts --codec avm --variants base,tunepsnr

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 change how long it takes and not what it says.

What would change this

The result is small and specific. It covers 16 crops from one camera at 320 px and the reference encoder rather than a production one. The image container is one libavif itself labels experimental, and both metrics come from the JPEG XL project. A production AV2 encoder is the likeliest thing to flip it, the way SVT-AV1 and later libaom releases were for AV1. A still-image tune would be the first place to look. When one ships, the probe above re-runs in an afternoon.

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

garagewatching2026-09-27