{
  "$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)"
}