{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreifmlb5hiw67cftidbppss66p46pvrseui2tixx2qb2xpikfboi5we",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpsskrwv32a2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreibcyupsonpnq3wmr7wrfu2qdojo2n7pv5422jdfryty7qe3sk7zyu"
},
"mimeType": "image/webp",
"size": 71908
},
"path": "/arhan_ahmad_a67ef7bd2b992/how-to-compress-images-in-the-browser-with-canvas-api-no-uploads-no-server-28j9",
"publishedAt": "2026-07-04T09:42:49.000Z",
"site": "https://dev.to",
"tags": [
"javascript",
"webdev",
"performance",
"tutorial"
],
"textContent": "# How to Compress Images in the Browser with Canvas API\n\nEvery image you upload to a \"free\" online compressor is sent to a server — often without you knowing what happens to it afterward. For a tool that processes your private photos, that's a terrible design.\n\nHere's how to build (or use) an image compressor that runs entirely in the browser using the HTML5 Canvas API. No uploads, no server costs, and unlimited file sizes.\n\n## The Core Technique: Canvas `toBlob()`\n\nThe key API is `HTMLCanvasElement.toBlob()`:\n\n\n js\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n\n const img = new Image();\n img.onload = () => {\n canvas.width = img.naturalWidth;\n canvas.height = img.naturalHeight;\n ctx.drawImage(img, 0, 0);\n\n canvas.toBlob((blob) => {\n const url = URL.createObjectURL(blob);\n }, 'image/jpeg', 0.8);\n };\n img.src = 'your-image.jpg';\n The second parameter is the MIME type (image/jpeg, image/png, image/webp, image/avif). The third is quality (0–1).\n Step-Down Resizing for Large Images\n If you're compressing a 6000×4000 px photo, drawing it at full resolution onto a canvas can eat 70+ MB of memory. Step-down resizing halves the dimensions repeatedly:\n function stepDownEncode(img, maxDim, quality) {\n let w = img.naturalWidth;\n let h = img.naturalHeight;\n let src = img;\n\n while (w > maxDim * 2 || h > maxDim * 2) {\n w = Math.floor(w / 2);\n h = Math.floor(h / 2);\n const temp = document.createElement('canvas');\n temp.width = w;\n temp.height = h;\n temp.getContext('2d').drawImage(src, 0, 0, w, h);\n src = temp;\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = w;\n canvas.height = h;\n canvas.getContext('2d').drawImage(src, 0, 0, w, h);\n\n return new Promise((resolve) => {\n canvas.toBlob((blob) => resolve(blob), 'image/jpeg', quality);\n });\n }\n This prevents memory crashes and actually produces better quality (step-down preserves more detail than a single jump).\n Comparing Real-World Results\n Format Avg Original Avg Compressed Avg Savings\n JPEG → JPEG (Q80) 3.2 MB 0.8 MB 75%\n PNG → WebP (Q85) 4.8 MB 0.6 MB 87%\n JPEG → AVIF (Q70) 3.2 MB 0.4 MB 87%\n WebP consistently beats JPEG at the same visual quality by 25–35%. AVIF beats WebP by another 20–30%.\n The Production Tool\n If you don't want to build your own, the ToolBox Image Compressor (https://toolboximage.com/compressor/) implements all of this with a clean UI — drag & drop, batch processing (up to 200 images), side-by-side preview, and target-size compression. It's free, processes everything locally, and supports JPEG/PNG/WebP/AVIF/GIF input.\n Key Takeaways\n 1. canvas.toBlob() is all you need for basic compression\n 2. Step-down resizing prevents memory issues on large images\n 3. WebP offers the best quality/size tradeoff for web use today\n 4. Client-side compression means zero server cost and zero privacy risk\n Try the tool at toolboximage.com/compressor/ (https://toolboximage.com/compressor/)\n",
"title": "How to Compress Images in the Browser with Canvas API (No Uploads, No Server)"
}