Blog › Why Hitting "Under 1MB" Is Harder Than It Looks
Upload forms love the phrase "images must be under 1MB." Most image editors hand you a quality slider from 1 to 100, and that's where it gets awkward. What quality gives you 1MB?
You can't work it out. Nobody knows until the file is written.
A JPEG quality value is a coefficient controlling how much detail gets discarded during compression. It says nothing about the resulting file size. So the same quality of 80 produces wildly different sizes from one photo to the next.
An image that's mostly flat background with some text has little detail to throw away, so it stays small whether you pick 80 or 95. A landscape full of leaves is the opposite: neighbouring pixels disagree constantly, compression barely helps, and the same quality setting yields a file several times larger. Noisy night shots behave the same way.
The relationship is also non-linear. Going from 90 to 80 cuts a lot; going from 50 to 40 barely moves the needle, because most of what could be discarded already has been. Intuitions like "halve the quality to halve the size" simply don't hold.
If it can't be calculated, the only option is to try. Trying all 100 values is wasteful, so we binary search instead — which works because of one guarantee: raising quality always increases size. Monotonic behaviour means you can probe the middle and halve the range each time.
Sixty encodes become six or seven. And note it isn't just looking for something under the limit — it's looking for the highest quality that stays under it. If 900KB is achievable there's no reason to hand back a 400KB file and throw away detail for nothing.
The bottom of the search range is 35, not 1. Below that, JPEG's characteristic square blocks start showing. Compression works in 8×8 pixel tiles, and discard too much and those tile boundaries surface as a visible grid.
Given a 1MB file at quality 20 versus a 1MB file at quality 75 scaled to 70% of its dimensions, the second one is better almost every time. So instead of cutting quality further, we switch to reducing resolution.
Once you decide to resize, the next question follows: by how much? Here we lean on one approximation — file size is roughly proportional to pixel count.
Scaling both dimensions by s multiplies the pixel count by s². So to take a 3MB file down to 1MB you need a third of the pixels, and the scale factor is the square root of that — about 0.58. That single calculation removes most of the trial and error.
So we don't treat it as a one-shot answer. At the estimated scale we run the quality binary search again, and if that still misses, we shrink by another 0.8× and repeat, up to five times. Almost everything lands within those five.
If the target is unreasonably small — a 40-megapixel photo squeezed into 50KB — five rounds won't get there. Rather than failing silently or shrinking forever, we return the smallest result achieved and flag that the target wasn't met. The person needs to be able to decide whether to relax the target or accept the result.
The search logic is separated from the actual encoding. It takes an injected function — "given a scale and a quality, return the resulting byte count" — from outside. In the live tool a browser canvas plays that role; in tests, a fake encoder does.
That means the search can be verified without launching a browser at all: does the binary search really find the maximum quality, does it return the smallest result when the target is unreachable, and so on. Verification is exactly the part of image-processing code that tends to become slow and flaky, and injecting the encoder makes that problem disappear.
Compress to a size you choose in KB
The search described here runs inside your browser.