In sharp, AVIF bytes grow fast with quality: a 1920 × 1280 photo of 576.8 KB came out at 54.4 KB at q30, 119.0 KB at q50 (sharp's default) and 332.7 KB at q80. That is 6.1 times the bytes from the bottom of the ladder to the top. Encode time stayed between 0.6 and 1.2 seconds on an M1 Pro at the default effort.
What does each quality setting cost?
The script, run from a folder with sharp installed:
import sharp from 'sharp'; import { readFile } from 'node:fs/promises'; const input = await readFile('jpg-sample-photo-original.jpg'); const median = (a) => [...a].sort((x, y) => x - y)[Math.floor(a.length / 2)]; await sharp(input).avif({ quality: 50 }).toBuffer(); // warm-up console.log('quality bytes KB ms(median of 5)'); for (const q of [30, 40, 50, 60, 70, 80]) { const times = []; let bytes = 0; for (let i = 0; i < 5; i++) { const t = performance.now(); const out = await sharp(input).avif({ quality: q }).toBuffer(); times.push(performance.now() - t); bytes = out.length; } console.log(`q${q}`.padEnd(9), String(bytes).padEnd(8), (bytes / 1000).toFixed(1).padEnd(7), median(times).toFixed(0)); } What it printed:
quality bytes KB ms(median of 5) q30 54361 54.4 980 q40 80292 80.3 624 q50 118975 119.0 711 q60 172876 172.9 779 q70 245756 245.8 971 q80 332733 332.7 1215 | Quality | Bytes | KB | vs the JPEG | Encode, median of 5 |
|---|---|---|---|---|
| q30 | 54,361 | 54.4 | 9.4% | 980 ms |
| q40 | 80,292 | 80.3 | 13.9% | 624 ms |
| q50 | 118,975 | 119.0 | 20.6% | 711 ms |
| q60 | 172,876 | 172.9 | 30.0% | 779 ms |
| q70 | 245,756 | 245.8 | 42.6% | 971 ms |
| q80 | 332,733 | 332.7 | 57.7% | 1,215 ms |
Why do the bytes curve upward?
Each step of 10 adds more than the one before: 25.9 KB, then 38.7, 53.9, 72.9 and 87.0 KB. The top step, q70 to q80, costs more bytes than all of q30 holds.
The encoder spends those bytes on detail you see less and less. At low quality it drops fine texture and noise first. At high quality it keeps them, and noise is expensive to store.
What do the steps look like?
The same 360 × 360 crop of the iguana's back, decoded from three of the encodes and enlarged 1.33 times with nearest-neighbour so pixels stay visible:
At q30 the small scales smear together and the spines lose their edges. At q50 the scale pattern is back. Side by side, q50 and q80 are hard to tell apart, and q80 costs 2.8 times the bytes.
Note: AVIF quality numbers do not line up with JPEG quality numbers. q50 in AVIF is not "half" of anything, and a JPEG q80 habit carried over to AVIF buys a much larger file than you need. Pick an AVIF setting by looking at your own photos, or by a metric target, never by the number you used for JPEG.
Why was q30 slower than q40?
I expected time to rise with quality. It mostly did, from 624 ms at q40 to 1,215 ms at q80. But q30 took 980 ms, slower than q40, 50 and 60. It did so in a first run too (1,045 ms against 698 ms at q40), so it is not a one-off.
I have no confirmed cause. A guess: at very low quality the encoder tries more ways to fit the budget. Treat it as a reason to time your own settings, not as a rule.
What I can say: sharp's output docs give quality a default of 50 and effort a default of 4, on a scale of 0 to 9. Effort is the knob for time; quality is the knob for bytes. I held effort at 4 for this whole ladder.
Which quality should you ship?
It depends on what the image does on the page.
- Hero and product photos at q50 to q60. Here that is 119.0 to 172.9 KB, against 576.8 KB for the JPEG.
- Thumbnails and cards at q40. Small display sizes hide the smoothing; 80.3 KB here at full size, far less once resized.
- q70 and up for photography sites where the detail is the product, and only after you compare.
For a reference point from a different encoder: Blinkimg's engine, which picks a quality from the content, wrote this same JPEG as a 123.9 KB AVIF. That sits just above sharp's q50. If you would rather not pick a number, the JPG to AVIF converter makes that call per image.
And if you are deciding whether AVIF is worth it at all, what AVIF is and when it loses covers the cases where WebP or JPEG come out smaller.
How I measured
- Apple Silicon Mac (M1 Pro), 2026-10-17, Node 24.22.0, sharp 0.35.4, libvips 8.18.6, libaom 3.15.0.
- Source:
jpg-sample-photo-original.jpgfrom the Blinkimg site, 1920 × 1280, 576,828 bytes, a real camera photo. - Times: median of 5 encodes after one warm-up. Other jobs were using the CPU, so compare the times with each other, not with your server.
- The 123.9 KB figure comes from Blinkimg's measured engine output (
measured.ts,compressJpg.asAvif).
What I did not test
One photo is not every photo; a screenshot or a noisy night shot will curve differently. I did not score quality with SSIM or Butteraugli, and I did not vary effort.
I build Blinkimg, an image compressor and converter with a developer API. I wrote this article with help from an AI assistant (Claude), which ran the code on my machine; every number in it comes from those runs.


