← Blog

Why some files come back the same size — when our compressor returns the original

Five guards in the pipeline that decide "leave this file alone" Skip conditions 1. JPEG measured quality ≤ slider quality (already at or below target) 2. PNG palette already < 256 colours (no quantization room) 3. GIF re-encode shrinks file by <3% (overhead exceeds gain) 4. Output bytes ≥ input bytes (encoder couldn't beat source) 5. Format unsupported (we can't decode the input at all) In all five cases we return the original file unchanged, with a flag indicating it wasn't recompressed. The output is byte-identical — same EXIF, same colour profile, same modification time.

Drop a file into the compressor, hit go, and sometimes the result is "0% smaller — original returned". This is intentional. The pipeline contains several guards that detect when re-compression is pointless or harmful and short-circuit to handing back the input unchanged. Knowing which guard fired explains the size that comes out.

Guard 1: JPEG already at target quality

Before re-encoding a JPEG, we read its existing quantization tables and back-compute its quality (covered in detail in the JPEG quality detection article). If the source's measured quality is already at or below the slider value, we skip the re-encode entirely.

The reasoning: re-encoding a quality-72 photo at slider quality 80 would produce a file that's bigger AND a generation worse. Both axes go the wrong way. Recompressing only makes sense when the source is at higher quality than the target.

This guard fires often. About 30% of real-world JPEG inputs hit it — many photos people upload have already been compressed by a phone, social-media platform, or messaging app, and arrive at quality 65–78.

Guard 2: PNG already palette-quantized

If the input is a palette PNG with fewer than 256 colours in its PLTE chunk, the size is dominated by the deflate-compressed pixel grid. Re-running palette quantization wouldn't help (the palette is already the small one). Re-running deflate optimisation might gain 1–2%, but the cost of the full decode/re-encode cycle isn't worth that small saving.

We detect this by reading the PLTE length: under 768 bytes (256 entries × 3 RGB bytes) means the palette is sub-256. We hand back the original file with a flag indicating no compression was done.

If you specifically want a deflate-optimised version of an already-palette PNG, the right tool is a dedicated PNG optimiser run separately. Our compressor is tuned for "make this meaningfully smaller", not "squeeze the last 1%".

Guard 3: GIF re-encode under 3% gain

GIF compression is mostly determined by the input file's existing palette and frame layout. A well-encoded GIF doesn't have much room for improvement. We try to compress it with the GIF entropy optimiser and palette reducer, then check the result:

savings = 1 − compressedSize / originalSize
if savings ≤ 0.03:
    return original GIF unchanged

3% is the threshold. Below that, the compressed file is essentially the same size, and the output isn't byte-identical (which is jarring for users who expect "no compression" to mean "no changes"). Returning the original keeps modification times and exact bytes intact.

Guard 4: encoder produced something bigger

Sometimes — usually with already-heavily-compressed inputs — the encoder runs successfully and produces a "compressed" file that's actually larger than the source. This can happen when:

If output.length ≥ input.length, returning the larger output would be silly. We hand back the original.

Guard 5: format we can't process

The compressor accepts JPEG, PNG, WebP, GIF, BMP and ICO via the browser's native decoder, plus AVIF where the browser supports it. Inputs in any other format fail at the decode step before compression has a chance to run.

For HEIC, RAW formats (CR2, NEF, ARW), TIFF, PSD, and so on, we fall back to one of two paths. If the file can be decoded by a converter (HEIC, TIFF), we offer a converter route in the UI. If it can't (RAW formats), we ask you to convert to JPEG or PNG first in your photo editor.

How the skip path looks in the UI

When a guard fires, the file panel shows "Original (smallest)" or "0% reduction — already optimised". The download button still works, but the file you download is byte-identical to what you uploaded. We do this rather than display "compression failed" because for the user the outcome is the same: the file is already as small as we can make it without losing visible quality.

If you specifically want to force a re-encode (because you don't care about quality, or you want to strip metadata, or you want the file to be bit-different from the original for some workflow reason), you can override the slider to a lower quality. That bypasses the guards and runs the encoder unconditionally.

In batch processing

If you drop 50 photos at once, expect a meaningful fraction (often 20–40%) to come back unchanged. They aren't broken — they're files that were already at or below the requested quality target. The total bytes saved is what matters, and the skipped files contribute zero bytes saved either way. The other 60–80% deliver the bulk of the size reduction.