IMAGE TOOLS
WebP vs PNG vs JPEG: Three Images, Measured
8 min read · ToolsBay editorial · Published · Updated
Just want to do it now?
Convert modern WebP images into widely-supported PNG files.
Every article about image formats ends with the same table: JPEG for photos, PNG for logos, WebP for everything. The table is roughly right and completely useless, because it never tells you what the choice is worth on your image. So here are three real files put through this site's own image compressor, with the byte counts it produced.
The three sources are a 1920x1200 landscape photograph that arrived as a 1,194,532-byte JPEG, a 1440x900 screenshot of this site's PDF merge tool saved as a 71,360-byte PNG, and this site's 512x512 logo, an RGBA PNG with a transparent background, 17,303 bytes. Every output below came out of Chromium 151's canvas encoders, which is what the tool runs in your browser. Nothing was uploaded anywhere to produce them.
What JPEG actually discards
The common explanation — that JPEG finds similar pixels and averages them into flat blocks — is not what happens, and believing it leads to bad format choices.
JPEG converts the image from RGB into one brightness channel and two colour channels, then usually throws away three quarters of the colour resolution outright by storing one colour sample per 2x2 block of pixels. That step alone is why a thin red line on a blue background smears in a JPEG while the black text beside it survives.
Then it cuts each channel into 8x8 blocks and runs a discrete cosine transform on every one. That does not compress anything; it re-describes the 64 pixels as 64 coefficients, from the block's average value up to its finest checkerboard detail. The compression is the next step: each coefficient is divided by an entry in a quantisation table and rounded to a whole number. The high-frequency entries are large, so fine detail rounds to zero, and long runs of zeros are what the entropy coder gets cheap. The quality slider scales that table.
Two things follow that the usual advice gets backwards. Blocking artefacts land on an 8x8 grid because that is the unit of the transform. And a smooth gradient is one of the worst cases, not one of the best — a gradient is a low-amplitude ramp, quantisation rounds it into steps, and you get visible banding across a sky. The old version of this post recommended JPEG for "dense gradients". That was wrong.
The photograph
source JPEG 1920x1200 1,194,532 B
JPEG q90 929,874 q75 510,922 q50 330,810
WebP q90 878,114 q75 476,780 q50 337,246
PNG 4,789,068Three things worth noticing. PNG produced a file four times larger than the JPEG it started from, because lossless coding of camera noise is close to hopeless. WebP beat JPEG by 6.7% at the same dial setting, not the 25-34% that gets quoted everywhere — that figure comes from Google's own WebP comparison study, which matched images by a structural similarity score rather than by quality number. And at q50, WebP came out larger than JPEG.
A quality number is a codec-specific instruction, not a unit of anything. Comparing two codecs at the same number tells you very little about how they compare at the same appearance, which is the comparison you actually care about.
Now the number that dwarfs all of them. The same photograph, scaled so its longest edge is 800 pixels and encoded at q75, came to 86,110 bytes as JPEG and 79,720 as WebP. That is 83% off the full-size q75 file, from a change that has nothing to do with format. If an image is displayed in an 800-pixel column, the format argument is worth a few tens of kilobytes and the resize is worth four hundred.
The screenshot
source PNG 1440x900 71,360 B
JPEG q90 108,766 q75 73,868 q50 53,533
WebP q90 65,388 q75 41,970 q50 34,698
PNG 115,512JPEG at q90 is half again as large as the PNG it replaced, and at q75 it is still bigger — while also looking worse, because UI screenshots are flat colour with hard type edges, exactly the content the 8x8 transform handles badly. WebP at q75 is 41% smaller than the source PNG and holds the text. This is the case where "use JPEG, it is smaller" is simply false, and where the file that ends up on the page is often a JPEG anyway, because somebody applied a rule instead of measuring.
The logo, and what "lossless" does not promise
source PNG 512x512 RGBA 17,303 B
JPEG q90 14,481 q75 8,668 q50 6,554 (alpha lost)
WebP q90 7,932 q75 5,480 q50 4,846 (alpha kept)
PNG 66,104JPEG has no alpha channel, so the tool composites transparency onto white before encoding. That is deliberate: the naive path decodes transparency as black and turns a logo's background into a black rectangle. If the logo sits on a white page you will not notice. On anything else you will. The same flattening happens in PNG to JPG, and it is the reason to reach for WebP instead when the background has to stay clear.
The PNG row deserves the rest of this section. Re-encoding a 17,303-byte PNG through a canvas produced 66,104 bytes — nearly four times bigger, for the same picture. PNG is lossless as a format, but lossless says nothing about how well a given encoder packs the result. Deflate settings and per-row filter choices vary enormously between encoders. Handed the same file, libvips wrote it back at 9,611 bytes. A browser's canvas encoder is built to be fast and correct, not small, which is why re-encoding a PNG as a PNG at the same size through a canvas-based compressor — this site's included — will not shrink it. Scale it down, or convert it to WebP, instead.
There is a smaller surprise underneath. Comparing the canvas round-trip against the original pixel by pixel, 843 of the image's 1,048,576 samples came back off by one. Canvas stores alpha premultiplied, so a transparent PNG does not survive the trip bit-exact. It is invisible to the eye, and it matters if you are checksumming assets.
AVIF, and one trap that fails silently
AVIF is the strongest of the modern options: intra-frame AV1 coding, larger and more flexible transform blocks than JPEG's fixed 8x8, and many more prediction modes. Chrome has decoded it since 2020, Firefox since 2021, and Safari since version 16 in 2022, so it is safe to serve today behind a fallback.
This site's homepage screenshot exists in both formats, written from one source by libavif and libwebp: 36,843 bytes as AVIF against 52,042 as WebP for the hero, and 4,741 against 5,820 for the smaller one. Real files, in this repository, at the encoder settings the capture script uses.
What you cannot do is ask a browser canvas for one. Chromium 151 has no AVIF encoder behind canvas.toBlob, and the HTML specification says that when a requested type is unsupported the browser falls back to PNG. It does not throw. You get a PNG blob, and if your code names the download after the format you asked for, you now have a PNG called photo.avif — which browsers will still display, because they sniff the bytes, so nobody notices until someone wonders why the AVIF is bigger than the JPEG. That is why the compressor offers JPEG, WebP and PNG and stops there. AVIF needs a real encoder, which on the web means WebAssembly or a build step.
The delivery half nobody writes about
Choosing a format is half the job. The other half is not shipping every visitor the same file.
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" width="1520" height="1167"
loading="lazy" decoding="async" alt="...">
</picture>The browser takes the first <source> whose type it can decode, so older clients quietly get the JPEG and you never write a line of detection code. The width and height attributes have to be there or the page reflows when the image lands. And loading="lazy" belongs on images below the fold, never on the one at the top of the page, which you want fetched immediately.
One thing that catches people: hiding an <img> with display: none does not stop the download. The request is already in flight before your stylesheet has an opinion. This site's homepage hit exactly that — a hero image fetched in full on phones and then never shown, on the devices least able to afford it. The fix was a media attribute on each <source> so that below 640px no source matches at all and the browser asks for nothing.
The rule
There is no site-wide answer, only a per-image one. Photographs: WebP, falling back to JPEG, resized before anything else. Screenshots and interface captures: WebP, and check the number before assuming JPEG is smaller. Anything needing transparency: WebP, with PNG as the fallback for software that still refuses to open it — WebP to PNG exists for exactly that. Whatever the format, the resize is almost always the larger win, and the only way to know what any of it cost is to look at the bytes.