By combining OffscreenCanvas with Web Workers, you can process images without blocking the main thread.
Technologies used
- Web Worker
- Offscreen Canvas
- Blob
- Bitmap
Interface
export interface WorkerRequestBody { buffer: ArrayBuffer; type: string; size: { height: number; width: number; };}
export interface WorkerResponseBody { buffer: ArrayBuffer; contentType: string;}Process an image inside a Web Worker
async function imageProcessing( data: WorkerRequestBody): Promise<WorkerResponseBody> { const { buffer, type, size } = data;
// Convert the ArrayBuffer back into a Blob const blob = new Blob([buffer], { type });
// Create an OffscreenCanvas const canvas = new OffscreenCanvas(size.width, size.height);
// Create a Bitmap from the Blob const bitmap = await createImageBitmap(blob);
// Get a CanvasRenderingContext2D const ctx = canvas.getContext("2d"); if (!ctx) throw new Error("Context2D is not defined");
// Draw the image onto the canvas ctx.drawImage(bitmap, 0, 0);
// Convert to WebP const webp = await canvas.convertToBlob({ type: "image/webp", quality: 0.85, });
// Convert the WebP Blob into an ArrayBuffer const buf = await webp.arrayBuffer();
return { buffer: buf, contentType: "image/webp", };}
self.addEventListener("message", async (e: MessageEvent<WorkerRequestBody>) => { const output = await imageProcessing(e.data); // Pass the second argument so ownership of the ArrayBuffer moves to the UI thread self.postMessage(output, [output.buffer]);});Pass an image to the worker and receive the converted image
import MyWorker from "./worker.ts";
const worker = new MyWorker();
const fileInput = document.querySelector<HTMLInputElement>("#js-file-input");
worker.addEventListener("message", (e: MessageEvent<WorkerResponseBody>) => { const { buffer, contentType } = e.data; const blob = new Blob([buffer], { type: contentType });
// do something});
fileInput.addEventListener("change", async (e) => { const [file] = Array.from(fileInput.files); if (!file) return;
const buffer = await file.arrayBuffer(); const type = file.type; const size = { height: 1080, width: 1980 }; // obtain image size
// Send to the Web Worker and transfer ownership worker.postMessage( { buffer, type, size }, [buffer] );});Key points
- OffscreenCanvas: a Canvas API that works inside Web Workers
- ArrayBuffer transfer: hand off ownership for efficient data passing
- convertToBlob: convert to formats such as WebP using OffscreenCanvas
Summary
By combining OffscreenCanvas and Web Workers, you can run image processing without blocking the main thread.
When you need to process many images or run expensive conversions, this approach helps preserve UI responsiveness and improves the user experience.
hsb.horse