Dev.to WebDev 🛠 Dev 👁 0 📖 2 min read

Building a Client-Side Image Compressor: No Server, No Upload, Just JavaScript

My most-used tool shrinks images without sending a single byte anywhere. Here's the 30 lines of JavaScript that do it. Every image compressor you've used uploads your photo to a server, compresses it there, and sends

My most-used tool shrinks images without sending a single byte anywhere. Here's the 30 lines of JavaScript that do it.

Every image compressor you've used uploads your photo to a server, compresses it there, and sends it back. That works — but it means your photos leave your device.

I built the Image Compressor on Toolenix to do the whole job in the browser. No upload, no server, no waiting on a network. Here's how it works.

The core idea: canvas does the work

The browser's <canvas> element can decode an image, redraw it at a smaller size, and export it as a compressed JPEG — all locally. That's the entire trick. Three steps:

  1. Read the file into an <img> with URL.createObjectURL.
  2. Draw it onto a canvas at the target dimensions.
  3. Export with canvas.toBlob() at a quality setting you choose.

The code

Here's the heart of it — a function that takes a file and a target width, and returns a compressed blob:

async function compressImage(file, maxWidth = 1920, quality = 0.8) {
  const img = await loadImage(file);

  // Keep the aspect ratio, shrink only if larger than maxWidth
  const scale = Math.min(1, maxWidth / img.width);
  const width = Math.round(img.width * scale);
  const height = Math.round(img.height * scale);

  const canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;

  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0, width, height);

  // toBlob with 'image/jpeg' + quality does the compression
  return new Promise((resolve) => {
    canvas.toBlob(resolve, 'image/jpeg', quality);
  });
}

function loadImage(file) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = URL.createObjectURL(file);
  });
}

Quality 0.8 is the sweet spot in my experience: files shrink dramatically with no visible difference for web use. Drop to 0.6 for thumbnails, raise to 0.9 when quality matters more than size.

The details that make it feel like a real tool

The compression is the easy part. The UX around it is what makes people come back:

  • Drag and drop — handle dragover/drop events, accept multiple files at once.
  • Before/after sizes — show the original KB and the compressed KB side by side. People love seeing "2.4 MB → 380 KB".
  • Batch download — loop over files, compress each, and offer a single ZIP or individual downloads.
  • Memory hygiene — call URL.revokeObjectURL() after you're done, or a long session leaks memory with every image.

Why client-side wins for tools like this

  • Privacy: a photo never leaves the device. For personal photos, that's the feature.
  • Speed: no upload time, no queue, no server round-trip. Compression of a typical photo takes well under a second.
  • Cost: static hosting. No server to scale, no bandwidth bill for other people's photos.
  • Reliability: nothing to go down. If the page loads, the tool works — even offline after the first load.

The limits (honestly)

Canvas compression isn't magic. It can't beat a dedicated encoder on every image, PNG transparency needs care (JPEG has no alpha channel — draw onto a white background first or keep PNG output), and very large images can still spike memory. For a free web tool, though, it's more than good enough.

Try it

The compressor is live at toolenix.com — free, no sign-up, and your images never leave your browser. The whole site is built on the same idea: 113 tools, all client-side.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.