Convert WEBP to PNG
Take a WEBP apart and store its pixels in a PNG. The image does not improve and the file usually grows.
How it works
Converting WEBP to PNG is a compatibility fix rather than an upgrade. Something at the far end of the pipeline takes PNG and refuses WEBP — an icon builder, an upload form, a plugin older than the format itself — so the picture has to arrive in a container that program recognises. What comes out is exactly the pixels the WEBP decoded to, and that is a smaller promise than it first sounds.
Which of the two WEBPs you have
A WEBP file is a RIFF container: the ASCII tag RIFF, a length, the tag WEBP, then a list of tagged chunks, all of it documented in RFC 9649. The first image chunk decides what this conversion means. VP8 with a trailing space is the lossy bitstream, taken from VP8's intra-frame coding. VP8L is the lossless bitstream, added in 2012, and it shares no coding tools with VP8 at all: spatial prediction, colour transforms, a cache of recently used colours, then Huffman coding. VP8X marks an extended file whose image chunk sits further in, behind flags for alpha, colour profile, metadata and animation.
This tool reads that tag before it decodes anything and reports which one it found, because the honesty of the output is settled there. From VP8L the PNG receives the same values the WEBP encoder was handed, so the two files decode to an identical raster and only their byte counts differ. From a lossy chunk the PNG is accurate without being faithful: an exact record of an image that has already lost detail to quantisation and to chroma stored at half resolution on each axis.
Filters, then DEFLATE
PNG compresses in two stages, both specified in RFC 2083 and ISO/IEC 15948. Each row of pixels first passes through one of five filters — None, Sub, Up, Average or Paeth — which replace a byte with its difference from the byte on its left, the byte above it, or a blend of the two. The filtered rows then go through DEFLATE (RFC 1951): LZ77 matching across a 32 KiB window, then Huffman coding. Neither stage is allowed to discard anything, so every byte saved has to come out of the filtered rows being repetitive.
Screenshots and diagrams are repetitive in that specific way, and photographs are not. A run of one colour filters to a run of zeros, which DEFLATE collapses to almost nothing, and a smooth gradient predicts well from the row above it. Photographic texture leaves a residual that behaves like noise, and noise does not compress. A lossy source adds a second layer of the same difficulty, since block edges and ringing are high-frequency detail the filters cannot predict either.
Figures measured in this session, both from a 64×48 test image at effort 2. Stored as lossless WEBP, a smooth gradient is 72 bytes and its PNG is 139 bytes, under twice the size, because both formats are good at flat prediction. Stored as a quality-60 lossy WEBP the same gradient is 206 bytes and its PNG is 3455 bytes, sixteen times larger, because the PNG now has to encode the lossy encoder's block structure as though it were content.
Reading the delta
The result line reports the source size, the output size and the change between them, and for this conversion the change is normally a large positive number. A pseudo-random texture measured alongside the gradient went from an 1898-byte lossy WEBP to an 8673-byte PNG. Keep the WEBP wherever the destination can read it, since the conversion buys bytes and nothing else. Convert where the destination refuses the file, and treat the PNG as a delivery copy rather than an archive.
Transparency, animation and effort
PNG carries an 8-bit alpha channel, so transparency is kept unless you ask for it to be removed. Flattening exists for a destination that ignores alpha and reads the colour values underneath it, which in a lossy WEBP can be whatever the encoder happened to leave there. Compositing onto white or black first makes that choice yours. Measured on a 32×32 fill at half opacity: kept, the PNG is 104 bytes; flattened onto white, 90 bytes, with oxipng reducing both to an indexed palette.
Effort drives oxipng, a lossless recompression pass that runs after the PNG is written. It retries the per-row filter choices and the compression parameters, keeping whichever combination stores the same pixels in fewer bytes, so the image is untouched at every level and only time changes hands. On that same texture, converted from its lossless WEBP: level 0 produced 7706 bytes, level 2 produced 7620, and level 6 produced 7620 again after a considerably longer search.
An animated WEBP keeps its frames in ANMF chunks and a PNG holds one image. The tool counts the frames and declines, rather than returning a still that has quietly dropped the rest of the animation.
Questions
Will the PNG be larger than the WEBP?
Almost always, and frequently by several times. PNG stores every pixel exactly, including the artefacts a lossy WEBP introduced, so it spends bytes recording compression damage as though it were detail. In the figures measured for this page the increase ran from 1.12x on a lossless source to 16.8x on a lossy one. If nothing downstream demands PNG, the WEBP is the better file to keep.
Does this restore what a lossy WEBP threw away?
No, and nothing can: the discarded coefficients are not recorded anywhere in the file. What the PNG gives you is a stopping point, since edits and re-saves from here on lose nothing further.
Is anything uploaded?
No. Decoding and encoding both run in a Web Worker on your own machine, and the only requests a conversion makes are for the codec modules themselves. Watch the Network tab while you convert: nothing carrying image data goes out.
What happens with an animated WEBP?
It is declined, with the frame count named. Animation lives in ANMF chunks inside the container while a PNG holds a single image, so the alternative would be handing back frame one and saying nothing about the rest. APNG can hold an animation, but it is a different format and this tool does not write it.