Speed Lab: JPEG, WebP and AVIF compared
One photograph saved as JPEG, mozjpeg, WebP, AVIF and PNG at matched quality, then loaded as the main image: file sizes, a quality score and LCP from nine runs.
In this guide
The question
Every guide on images says to use a modern format. How much does it buy for the main image of a page, and does the format change how quickly the image can be drawn? MDN says that lossy WebP files are on average 25% to 35% smaller than JPEG files of visually similar quality. The 2025 Web Almanac found that JPEG was the format of 57% of the Largest Contentful Paint images on the web, PNG 26%, WebP about 11% and AVIF 0.7%. So most main images are still JPEG, and a quarter are PNG. I wanted to see what the gap looks like on one real photograph.
How I made the images
The source is a photograph from the test files of the open-source image library sharp: a person cutting a wooden beam with a circular saw, 2,725 by 2,225 pixels, 829,183 bytes as a JPEG. It has fine detail, lettering and wood grain, which is hard for a compressor. I cropped it to a widescreen shape and resized it to 1,200 by 675 pixels. That master was saved as a lossless PNG of 1,705,917 bytes, and every other file was made from it.
I used sharp 0.35.5 (libvips 8.18.7) to write the files. A quality setting of 75 means different things in different encoders, so I did not compare at the same number. I measured how close each file is to the master with SSIM, using the ssim filter of FFmpeg on the decoded files (the All value, where 1 is identical), and picked the setting of each format that gave about the same SSIM. These are the sweeps around the target:
| Format | Quality | File size, bytes | SSIM |
|---|---|---|---|
| JPEG (libjpeg) | 60 | 68,156 | 0.9386 |
| JPEG (libjpeg) | 75 | 93,466 | 0.9533 |
| JPEG (libjpeg) | 90 | 169,016 | 0.9724 |
| JPEG (mozjpeg) | 60 | 51,196 | 0.9255 |
| JPEG (mozjpeg) | 80 | 86,492 | 0.9529 |
| WebP, effort 4 | 60 | 40,602 | 0.9224 |
| WebP, effort 4 | 75 | 50,096 | 0.9340 |
| WebP, effort 4 | 84 | 79,886 | 0.9548 |
| AVIF, effort 4 | 40 | 23,117 | 0.9161 |
| AVIF, effort 4 | 50 | 37,725 | 0.9407 |
| AVIF, effort 4 | 58 | 51,550 | 0.9529 |
The same quality number gives very different results: at 75, the JPEG scores 0.9533 and the WebP 0.9340. The four files I tested are the ones near 0.953: JPEG at 75, mozjpeg at 80, WebP at 84 and AVIF at 58, all within 0.002 of one another. The fifth is the lossless PNG.
The test
The page and the method are the same as in the lazy-loading lab: a header, a heading, the hero image with its width and height, a paragraph, a small style sheet and a deferred 52 KB script, served over plain HTTP/1.1 by a local server and measured in Lighthouse 13.5.0 with its mobile settings and devtools throttling, nine runs per variant taken in turns. The full method is in how the Speed Lab tests are run. Only the hero file changes. The server sent each file with the right content type and without extra compression.
Results
| Format | Hero size | Against JPEG | LCP, median (lowest to highest) | Against JPEG |
|---|---|---|---|---|
| JPEG, quality 75 | 93,466 bytes | 1,986 ms (1,977 to 1,996) | ||
| JPEG with mozjpeg, quality 80 | 86,492 bytes | 7.5% smaller | 1,945 ms (1,935 to 1,955) | 41 ms sooner |
| WebP, quality 84 | 79,886 bytes | 14.5% smaller | 1,920 ms (1,905 to 1,974) | 66 ms sooner |
| AVIF, quality 58 | 51,550 bytes | 44.8% smaller | 1,787 ms (1,768 to 1,903) | 199 ms sooner |
| PNG, lossless | 1,705,917 bytes | 18 times larger | 10,534 ms (10,517 to 10,550) | 8,548 ms later |
First Contentful Paint was between 1,213 and 1,225 ms in every variant, because the page paints its text long before the image. Cumulative Layout Shift was 0, and Total Blocking Time was 0 in all but one run of the AVIF page, which had 20 ms.
Time follows bytes
The Lighthouse timeline says when the hero request finished: 1,964 ms for the JPEG, 1,914 for mozjpeg, 1,888 for WebP, 1,740 for AVIF and 10,505 for the PNG. The download rate in this setup is 1,474.56 Kbps, which is about 184,000 bytes a second, or 5.4 ms for every 1,000 bytes. The AVIF file was 41,916 bytes smaller than the JPEG, which predicts a download 227 ms shorter. The measured difference was 224 ms. The WebP and mozjpeg files also arrived about where their sizes predict.
So on a slow connection, the bytes of the main image are close to the whole story, and every 10,000 bytes you remove is worth about 54 ms. On a faster connection the saving is smaller in milliseconds, and the percentage is the same.
Decoding
The LCP is a little later than the moment the file arrives, because the browser must decode and paint the image. That gap was 22 ms for the JPEG, 31 for mozjpeg, 32 for WebP, 29 for the PNG and 47 for the AVIF. The AVIF file takes longest to decode here, with the processor slowed four times, which is why its gain in LCP (199 ms) is smaller than its gain in download time (224 ms). The effect is small next to the saving, but it is a reason to test on a modest phone before you convert everything to AVIF. The AVIF runs also varied more, from 1,768 to 1,903 ms.
Resize first, then choose the format
The hero file is 1,200 pixels wide, but the page shows it about 380 pixels wide on the test phone, which is 665 device pixels at the phone's pixel ratio of 1.75. I made a second size, 800 pixels wide, from the same master as a JPEG and as an AVIF with the same settings, and let the browser choose with srcset and sizes. Four variants, nine runs each, on the same page and the same setup:
<!-- c: a choice of two JPEG sizes; d is the same with the AVIF files -->
<img class="hero" src="hero-1200.jpg" srcset="hero-800.jpg 800w, hero-1200.jpg 1200w"
sizes="(min-width: 720px) 688px, calc(100vw - 32px)" width="1200" height="675" alt="A person cutting a wooden beam with a circular saw">| Variant | Hero file | LCP, median (lowest to highest) | Against a |
|---|---|---|---|
| a. JPEG, 1,200 px | 93,466 bytes | 1,995 ms (1,975 to 2,016) | |
| b. AVIF, 1,200 px | 51,550 bytes | 1,793 ms (1,761 to 1,813) | 202 ms sooner |
c. JPEG with srcset | 47,648 bytes | 1,719 ms (1,707 to 1,729) | 276 ms sooner |
d. AVIF with srcset | 27,364 bytes | 1,525 ms (1,509 to 1,538) | 470 ms sooner |
The browser chose the 800-pixel file in variants c and d, as the request list in the Lighthouse report shows. The page weighed 143.7 KB in variant a and 79.3 KB in variant d. Three results stand out.
- Resizing the JPEG gained more than changing the format of the large file: 276 ms against 202 ms.
- The two together gained 470 ms, close to the sum of the two separate gains (478 ms).
- AVIF still helped at the smaller size: variant d was 194 ms sooner than variant c.
The ranges of the four variants do not overlap.
What the pictures look like
SSIM is one number, and it does not say how a picture looks. I viewed the files side by side at twice and three times their size. At twice the size the differences were small. At three times the size, on the wood grain, the AVIF file was the smoothest: it had lost some of the fine speckle that the master and the JPEG keep, while the grain lines and the lettering on the saw blade stayed clear. The JPEG and the WebP were close to each other. That is a judgment about one photograph at one setting, so look at your own images at the quality you choose.
What this test does not show
- One photograph. Other images compress differently. Flat graphics, screenshots and photos with soft focus give other ratios, and MDN's figure for WebP (25% to 35% smaller than JPEG) is higher than the 15% measured here.
- One quality measure. SSIM favors some encoders over others. A different measure could rank the files differently.
- Encoder settings. I used each encoder's ordinary settings and an effort of 4 for WebP and AVIF. A slower, more thorough setting makes smaller files and takes longer to encode.
- A generated page on a local server. See the method page for the limits of all these tests.
- Decoding on real phones. The slowed processor is a stand-in. A real low-end phone may take longer or shorter.
- The pixel ratio. The test phone has a ratio of 1.75 and needed 665 pixels for the image. A phone with a ratio of 3 needs about 1,140 pixels for the same slot and would take the 1,200-pixel file, so offer more than two widths. See responsive images.
What to do
- Send the size that is shown. Make several widths and let the browser choose with
srcsetandsizes. In the second test an 800-pixel file for a slot of 665 device pixels gained 276 ms. - Offer AVIF and WebP, with a JPEG fallback, so every browser gets a file it can read. A
pictureelement does it, and many CDNs and image services can do it for you:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" width="1200" height="675" alt="A person cutting a wooden beam with a circular saw">
</picture>- Compare at the same measured quality, not the same quality number, and look at the result at a larger size.
- Keep PNG for graphics with flat colors and sharp edges. For a photograph it was 18 times larger.
- Keep the original, and make the web files from it. Each time you re-save a lossy file you lose a little more.
- Measure the effect on your own page, in the Network panel and in Lighthouse. See image formats for the support notes.
Run it yourself
Install sharp with npm install sharp, then save this as make-formats.mjs and run node make-formats.mjs photo.jpg. It cuts the photo to 1,200 by 675 pixels and writes the four lossy files with the settings used above, and two files 800 pixels wide for the second test.
// make-formats.mjs: node make-formats.mjs photo.jpg
import sharp from 'sharp';
const master = await sharp(process.argv[2]).resize(1200, 675, { fit: 'cover' }).png().toBuffer();
await sharp(master).jpeg({ quality: 75 }).toFile('hero.jpg');
await sharp(master).jpeg({ quality: 80, mozjpeg: true }).toFile('hero-moz.jpg');
await sharp(master).webp({ quality: 84, effort: 4 }).toFile('hero.webp');
await sharp(master).avif({ quality: 58, effort: 4 }).toFile('hero.avif');
// The same photograph 800 pixels wide, for the srcset test.
await sharp(master).resize({ width: 800 }).jpeg({ quality: 75 }).toFile('hero-800.jpg');
await sharp(master).resize({ width: 800 }).avif({ quality: 58, effort: 4 }).toFile('hero-800.avif');To score a file against the master, save the master as master.png, decode the file to PNG, and ask FFmpeg for the SSIM:
ffmpeg -hide_banner -i decoded.png -i master.png -lavfi ssim -f null - 2>&1 | grep -o 'All:[0-9.]*'Then put each file in its own folder with the same page, and measure them with the runner on the method page.
Mistakes to avoid
- Comparing formats at the same quality number. The scales are different.
- Using PNG for photographs.
- Serving AVIF or WebP with no fallback where a visitor's browser may not read them.
- Converting to a new format and keeping the old, larger size. Resize first.
- Judging only by a score. Look at the picture.
Check your own page
Run the free audit and look at the image findings and the Largest Contentful Paint element. If the main image is a JPEG or a PNG, save it as AVIF or WebP at a size that fits the layout, and run the audit again.
Sources
- Image file type and format guide on MDN: the formats, and the average saving of lossy WebP over JPEG.
- Performance chapter, Web Almanac 2025: the shares of image formats among Largest Contentful Paint images.
- The `ssim` filter in the FFmpeg documentation: the structural similarity measure used to match the files.
Start here
Free website audit
Live Google data for any public page: mobile and desktop.
Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it
Rather have this done for you? Speed optimization · Book a call