MiniPx
Blog
๐Ÿ”’ Images never leave your browser
โ† Blog

How Browsers Compress Images (Canvas API Explained)

By Gaurav Bhowmickยทยท7 min read

Every browser has a built-in image encoder. When tools like MiniPx compress your photos without a server, they are using the Canvas API's toBlob() and toDataURL() methods. Here is exactly how that works.

The Canvas pipeline

Browser-based compression follows a simple pipeline: decode the source image into raw pixel data, draw those pixels onto a Canvas, then re-encode the canvas contents into a compressed format.

The decode step happens when you set an Image element's src. The browser reads the file (JPEG, PNG, WebP, whatever) and converts it into an uncompressed bitmap in memory. The encode step happens when you call toBlob() or toDataURL() โ€” the browser runs its built-in encoder to produce a new compressed file.

toBlob() โ€” the main compression method

The toBlob() method is where compression actually happens. It takes three arguments:

canvas.toBlob(
  callback,    // function that receives the Blob
  mimeType,    // "image/jpeg", "image/webp", or "image/png"
  quality      // 0 to 1 (only for JPEG and WebP)
);

The quality parameter is the key control. At 1.0, you get maximum quality with minimal compression. At 0.1, you get aggressive compression with visible artifacts. The sweet spot for most photos is 0.7 to 0.85.

Quality parameter deep dive

The quality value maps to the internal quantization tables used by the JPEG encoder. Here is how different values affect a typical 3 MB photo:

Quality
Output Size
Visual Quality
0.95
~1.8 MB (40% smaller)
Indistinguishable
0.85
~800 KB (73% smaller)
Excellent
0.75
~500 KB (83% smaller)
Very good
0.60
~300 KB (90% smaller)
Good, minor artifacts
0.40
~180 KB (94% smaller)
Noticeable artifacts
0.20
~100 KB (97% smaller)
Significant artifacts

The relationship between quality and file size is not linear. Dropping from 0.95 to 0.85 saves a lot of space with almost no visible difference. Dropping from 0.40 to 0.20 saves much less space but introduces very visible degradation.

Format differences in toBlob()

JPEG (image/jpeg)

JPEG is lossy and works best for photographs. The quality parameter works as expected. One catch: JPEG does not support transparency. If your canvas has transparent areas, they will be filled with black in the JPEG output.

WebP (image/webp)

WebP supports both lossy and lossless compression. With a quality parameter, it behaves like JPEG but produces 25-35% smaller files. WebP also supports transparency, making it a good all-around format.

PNG (image/png)

PNG is always lossless in the Canvas API. The quality parameter is ignored. PNG files from the Canvas API tend to be larger than optimized PNGs because the browser uses basic deflate compression without advanced techniques like color quantization.

Browser differences matter

Different browsers use different encoder implementations. Chrome uses libjpeg-turbo for JPEG and libwebp for WebP. Firefox has its own implementations. Safari uses Apple's native image frameworks.

In my tests, the same image at quality 0.8 produced a 487 KB file in Chrome, 512 KB in Firefox, and 495 KB in Safari. The visual quality was comparable across all three, but Chrome's output was consistently a few percent smaller.

OffscreenCanvas for background processing

Regular canvas operations block the main thread. For large images or batch processing, OffscreenCanvas lets you run compression in a Web Worker.

// In your Web Worker:
self.onmessage = async (e) => {
  const { imageBitmap, quality } = e.data;
  const canvas = new OffscreenCanvas(
    imageBitmap.width,
    imageBitmap.height
  );
  const ctx = canvas.getContext('2d');
  ctx.drawImage(imageBitmap, 0, 0);

  const blob = await canvas.convertToBlob({
    type: 'image/jpeg',
    quality: quality
  });
  self.postMessage({ blob });
};

Note the API difference: OffscreenCanvas uses convertToBlob() (which returns a Promise) instead of toBlob() (which uses a callback). This is how MiniPx keeps the interface responsive during batch compression.

Limitations and workarounds

Canvas size limits. Most browsers cap canvas dimensions at 16,384 x 16,384 pixels. Some mobile browsers have lower limits. If an image exceeds this, you will need to downscale it before drawing to the canvas.

No EXIF preservation. The Canvas API strips all metadata. This is good for privacy but means you lose orientation data. Always apply EXIF orientation before drawing to the canvas, or your rotated photos will appear sideways.

Color space issues. Canvas works in sRGB by default. If your source image uses a different color space (like Adobe RGB or Display P3), colors may shift during compression. For most web images, this is not a problem.

Frequently asked questions

What is the difference between toBlob() and toDataURL()?
toBlob() returns a Blob object asynchronously via a callback. toDataURL() returns a base64-encoded string synchronously. toBlob() is better for most use cases because it does not block the main thread and produces a Blob that can be directly downloaded or uploaded. toDataURL() is useful when you need a data URI for inline display.
Why does canvas.toBlob() quality differ between browsers?
Each browser uses its own JPEG/WebP encoder implementation. Chrome uses libjpeg-turbo, Firefox uses its own build of libjpeg, and Safari uses Apple's ImageIO framework. These encoders produce slightly different output at the same quality level โ€” typically within 5-10% of each other.
Can I use AVIF with the Canvas API?
AVIF support in canvas.toBlob() is still limited. Chrome 124+ supports it, but Firefox and Safari do not as of early 2026. You can check support with: canvas.toBlob(blob => console.log(blob.type), "image/avif"). If the returned blob type is not "image/avif", the browser fell back to PNG.
What happens when I pass quality to a PNG export?
Nothing. The quality parameter is ignored for PNG because PNG is a lossless format. The browser always exports PNGs at full quality. To reduce PNG file size, you need to reduce dimensions, reduce color depth (which the Canvas API cannot do directly), or convert to a lossy format like JPEG or WebP.

Related tools

Compress JPEGConvert JPG to WebPCompress PNG

More from the blog

JPEG vs PNG vs WebP โ€” Best Format for Websites โ†’Client-Side Image Compression with JavaScript โ†’AVIF vs WebP vs JPEG: Which Format Wins in 2026? โ†’
๐Ÿ”ง
Try MiniPx โ€” free, no signup

Compress, convert, and resize images in your browser. Nothing gets uploaded.

Open MiniPx โ†’