Two charts comparing baseline, optimize and progressive JPEG encoding on eight 1024 pixel PNG files from this gallery across quality 50 through 95, showing the optimize and progressive savings curves crossing near quality 80

Left: bytes saved against the baseline encoder, in percent, for optimize and for progressive across the quality range. Lower is better and the two curves cross between quality 75 and 80. Right: mean file size by quality for all three. Charts generated from the measurement run described below.

Progressive And Optimize Both Shrink A JPEG For Free, And Which One Wins Flips At Quality 80

Two JPEG flags cost nothing and change nothing you can see. Every guide tells you to turn both on. On this run they compete with each other, one wins at low quality, the other wins at high quality, and the handover is sharp enough to put a number on.

Published September 7, 2026 · RealAIGirls · About a 6 minute read

Share on X Share on Facebook Share on Reddit

There are two JPEG settings that make a file smaller without changing a single decoded pixel. Not smaller in the way the quality slider makes things smaller. Smaller with the output bit for bit identical to what you would have got anyway.

They are optimize and progressive, and every guide that mentions either one tells you to turn both on and moves along. On this run they are not additive, they are not equivalent, and which one you want depends on a number nobody states.

What The Two Settings Actually Do

Optimize tells the encoder to make a second pass and compute Huffman tables tailored to this specific image rather than using the standard tables baked into the library. The coefficients being coded are exactly the same. Only the code book changes.

Progressive changes the scan order. Instead of storing the image once, top to bottom at full detail, it stores several passes, coarse first and refining, which is why a progressive JPEG on a slow connection appears blurry and then sharpens instead of loading down the page like a window blind. As a side effect the coefficients get grouped into scans that each compress better than the interleaved order does.

Neither one touches quantisation. That is the claim this whole page rests on, so it gets tested rather than asserted.

The Setup

Eight 1024 pixel PNGs from this gallery. JPEG at quality 50, 60, 70, 75, 80, 85, 90 and 95, each encoded four ways: baseline, optimize, progressive, and both together. Chroma subsampling pinned at 4:4:4 so that setting cannot leak into the result. That is 256 encodes.

For every one: exact encoded byte count, and the decoded pixel array compared element by element against the baseline decode of the same file at the same quality.

First, The Claim That Has To Hold

If either setting changed the image, none of the size numbers would mean anything, because you could always make a file smaller by making it worse.

Across all 256 encodes, every optimize, progressive and combined output decoded to an array pixel identical to the baseline output. Not close. Not within a tolerance. Identical, every channel, every pixel.

So everything below is free.

The Result

Total bytes across the eight files at each quality, and the change against baseline.

QualityBaselineOptimizeProgressiveOptimize vs baseProgressive vs base
50811,773754,236772,628-7.09%-4.82%
60914,419862,816880,268-5.64%-3.73%
701,072,4271,030,5411,041,613-3.91%-2.87%
751,176,0991,138,8221,146,564-3.17%-2.51%
801,343,7541,312,5281,312,130-2.32%-2.35%
851,586,4051,559,3751,547,619-1.70%-2.44%
902,008,5251,969,8781,937,680-1.92%-3.53%
952,972,2362,897,8182,806,146-2.50%-5.59%

Read down the last two columns. At quality 50 optimize saves 7.09 percent and progressive saves 4.82. At quality 95 progressive saves 5.59 percent and optimize saves 2.50. The two curves swap places, and they do it between 75 and 80.

Per file, counting all eight qualities, the progressive saving runs from 3.18 percent on the tamest image to 4.46 percent on the busiest, and every file moves the same direction.

The Crossover, Counted Properly

Totals can hide a split decision, so here is the file by file count. Out of eight files at each quality, the number where progressive produced a smaller file than optimize:

Quality5060707580859095
Progressive wins0/80/81/83/83/86/88/88/8

That is about as clean a monotone handover as eight images will ever give you. Zero out of eight at the bottom, eight out of eight at the top, and the middle of the range genuinely contested.

Note the honest wrinkle at quality 80. Progressive wins on total bytes, -2.35 against -2.32, while only winning on three of the eight individual files. The total is being carried by the largest images. On a mixed library that is still the right way to read it, since the big files are where the bytes are, but a single image at quality 80 is a coin flip rather than a rule.

The Finding Nobody Mentions

Progressive and progressive plus optimize came out byte for byte identical in all 64 file and quality combinations. Not close. The same number, every time.

The reason is that the progressive encoder in this library already computes optimised Huffman tables per scan, because it has to; there is no standard table set for arbitrary progressive scan scripts. So passing optimize alongside progressive asks for something that has already happened.

This matters practically. If your build pipeline sets both flags and you assume you are getting two savings stacked, you are getting one. The other flag is a no op, and if you were relying on it to cover the low quality end, it is not doing that either.

Why The Curves Cross

The mechanism is coefficient density, and once you see it the shape is obvious.

At low quality the quantiser has zeroed most of the high frequency coefficients. What is left is a short, lopsided symbol distribution, which is exactly the situation where a custom Huffman table beats the generic one by a wide margin. Meanwhile progressive has very little left to reorganise, because there is not much detail to spread across refinement scans.

At high quality that reverses. The coefficient distribution is broad and much closer to what the standard tables were designed for, so the optimize gain shrinks toward two percent. But now there is a lot of high frequency detail, and separating it into its own scans groups similar magnitudes together, which is where progressive earns its 5.59 percent.

Two mechanisms, aimed at two different kinds of redundancy, each strongest where the other is weakest.

What To Actually Do

Turn one of them on. Always. The worst result anywhere in this run was a 1.70 percent saving, and the output is pixel identical. There is no argument for shipping a plain baseline JPEG.

Below quality 80, use optimize. It is worth up to 7.09 percent there and progressive is worth less than five.

At quality 85 and above, use progressive. It is worth up to 5.59 percent, it beats optimize on every file at 90 and 95, and you get the coarse to fine load behaviour as a bonus rather than as the reason.

Do not expect the two to add up. Passing both gives you the progressive number, exactly, and never anything better.

One caveat that is not about bytes. Progressive JPEGs cost more to decode, because the decoder walks multiple scans, and they need more memory since the whole coefficient array has to be held before the image resolves. On a page of thumbnails aimed at cheap phones that can matter more than four percent of transfer. This run measured bytes, not decode cost, and it is not going to pretend otherwise.

Method

Eight files, eight qualities, four encoder configurations, 256 encodes. Pillow with the standard JPEG encoder, subsampling passed explicitly as 0 so no library default is in play, and quality passed explicitly at each step. Every source is a PNG resized once to 1024 pixels wide with Lanczos, so there is no prior lossy pass to muddy the comparison. Pixel identity was checked by decoding every output and comparing the full integer arrays against the baseline decode, not by sampling. Sizes are exact byte counts of the encoded buffers. Eight images is a small sample; the direction of the crossover is consistent across all eight, the magnitudes are specific to this content, and a different encoder such as mozjpeg would produce different numbers with, most likely, the same shape.