DEV Community

yue xing
yue xing

Posted on

Only one of four browsers gave me AVIF from canvas.toBlob, and that one gave me PNG for WebP

My side project has an export button with a format dropdown: PNG, JPEG, WebP, AVIF. AVIF is a newer image format that people pick because the files come out small. I added it without much thought, because every browser I use can display AVIF files. The export goes through canvas.toBlob(), the canvas method that hands the drawing back as a file-like object in the format you name. My code named the download after whatever the dropdown said. That turns out to be how you end up with a file called something.avif that is a PNG inside.

I found this out from a small test page I wrote before shipping. It draws one 640×360 canvas and asks for image/avif and image/webp at quality 0.8, then writes down blob.type, which is the format label on the object you get back, and the byte size. I ran it in Chromium 149, Chrome 155, Firefox 151 and WebKit 26.5 on one Apple M4 Mac.

Table of four browsers: toBlob('image/avif') returns image/png in Chromium 149, Chrome 155 and Firefox 151 and image/avif in WebKit 26.5; toBlob('image/webp') returns image/webp in the first three and image/png in WebKit 26.5

The AVIF column is red for three rows and green for the last one, and the WebP column is the exact mirror. No browser threw an error for any cell.

Asked for AVIF, Chromium and Chrome each returned 178,010 bytes of PNG, the same byte count I got when I asked for PNG outright. Firefox returned a PNG too, 35,415 bytes. Only WebKit 26.5 returned something labelled image/avif, at 2,256 bytes. I repeated the request through toDataURL(), which gives the image as a text string, and through OffscreenCanvas.convertToBlob(), the version of the same call for a canvas that isn't on the page. Both agreed with toBlob() in every browser. Then I asked for WebP and the result flipped: the three browsers that had refused AVIF produced WebP, and WebKit 26.5 handed back a 210,438-byte PNG. It felt like a jukebox that plays track one whenever you punch in a number it doesn't have, and each of these jukeboxes is missing a different number. In this test neither AVIF nor WebP came back from all four.

What threw me most is that being able to open a format told me nothing about being able to write it. I decoded a small AVIF file with createImageBitmap(), a function that turns image bytes into pixels, and that worked in all four browsers. Three of those four still gave me PNG when asked to produce one. I didn't look up why, so I can only report what these builds did. Firefox's fallback PNG being about a fifth the size of Chromium's for the same canvas distracted me for a while, and I didn't chase that either.

I'd be careful with the WebKit result. 2,256 bytes next to 178,010 looks like a huge win, but I recorded only the type and the size of that file. I didn't measure how it looks or how long the export took, so I can't say whether WebKit's built-in AVIF is good. WebKit 26.5 here is a WebKit build on macOS, not a Safari release. I haven't tested Safari itself, Edge, a release build of Firefox, Windows, or any phone.

I wanted to see how a tool that already ships AVIF export gets around the gap. I used ImgIng (https://imging.ai/) in Chromium 149, one of the browsers that gave me PNG, and converted a 2-megapixel PNG of a lake photo. During the conversion the button text went through Downloading module…, Initialising core…, Reading image… and Encoding AVIF…, and the output was a real AVIF of 60,326 bytes. The module it downloads is an encoder compiled to WebAssembly, which is code a page fetches and runs itself. I had watched the network requests for the same step on its Chinese-language site: there the download started the moment I selected AVIF, before I clicked convert, and the file was about 1.1 MB gzipped. I ran this once and only in Chromium 149, so I don't know what it does in WebKit, and I didn't look at how it is written inside. I'm not quoting how long it took either, because all I could time from outside was the whole wait and not the encode call.

Result panel of the converter showing Conversion complete, 2.65 MB to 58.9 KB, a Saved 98% badge, and the line 1738×1150 · WASM AVIF encoder

The line to look at is the small grey one, "1738×1150 · WASM AVIF encoder", which says where the file came from. The 98% badge compares against the PNG I fed in, which was large to begin with, so I don't read it as a statement about AVIF. The lake photo is by Jakub Fryš, from Wikimedia Commons, under CC BY-SA 4.0.

In my own export code I now compare blob.type with the type I asked for before I name the file, and I no longer hardcode the dropdown. The page asks a tiny canvas for each format once and lists the ones that come back as themselves. For AVIF in the three browsers that said PNG, what's left is an encoder of about 1 MB that the page has to download, and I haven't decided if my project needs that.

Top comments (0)