Re-encoding is not optimising
Most browser-based image tools work the same way: decode your file onto a canvas, then ask the canvas to write it back out. That is the right trade for changing a format — it costs no download and the browser's decoders are excellent. It is the wrong tool for making a file smaller, and the numbers are not close.
We measured it. A flat interface graphic of 21,493 bytes came back out of a canvas PNG encode at 38,905 bytes — 81% larger than the file we put in. A one-megabyte photograph came back 23% larger. Not corrupted, not visibly different: just bigger, because a general-purpose encoder writing a PNG in a hurry makes different choices from one whose entire job is to find the smallest legal encoding of the same pixels.
So the PNG path here never touches a canvas. The bytes you drop go straight into oxipng and the bytes it returns come straight back. On that same flat graphic oxipng produced3,350 bytes — 91% smaller than the canvas output and 6.4 times smaller than the file the user brought in. On the photograph it took the canvas output from 1,236 KB down to 864 KB, a 30% saving, and still beat the original source file by 14%. Every pixel identical.
Two engines, two different promises
The tool picks the engine from the file you give it, and the two promises are not the same, so they are not presented as if they were.
oxipng is lossless. It handles PNG, and it handles GIF and BMP by decoding them once and writing an optimised PNG instead — a 256-colour GIF is usually smaller as an optimised PNG than it was as a GIF. Its only control is effort, which costs time and nothing else. The default is level 2 because the measurements say so: level 2 took 0.97 seconds for 884,853 bytes on a test photograph, and the maximum took 3.45 seconds to reach 864,362. That is 2.3% more saving for three and a half times the work. A default should never be the slow one.
MozJPEG is not lossless. It handles JPEG, it is genuinely better than the browser's encoder — 18% smaller on identical pixels at quality 75 — and it works by throwing detail away. The tool says so next to the slider rather than in a footnote, because "compressed" and "unchanged" are different claims and only one of them is true here.
Why there is no "we beat the browser by X%" badge
Because it would sometimes be a lie, and we know exactly when. On the same source pixels, MozJPEG at quality 75 produced 113,935 bytes against Chromium's 139,476 — an 18% win. At quality 92 MozJPEG produced 188,448 bytes against Chromium's 176,567 — a 7%loss. Same encoder, same image, same dial, opposite verdict.
The reason is simple and widely ignored: a quality number is one encoder's private dial, not a shared scale. Chromium's 92 and MozJPEG's 92 do not describe the same amount of information discarded, so comparing two encoders at the same number compares nothing. Any tool showing you a same-number comparison is either unaware of this or counting on you being unaware of it. What you get instead is the two images at the same size on the same background, and the real byte figure underneath each one.
What it refuses, and why that is the feature
WebP and AVIF are turned away. They are already compressed by encoders better than either of ours, and routing them through a PNG or JPEG path would reliably produce a bigger file. Animated GIFs and animated PNGs are turned away too, for a worse reason: decoding an animation in a browser hands back the first frame and drops the rest silently, so the tool would report a spectacular saving for having destroyed the animation. JPEGs, GIFs and BMPs past the canvas ceiling — roughly 8,192 pixels on a side, or about 16 megapixels — are refused rather than quietly scaled down, because a silent resize sold as compression is the same dishonesty in a different coat. A PNG never goes through a canvas, so it has no such ceiling beyond your device’s memory.
If what you actually need is a different format rather than a smaller file, that is a different job: the image converterhandles WebP, JPEG and PNG output and can resize on the way through, and the rest of the set lives under converters.
When a file comes out bigger
It happens, and the number turns red with a plus sign in front of it. A JPEG saved at high quality, a PNG that has already been through an optimiser, a small icon where the format's own overhead is a large share of the file — all of these can come out larger, and all of them are telling you something useful: there was nothing left to take. A compressor that only ever reports savings is not measuring anything, so the honest reading is to keep your original for those files. The batch total is computed by the same arithmetic, which means a single bad file can sit inside a good total and both figures are true.
Questions people ask
Straight answers. Each one links directly: copy the address after opening it.
Is this actually lossless?
For PNG, GIF and BMP, yes. Those go through oxipng, which rewrites how the file is stored — the filter applied to each scanline, the palette, the bit depth, the ordering of the compressed stream — without touching a single pixel value. Decode the result and the original side by side and every channel of every pixel matches. JPEG is the exception and is labelled as such: MozJPEG is a lossy encoder, and the detail it discards to save bytes does not come back.
Why does it refuse my WebP and AVIF files?
Because neither of the two engines here can beat them. WebP and AVIF are already modern compressed formats; re-encoding one as a PNG or a JPEG makes it bigger, every time. A compressor whose honest answer is "+40%" should not have accepted the file in the first place. If you need a WebP turned into something else, that is a conversion rather than a compression, and the image converter does it.
Why not just re-save the PNG in the browser?
Because the browser’s PNG encoder is not an optimiser. We measured it: running a flat interface graphic through a canvas round-trip produced a file 81% larger than the source, and a photograph came out 23% larger. Any tool built that way is an inflater on a meaningful share of real inputs. The PNG path here never touches a canvas — the bytes you dropped go straight into oxipng and the bytes it returns come straight back out.
What do the oxipng levels do?
They buy search effort, not quality — the output is lossless at every level, so a higher level only costs time. On a 1600×1000 photograph, level 2 took about a second and produced 884,853 bytes; the maximum took three and a half seconds and produced 864,362. Level 2 captures roughly 98% of the available saving in under a third of the time, which is why it is the default. WebAssembly runs two to three times slower than those native figures, so the higher levels are noticeably slow on large images.
Is MozJPEG’s quality 75 the same as my camera’s quality 75?
No, and this matters more than it sounds. A quality number is an encoder’s private dial, not a shared scale. On identical pixels MozJPEG came out 18% smaller than Chromium’s canvas encoder at "75" and 7% larger at "92" — same number, different meaning, opposite verdict. Judge the result in the before-and-after panes rather than trusting the number to mean what it meant somewhere else.
Why did one of my files get bigger?
Because it was already efficient and there was nothing left to take. That happens with JPEGs saved at a high quality, with PNGs that have already been through an optimiser, and with small images where format overhead is a large share of the file. The per-file figure turns red and shows a plus sign when it happens. A compressor that only ever reports savings is not measuring anything — keep your original for those files.
What happens to animated GIFs and animated PNGs?
They are refused, deliberately. Decoding an animation in a browser gives you the first frame and silently discards the rest, so a forty-frame GIF would come back as a still image reported as a 95% saving. That is a data-loss bug dressed up as a result. The file is turned away at the door with the reason written next to it.
Is there a file count limit?
No. Nothing is uploaded, so there is no server time to meter. Files are processed one after another rather than all at once, because two WebAssembly codecs decoding large images in parallel is the fastest route to exhausting a phone’s memory and losing the tab. The practical ceiling is your own machine.