Front-end Engineering Lab

Image Compression (Canvas)

Reduce image file size on the client before upload using Canvas API.

The Problem

Users often upload high-resolution photos (5MB or larger) directly from phones. This wastes bandwidth and slows down uploads.

Solution

interface CompressionOptions {
  maxWidth?: number;
  maxHeight?: number;
  quality?: number; // 0.0 to 1.0
}

async function compressImage(
  file: File,
  options: CompressionOptions = {}
): Promise<Blob> {
  const { maxWidth = 1920, maxHeight = 1080, quality = 0.85 } = options;
  
  // Load image
  const imageBitmap = await createImageBitmap(file);
  
  // Calculate new dimensions (preserve aspect ratio)
  let { width, height } = imageBitmap;
  
  if (width > maxWidth || height > maxHeight) {
    const ratio = Math.min(maxWidth / width, maxHeight / height);
    width = Math.floor(width * ratio);
    height = Math.floor(height * ratio);
  }
  
  // Draw to canvas
  const canvas = new OffscreenCanvas(width, height);
  const ctx = canvas.getContext('2d');
  
  if (!ctx) throw new Error('Canvas context not available');
  
  ctx.drawImage(imageBitmap, 0, 0, width, height);
  
  // Convert to blob
  return canvas.convertToBlob({
    type: 'image/jpeg',
    quality
  });
}

// Usage
const originalFile = document.querySelector('input[type="file"]').files[0];
const compressedBlob = await compressImage(originalFile, {
  maxWidth: 1920,
  quality: 0.8
});

console.log(`Original: ${(originalFile.size / 1024 / 1024).toFixed(2)}MB`);
console.log(`Compressed: ${(compressedBlob.size / 1024 / 1024).toFixed(2)}MB`);

// Upload compressed version
const formData = new FormData();
formData.append('image', compressedBlob, originalFile.name);
await fetch('/api/upload', { method: 'POST', body: formData });

Performance Note

Benefit: A 4MB photo can be reduced to ~600KB without visible quality loss. This means 85% less bandwidth usage and faster uploads, especially on mobile networks.

On this page