Seven encodes, each decoded and checked
Pick a test image or open your own. The page draws it to a canvas, saves it as PNG and as WebP at quality 1.00 down to 0.30 using your browser's own encoder, then decodes each file and compares it with the source. Colour error is measured with every pixel placed on white, so a change hidden under full transparency doesn't count. Alpha error is the largest change in any pixel's opacity.
Two things this shows that averages hide. First, a browser's "quality 1.00" isn't the same everywhere: Chromium switches to the lossless coder at 1.00, so the exact column reads 0, while other engines may still use the lossy coder. Second, the smallest file isn't always lossy: on flat logos and screenshots, lossless WebP often beats lossy WebP at high quality, because the lossy coder has to spend bits on the ringing it creates around sharp edges.
Inside the file you just made
A WebP is a RIFF file: a 12-byte header, then chunks. Which chunks appear tells you how the picture was stored. A VP8L chunk on its own means lossless, alpha included. A VP8 chunk means lossy. If the image has transparency, a lossy file also needs a VP8X header and a separate ALPH chunk, because the VP8 video format has no alpha channel. These are the actual bytes of ….
For every chunk type, every flag bit and a full inspector for any WebP, including animated ones, see the WebP format reference.
What your PNG carried, and what the WebP kept
The pixels are only part of a PNG. It can also hold a colour profile, a print resolution, text notes, EXIF data and animation frames. The original file is read chunk by chunk below. Each row then says what happens to that data when the image goes through a browser canvas, which is how this page and most online converters work, and what a command-line encoder can keep instead.
Up close: original against …
Lossy WebP works in 4×4 and 16×16 blocks and stores colour at half resolution (4:2:0). So the damage, if there is any, shows up at hard edges, in small text and in saturated colour against neutral backgrounds. The view is enlarged 5× with no smoothing. The original is on the left of the divider and the decoded WebP on the right. Drag the divider, or switch on the error view to see every changed pixel brightened 8×.
Ship it: the file, the markup, the command
Every current browser shows WebP, Safari included since version 14 (2020). A PNG fallback inside <picture> now mostly helps email clients, older apps and anything that downloads the image instead of displaying it. Sizes in the comments are the measured ones. Keep the PNG as your master: re-encoding a lossy WebP adds new loss every time.
<picture> <source srcset="logo.webp" type="image/webp"> <!-- --> <img src="logo.png" width="0" height="0" alt="" decoding="async"> <!-- --></picture>
cwebp -q 80 -alpha_q 100 -m 6 -sharp_yuv -metadata icc logo.png -o logo.webpmagick logo.png -quality 80 -define webp:alpha-quality=100 -define webp:method=6 logo.webpawait sharp('logo.png').webp({ quality: 80, alphaQuality: 100, effort: 6, smartSubsample: true }).toFile('logo.webp')Reference
-q 0-100Lossy quality (default 75). In lossless mode it sets effort instead. Browser q 0.80 is roughly the same as -q 80.-losslessUse the VP8L coder: exact pixels, alpha in-band.-z 0-9Lossless preset from fast to smallest. Sets -lossless, and -q / -m to match.-near_lossless 0-100Lossless coder after slight pixel pre-quantisation. 100 is off; 60 is often invisible and much smaller.-alpha_q 0-100Quality of the ALPH plane in lossy mode. 100 (default) keeps alpha lossless.-exactKeep RGB under fully transparent pixels. Without it, the encoder changes them to save bytes.-sharp_yuvSlower, more accurate RGB→YUV conversion. Reduces colour bleeding on sharp coloured edges.-m 0-6Encoder effort (default 4). 6 is slowest and smallest.-metadata all|none|exif|icc|xmpWhat to copy from the input. The default is none, so profiles and EXIF are dropped unless you ask.