Which browsers can run a browser-based HEIC converter?

I ran the same photo through two browsers, then disabled nine browser features one at a time to find out what it actually needs. Four of them turned out to be load-bearing.

Chrome 153 and Edge 153: same bytes No WebAssembly, no WebCodecs Four features required

Short answer: four ordinary things — Web Workers, a 2D canvas that can write a JPG (toBlob), blob URLs, and plain JavaScript. That is the whole list, and I confirmed it by breaking each one: disabling Worker, toBlob, getContext("2d") or createObjectURL makes the conversion fail within about a second, while disabling createImageBitmap, fetch, TextDecoder or FinalizationRegistry changes nothing.

What I measured on this site: the same HEIC — 41,465 bytes, 700×476 — converted at the default 85% in Chrome 153 and in Edge 153. Chrome took 1,264 ms, Edge 1,488 ms, and both produced a 38,078-byte JPG with an identical SHA-256 (93859de257dc44a7…). Two different browsers, one file out. I also read through the 2,999,737-byte decoder the page ships: it contains zero references to WebAssembly, ImageDecoder, VideoDecoder, SharedArrayBuffer or OffscreenCanvas.

Measured: two browsers, one file

BrowserConvert timeWall clockJPG outSHA-256
Chrome 1531,264 ms1,309 ms38,078 bytes93859de257dc44a7…
Edge 1531,488 ms1,580 ms38,078 bytes93859de257dc44a7…

Both are the same engine underneath, so identical output is what you would hope for — but it is worth having measured: the result does not depend on which Chromium browser you use. Both produced 700×476, the full resolution of the source.

Measured: what happens when you take a feature away

This is the useful part, because it says what a browser has to provide. Each row is a fresh page with one browser feature disabled before any page script ran, then the same file pushed through:

Feature disabledResultTimeWhat the page said
Nothing (control)Converted — 38,078 bytes1,310 ms—
WorkerFailed117 msWorker disabled
createImageBitmapConverted — 38,078 bytes1,453 ms—
canvas.toBlobFailed1,077 mscanvas.toBlob is not a function
canvas.getContext("2d") returns nullFailed1,233 msCannot read properties of null (reading 'putImageData')
fetchConverted (JPG produced)972 ms—
URL.createObjectURLFailed81 msURL.createObjectURL is not a function
FinalizationRegistryConverted — 38,078 bytes1,077 ms—
TextDecoderConverted — 38,078 bytes760 ms—

Read the failures in order of how quickly they arrive. URL.createObjectURL kills it in 81 ms — the page cannot even hand you the finished file. Worker kills it in 117 ms, before any decoding starts. The canvas failures come later, after the decode has already happened: the pixel data is there, and it is the writing out that breaks. That last one is the clearest evidence of the pipeline — decode in a worker, draw to a canvas, encode the canvas to JPG, hand it over as a blob URL.

One footnote on the fetch row: the conversion succeeded and the download link appeared, but I could not measure the output size in that run, because my measuring code uses fetch too.

What it does not need — from the decoder itself

I counted occurrences in the 2,999,737-byte decoder file the page ships and in the page’s own 14,630 bytes:

FeatureIn the decoderWhat that means
WebAssembly0No WASM module is loaded at runtime
ImageDecoder / VideoDecoder (WebCodecs)0It does not borrow the operating system’s HEIC support
SharedArrayBuffer / Atomics0No shared-memory threading, so no special headers needed
OffscreenCanvas0Ordinary canvas is enough
FileReader0Not used
crypto.subtle0No hashing or encryption

The word WebAssembly does appear four times in the page — all of it in the site’s own description text, none of it in the decoder. That matters for one specific reason: a converter built on WebCodecs would depend on whether your operating system can decode HEIC, which is exactly the thing that fails on Windows without Apple’s codec or on Linux without the right package. This one carries its own decoder, which is why the same page behaves the same on machines with no HEIC support at all — the reasoning behind the macOS comparison and the Android, Chromebook and Linux runs.

What this does not tell you

Questions

Does it need WebAssembly?

No. Zero references in the decoder, and disabling browser features it does rely on never surfaced a WASM dependency. The decoder is JavaScript on the wire — it is an Emscripten build with isWasm2js set, meaning libheif was compiled to WebAssembly and then translated into JavaScript.

Does my operating system need HEIC support?

No. Nothing in the decoder calls WebCodecs or any OS decoder, so the page does not care whether your machine can open a HEIC file. That is measured in this page’s API scan, not inferred.

Will it work in Safari or Firefox?

I do not know. Neither was tested today — no Mac for Safari, and I could not automate the installed Firefox. I would rather say that than guess.

Why does it need a Worker?

Because that is where the decoding happens — disabling the Worker constructor fails the conversion in 117 ms, before anything else runs. It also explains a side effect I found in the mobile measurements: slowing the page’s main thread barely changes the conversion time, because the work is not on it.

Do I need to enable any browser flags?

Nothing in the list of required features is behind a flag in the browsers I tested: Worker, 2D canvas, toBlob and blob URLs all worked in a stock Chrome and a stock Edge with no settings touched.

Whichever browser you are in: open the converter and drop a HEIC in. It brings its own decoder, so your operating system does not need one.

First published: 27 September 2026