How to Get an Image Under an Exact KB Limit, and When You Can't

An exam portal asks for your photo “in JPG format, maximum 20 KB”. Your phone photo is 3 MB. You try a random online compressor, it gives you 24 KB, and the portal still says no. I built ToolOnTap’s “compress to X KB” pages for that moment, and getting under a number reliably turned out to need more than a quality slider.

What “20 KB” means to a form

The first trap is the unit. Some sites count a kilobyte as 1,000 bytes, others as 1,024. For a 20 KB limit that’s either 20,000 or 20,480 bytes, and a file of 20,300 bytes passes one check and fails the other. You can’t tell from the form which one it uses.

So the compressor aims under the stricter one:

// Sites count a KB as 1,000 or 1,024 bytes; aim under the stricter one so either limit is met
const target = maxKB * 1000

The result list checks against the same number. On the fixed-target pages, a file that still ends up above it gets a warning, “Still above 20 KB”, so you don’t upload something that looks fine and then bounces.

The first pass: quality, then a little size

The compression itself is done by browser-image-compression (version 2.0.2), an open-source library that loads only when you add a file. I call it with a byte target and at most 15 rounds. Reading its source, one call works like this:

  1. Draw the image on a canvas and save it once at full quality.
  2. If that’s already under the target, stop.
  3. Otherwise, each round multiplies the quality by 0.95 and shrinks the width and height by 5%, then saves again.
  4. Stop when the file fits or the rounds run out.

After 15 rounds, the quality is down to about 46% of where it started (0.95¹⁵ ≈ 0.46) and each side is about 46% of the original. For most photos and the larger targets, that’s enough.

At 10 or 20 KB it often isn’t. Lower quality saves less and less, because a JPG still has to describe every 8 × 8 block of pixels in the picture.

When quality isn’t enough, shrink the picture

The bytes in a compressed photo come mostly from detail, and detail grows with the number of pixels. Halving the width and height leaves a quarter of the pixels, which usually saves more than lowering the quality further. So when the first pass misses, the tool estimates a smaller size and starts again from the original file:

export function nextMaxSide(side: number, size: number, target: number): number {
  const estimate = side * Math.sqrt(target / size) * 0.9
  return Math.max(1, Math.floor(Math.min(side * 0.9, estimate)))
}

If file size grows with side², then the side has to shrink by the square root of how far off the size is. A 2,000-pixel image that came out at 400 KB for a 100 KB target is 4 times too big, so the side should halve. The extra 0.9 aims a little lower, because the estimate is rough. The Math.min(side * 0.9, …) makes sure every retry shrinks by at least 10%, even when the file is only a few bytes over and the estimate says “almost no change”. For that 2,000-pixel image the next try is 900 pixels on the long side, a case that’s in the unit tests.

There are up to 4 of these retries, each one a full run of the library with a lower maxWidthOrHeight. Each run starts from the original file, so quality loss doesn’t pile up from one try to the next. When the size had to drop, the file gets a note such as “Scaled down to 640 × 480 px to get closer to 20 KB”, so you can check it against the form’s pixel rules.

JPG and PNG behave differently

JPG is lossy and has a quality setting, so the library can trade detail for bytes smoothly. PNG is lossless, and the browser’s canvas has no quality setting for it at all. The library handles PNG with its own encoder that reduces the number of colors instead. It starts at 4,096 colors and multiplies that by 0.85 each round, alongside the same 5% size cut.

That works for logos, icons and screenshots, which have few colors to begin with. For photos it’s poor value. A photo has smooth gradients, and squeezing them into a small palette gives visible bands while saving less than JPG would. That’s why the two kinds of page differ:

Page Output format Target
Compress images Same as the input (JPG stays JPG, PNG keeps transparency, WebP stays WebP; other formats become JPG) 200 KB
Compress image to 10, 20, 30, 50, 100, 200 or 500 KB Always JPG The number in the page name

The fixed-target pages exist because the forms that set such limits almost always want a photo, and almost always as a JPG. A PNG photo would rarely fit in 20 KB anyway. When a transparent PNG goes to JPG, the library paints the canvas white first, so see-through areas come out white.

When the target is impossible

Some images can’t reach some targets with a face still recognisable in them. A detailed landscape at 10 KB is one, and so is a photo full of grain from a dark room. The tool is built to stop rather than hand you a postage stamp:

  • Retries stop after 4 attempts.
  • They also stop if the next size would be under 64 pixels on the long side.
  • If the file is still over the target at that point, you get the last file it made, with the “Still above 20 KB” warning and a hint to crop it or use a smaller photo.

Cropping is usually the fix. In a head-and-shoulders photo, the background and the clothes take up a good share of the bytes. Cut them away, and more of the 20 KB is spent on the face.

At the other end, a file that’s already under the target comes back almost unchanged. If it ends up much smaller than the limit, the photo was simply efficient, and nothing was thrown away to get there.

A few things worth doing before you compress

  • Resize first if the form gives pixel dimensions. An image that’s already 600 × 600 needs much less compression than a 4,000-pixel photo squeezed to the same bytes, and it stays sharper.
  • Use daylight and a plain background. Smooth areas cost almost nothing. Noise and patterns cost a lot.
  • Keep the original. Compress a copy, because a compressed JPG can’t be turned back into the sharp one.

Try it

For a specific limit, use the matching page: Compress image to 20 KB, 50 KB, 10 KB, 30 KB, 100 KB, 200 KB or 500 KB. To make images smaller while keeping their format, use Compress images. Everything runs on your device. The other image tools are on the image tools page.

Tools in this article

  • Compress images

    Compress JPG, PNG and WebP images to 200 KB or less each, keeping their format. Batch compress in your browser and download a ZIP.

  • Compress image to 10KB

    Shrink a photo or scanned signature to under 10 KB for online application forms. Works in your browser, no upload.

  • Compress image to 20KB

    Compress a photo or signature to 20 KB or less for government, exam and job application forms. Free, private, in your browser.

  • Compress image to 30KB

    Reduce a JPG or PNG to 30 KB or less for applicant photos on online portals. Free, private, and done in your browser.

  • Compress image to 50KB

    Get a photo under 50 KB for job portals, university applications and online ID services, in seconds. No upload, no sign-up.

  • Compress image to 100KB

    Compress images to 100 KB for profile pictures, CV photos and images inside documents. Sharp results, free, nothing uploaded.

  • Compress image to 200KB

    Compress photos to 200 KB or less for email, CMS uploads and web pages without visible quality loss. Free and private.

  • Compress image to 500KB

    Bring large phone photos under 500 KB while keeping them high quality, for emails, listings and uploads with a 0.5 MB limit.

More articles