Build a Privacy-First Image Compressor That Runs Entirely in Your Browser
The Problem Every online image compressor uploads your files to a server. That means: Your images pass through someone else's infrastructure Compression takes time due to upload/download Privacy-sensitive images (screenshots, documents) leave your device The HTML5 Canvas API can compress images enti
Profiterole
The Problem
Every online image compressor uploads your files to a server. That means:
- Your images pass through someone else's infrastructure
- Compression takes time due to upload/download
- Privacy-sensitive images (screenshots, documents) leave your device
The Solution: Canvas API
The HTML5 Canvas API can compress images entirely in the browser. No server. No upload. Your images never leave your device.
Here's the core technique:
function compressImage(file, quality = 0.7, maxWidth = 1920) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
let { width, height } = img;
// Resize if needed
if (width > maxWidth) {
height = (height * maxWidth) / width;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(resolve, "image/jpeg", quality);
};
img.src = URL.createObjectURL(file);
});
}
Key Insights
Quality vs File Size
- 0.8 quality: Usually 60-70% smaller with minimal visible difference
- 0.6 quality: 80%+ smaller, noticeable on close inspection
- 0.4 quality: Aggressive compression, good for thumbnails
Format Matters
- JPEG: Best for photos (lossy, great compression)
- WebP: 25-35% smaller than JPEG at same quality
- PNG: Lossless โ Canvas can't truly compress PNGs
Batch Processing
Use Promise.all() with a concurrency limit to compress multiple files without freezing the browser:
async function batchCompress(files, quality, concurrency = 4) {
const results = [];
for (let i = 0; i < files.length; i += concurrency) {
const batch = files.slice(i, i + concurrency);
const compressed = await Promise.all(
batch.map(f => compressImage(f, quality))
);
results.push(...compressed);
}
return results;
}
Try It
I built a free, open-source image compressor using this technique:
Browser Image Compressor โ drag and drop, batch processing, quality control, ZIP download. Zero server uploads.
Also check out the OG Image Generator for creating social media cards.
All tools at Profiterole Dev Tools
Found this useful? Share it!
Read the Full Story
Continue reading on Dev.to
Related Stories
Majority Element
about 2 hours ago
Building a SQL Tokenizer and Formatter From Scratch โ Supporting 6 Dialects
about 2 hours ago
Markdown Knowledge Graph for Humans and Agents
about 2 hours ago

Moving Beyond Disk: How Redis Supercharges Your App Performance
about 2 hours ago