createImageBitmap(source, options) decodes a Blob, image or canvas off the main thread into a ready-to-draw ImageBitmap, optionally resized or flipped, and works in workers. With OffscreenCanvas (Baseline widely available since September 2025) a worker can own a visible canvas: transferControlToOffscreen() hands it over.
<canvas id="c" style="width:600px; height:86px"></canvas>
<script type="text/worker" id="src">
onmessage = async ({ data: { canvas, blob, r } }) => {
const ctx = canvas.getContext('2d'); ctx.scale(r, r); ctx.font = '14px sans-serif';
const logo = await createImageBitmap(blob, { resizeWidth: 240 * r });
ctx.drawImage(logo, 0, 5, 240, 76);
ctx.fillText(`${logo.width} x ${logo.height} bitmap, drawn in a worker`, 260, 52);
postMessage('drawn');
};
</script>
<script>
const r = devicePixelRatio; c.width = 600 * r; c.height = 86 * r;
const canvas = c.transferControlToOffscreen(); // the worker now owns the pixels
const worker = new Worker(URL.createObjectURL(new Blob([src.textContent])));
worker.onmessage = () => setTimeout(() => __done = true, 100); // let the frame land
fetch('booknest-logo.png').then(res => res.blob())
.then(blob => worker.postMessage({ canvas, blob, r }, [canvas]));
</script>
The main thread only fetched bytes, so input stays smooth while images decode.