Build1 publisher3 min readPublished
Chrome's canvas.toBlob writes byte-identical PNGs at quality 0.1 and 1.0
Headless Chrome 149 wrote the same 1,345,909-byte PNG via canvas.toBlob at quality 0.1 and at 1.0, one developer's measurements show. A PNG export slider only changes the file if the tool downscales, quantises colours or switches to WebP or AVIF.
The Engineer · Build desk

What happened
- A developer who builds client-side image tools encoded one 1200x800 canvas repeatedly with canvas.toBlob in headless Chrome 149 on Linux, changing only the quality value.
- PNG output was 1,345,909 bytes at quality 0.1 and at 1.0, and a separate 200x200 canvas also matched itself at both settings.
- On the same pixels at quality 1.0, JPEG came out at 962,887 bytes and WebP at 477,822 bytes.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Image tools that promise smaller PNGs have to tie the slider to a downscale, an external quantiser or a format switch, since toBlob gives them no PNG size control in Chrome.
- constraint The byte-identical result covers one Chrome build on Linux, so any statement about other browsers rests on the lossless-format argument alone until someone measures them.
- exposure Tools that re-encode through canvas publish photos without their location data, and colour-managed pipelines lose ICC profiles with no error raised.
The call is `canvas.toBlob(callback, type, quality)`. For JPEG and WebP, the post explains, the quality value is a quantisation target. PNG is lossless and has no such target, so Chrome's PNG encoder takes the value and throws it away [5]. A slider wired to that argument on PNG export is at least perfectly reproducible. Two canvases of different sizes each produced one byte count across the range: 1,345,909 bytes for the 1200x800 test image and 17,279 bytes for a 200x200 one [3][4].
I'd expect the PNG result to hold for any image in that build, because the encoder never reads the value. The browser coverage is narrower. Every run used headless Chrome 149 on Linux [1]. The author wrote that the quality field "does nothing, in any browser" [16], a conclusion drawn from PNG being a lossless format. The post does not include measurements from another browser.
The lossy rows are claims about one synthetic image: a three-stop gradient, 6,000 semi-transparent 4x4 noise rectangles and 72px monospace text [2]. On those pixels, WebP at quality 1.0 came out at 49.6% of the JPEG size [1]. At 0.8 it was 70.8% [2]. Dropping JPEG from 1.0 to 0.8 cut its output by about 90% [3]. The author calls the image a compression-friendly worst case for JPEG and says flat, vector-like artwork shrinks the percentages a lot [8]. For the ratios to transfer, the content has to be photographic and the encoder has to be Chrome's. Canvas JPEG stays lossy even at 1.0, so bit-accurate output means PNG or lossless WebP [9].
What does shrink a PNG, per the post, is fewer pixels, fewer colours or a different format [10]. Downscaling is the lever the author says always works. Palette quantisation needs a real PNG encoder such as UPNG or ImageQuant, outside toBlob. WebP and AVIF both keep transparency [10]. On the test image, the PNG was 19.7 times the size of the quality-0.8 WebP [4].
The same call changes things no slider controls. toBlob encodes the RGBA pixels on the canvas, not the source file, so EXIF, GPS, XMP and the ICC profile do not survive [11]. JPEG has no alpha channel. Transparent regions come out black unless the canvas is painted first, for example with a white `ctx.fillRect` before `drawImage` [12]. Animated GIF, APNG and animated WebP input flattens to whichever frame was showing when it was drawn [13].
An SVG-to-PNG converter runs this path [14]:
1. Serialise the SVG and load it into an `<img>`. 2. `drawImage()` it onto a canvas at the target pixel size. 3. Call `canvas.toBlob(cb, 'image/png')` and hand the result to the user.
The quality argument is discarded at step 3 [14]. The setting that changes the output file is the pixel size chosen at step 2 [10]. "If you see a PNG quality slider, it is decoration," the author wrote [15].
What to watch
- The same toBlob runs repeated outside Chrome, to test the author's claim that the PNG quality field does nothing in any browser.
- JPEG and WebP ratios re-measured on flat vector artwork, where the author expects the size differences to shrink a lot.
- AVIF numbers from the same harness: the post recommends AVIF for photos, but its table covers only PNG, JPEG and WebP.