About this image compressor

A small, dependency-free tool for one job: shrinking JPEG, PNG and WebP files without a server in the loop. No account, no upload — the compression happens on the device you're reading this on.

Why it exists

Most "compress your image online" tools ask you to upload the file to a server, wait, then download a result — which works, but means a copy of every photo you compress passes through someone else's infrastructure first. Modern browsers can decode an image, redraw it at a smaller size, and re-encode it to JPEG or WebP without any of that, using APIs that have shipped in every major browser for years. This page is that path, with the settings a compressor actually needs — format, quality, maximum size — exposed directly.

How the compression is implemented

Three browser APIs, in order, for every image:

  1. createImageBitmap(file) decodes the file directly, requesting imageOrientation: 'from-image' so the result is already rotated upright according to the file's EXIF tag where one exists. Browsers that don't support this fall back to loading the file into an <img> element instead.
  2. Dimension math fits the decoded size inside the optional max width/height box, preserving aspect ratio and never upscaling a smaller image just because the box is bigger than it.
  3. canvas.toBlob(callback, mimeType, quality) draws the bitmap onto an in-memory canvas at the final size and asks the browser's own encoder for a compressed file in the chosen format. The quality argument only affects JPEG and WebP — PNG output from toBlob is always lossless, by specification, regardless of what quality value is passed.

Every image in a batch is processed one at a time, in the order it was added, rather than all at once — this keeps memory use predictable when someone drops in a folder of dozens of full-resolution photos.

How it was verified

The parts of this tool that are plain arithmetic — not the browser's own image codecs, which this site cannot re-verify — were checked against known values before shipping:

Verification cases for the pure helper functions
FunctionInputExpectedResult
formatBytes1536"1.5 KB"match
percentSaved1000 → 400"60%"match
fitDimensions4000×3000, max 19201920×1440match
fitDimensions800×600, max 1920800×600 (no upscale)match

Those functions live in a module with a module.exports hatch specifically so they can run under Node, outside a browser, against fixed test cases — the same pattern this whole site uses for logic that doesn't need a DOM. What that testing deliberately does not cover is the actual pixel-level output of canvas.toBlob itself: there is no way to run a browser's image encoder inside Node, so the encode step was checked by eye across JPEG, WebP and PNG output, at several quality settings, in real desktop and mobile browsers, rather than asserted against a fixed byte count that varies by browser version and platform.

How the site is built

  • Static HTML, one small stylesheet, two small scripts. No framework, no build step, no bundler.
  • Nothing is fetched from the network to run it. No CDN library, no web font, no remote image; the favicon is an inline SVG data URI. Once the page has loaded, compressing an image needs no connection at all.
  • System font stack, so nothing has to download and nothing shifts as the page loads.
  • Every object URL is revoked once its image card is removed, the queue is cleared, or a fresh compression replaces an older result — so repeatedly compressing a large batch doesn't quietly leak memory over a long session.
  • Dark mode follows your system setting via prefers-color-scheme.

Scope and honesty about limits

This tool calls the browser's own JPEG, WebP and PNG codecs — the same ones any native app on your device would use — rather than shipping a specialised encoder of its own. A dedicated tool such as a mozjpeg-based pipeline can often produce a noticeably smaller file at matching visual quality, at the cost of being slower and needing dedicated software rather than a web page. PNG output has no lossy mode in any browser, so the only lever this tool has over PNG file size is downscaling its dimensions.

It also can't decode a format your browser doesn't support — whether a given file opens at all depends on your browser's built-in codecs, not on anything this page controls.