Image KB Reducer
Compress an image to an exact file size, from a terminal, a CI step, or your own Node/Worker/browser code.
50 KB or less, at the highest quality possible — never exactly 50.000 KB.
That promise ships in four places, and all four are the same engine:
- imagekbreducer.com — the website. Runs entirely in your browser; your image never leaves your device.
imagekbreducer/ikb— the command-line tool documented here.npx imagekbreducer compress photo.jpg --to 50kbfrom a terminal or a CI step.@imagekbreducer/image-core— the library underneath all of them, published so the same target-size search and the same wasm codecs run in your own Node process, Cloudflare Worker, or browser tab.- The HTTP API — a hosted endpoint for the same job, for anything that isn’t already a Node process. Documented in the API reference tab.
Three of the four are documented on this site: the CLI and the library under Guides, the HTTP API under API reference. The website has its own pages, including the privacy policy where its stronger promise — the image never leaves your device — is stated and scoped to the browser tools it covers.
Where to start
Quickstart
Install the CLI and compress your first file in under a minute.
CLI reference
Every command, every flag, every exit code — read from the source, not guessed at.
Library
Run a job from your own Node, Worker, or browser code.
HTTP API
POST /v1/compress from any language — one endpoint, one job.
Recipes
A page-weight budget gate for CI, and a pre-commit hook.
Why a CLI and a library, not just the website
Per the project’s own framing: if the CLI ever needs image logic of its own, that’s a sign the
library’s API is wrong. So the CLI (packages/cli) is a thin argument parser over
@imagekbreducer/image-core’s job-recipe layer (compressJob, resizeJob, convertJob,
cropJob, rotateJob) — the exact same recipes the website and the HTTP API build from. That’s
what makes the identical-bytes guarantee below possible: there’s only one place a “compress to
50 KB” job is actually defined.
Parity with the website
The website and this library both return a file at or under your target — that promise is encoder-independent and holds on both.
They are not byte-identical, and it’s not only JPEG. The browser encodes JPEG through the
platform’s own <canvas> encoder; the library encodes JPEG through mozjpeg (compiled to wasm).
For WebP, the browser can bind a SIMD-accelerated encoder where the host CPU supports it, while
the library is deliberately pinned to the non-SIMD build, so its output does not vary with the CPU
it happens to run on. A parity test measured the JPEG divergence for a 1600×1200 photo at a 50 KB
target, comparing the browser’s canvas encoder against the library’s wasm backend, on all three
engines: 2.5% on Chromium, 2.0% on Firefox, 0.3% on WebKit. That number is specific to that
fixture and that target — it is not a general bound on how far apart the two encoders can land.
The library, the CLI and the HTTP API, by contrast, share this one wasm backend and produce identical bytes for identical input. That’s the guarantee that actually matters for automation: script the library, the CLI or the API interchangeably against the same input and get back the same file, byte for byte.
That guarantee is about the encoder, and it holds for every input the API will accept — but
the API accepts fewer inputs than the other two, so “the same input” is not always something you
can hand to all three. It refuses HEIC with 415 unsupported-format where the CLI and the library
decode it, and its ceilings are lower: 25 MiB and 20 MP, against the library’s default 50 MiB and
40 MP — so a file the CLI compresses can come back 413 input-too-large, or 422 dimension-exceeded, from the API. (AVIF is refused by the API too, but this wasm backend decodes
no AVIF on any surface, so that one is not a divergence between them.) Limits gives
the reason behind each refusal.