How Browser-Native Image Compression Works via HTML5 Canvas
Modern web applications can compress and transcode images entirely on the client device without sending bytes over the network. When an image file is selected via an <input type="file"> or drag-and-drop event, the browser decodes the compressed bitstream into raw pixel data using the createImageBitmap() API or HTMLImageElement. This decoded bitmap is drawn onto an HTML5 <canvas> or worker-based OffscreenCanvas at desired target dimensions using the 2D rendering context (CanvasRenderingContext2D). Finally, the canvas canvas.toBlob(callback, mimeType, quality) method encodes the raw uncompressed RGBA pixel buffer back into an optimized target format (such as image/webp or image/jpeg) using the browser internal hardware-accelerated codec.
// Pure Client-Side Image Compression in TypeScript
export async function compressImage(
file: File,
quality: number = 0.8,
maxWidth: number = 1920,
maxHeight: number = 1080,
mimeType: 'image/webp' | 'image/jpeg' | 'image/png' = 'image/webp'
): Promise<{ blob: Blob; width: number; height: number; reductionPercent: number }> {
const imageBitmap = await createImageBitmap(file);
let { width, height } = imageBitmap;
// Maintain aspect ratio while constraining dimensions
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width = Math.round(width * ratio);
height = Math.round(height * ratio);
}
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('Failed to acquire canvas rendering context');
// Render high-quality scaled image
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(imageBitmap, 0, 0, width, height);
const blob = await canvas.convertToBlob({ type: mimeType, quality });
const reductionPercent = Math.max(0, ((file.size - blob.size) / file.size) * 100);
return { blob, width, height, reductionPercent };
}