Image codec benchmark
Run metadata
Chart 1 — rate / distortion
x = SSIMULACRA2, y = file size. Lower file size for a given SSIMULACRA2 is better. One line per series (codec + effort + depth).
Chart 2 — encode cost
x = SSIMULACRA2, y = encode time (best of N). Same series as above.
Chart 2 — encode cost
This run was made with --timing none, so no encode times
were recorded and the cost chart is omitted. Re-run without that flag
to measure encode cost.
Chart 3 — decode cost
x = SSIMULACRA2, y = decode time (best of N), measured in the browser
with createImageBitmap — no layout, no paint, just the
decode. Same series as above.
Visual comparison
The question here is how this image would look on a real web page, not pixel-peeping. Each variant is shown at half its intrinsic size — what a site does when serving 2×-density imagery to a high-DPR display. No zoom, no pan.
Click a variant or press its number key. Hold space to flip back to the lossless reference.
Lossless
Each codec is swept across its effort levels
(avifenc -s, cjxl -e, cwebp -z),
so these are curves rather than single points.
Chart 4 — lossless size vs encode time
y = file size, x = encode time. Down and to the left is better. One line per codec, one point per effort level.
Chart 5 — lossless size vs decode time
y = file size, x = browser decode time. Same axes as Chart 4 with decode substituted for encode, so the two read against each other: a codec can win on size and still cost more to get the pixels back.
The Decode column is browser decode time, measured the same way as Chart 3. A format can win on lossless size and still cost more to get the pixels back, so the two are worth reading together. The source PNG row is the baseline.
Every row is decoded and asserted bit-exact against the reference and asserted to score exactly 100.00 — either check failing aborts the run, so a size here is a real lossless size.
All results
Depth is an AVIF-only axis. avifenc -d sets
the coded bit depth, and 10-bit 4:4:4 typically scores better than 8-bit
at the same -q. JPEG XL has no equivalent: its bitstream
declares 8-bit for an 8-bit source, but lossy JXL reconstructs in
float/XYB, so the 8 bits are only the final rounding step rather than a
setting that was chosen. Those rows show — rather than a
number that would invite reading the two as like-for-like.