compressthemall
Benchmarks
Each codec and setting was measured before it was added to Underpressured. Each test runs the app's choice and a common alternative on the same images. SSIMULACRA2 and Butteraugli score each output against its source file.
- smaller JPEG files than ImageIO at the same quality
- −11%
- for Underpressured to resize and encode 100 photos of 12 MP
- 4.45 s
- smaller screenshots, with every pixel unchanged
- 28%
- SSIMULACRA2 points when the resize uses linear light, not gamma space
- +10
- Machine
- Apple M2 (4 performance + 4 efficiency cores), 16 GB, macOS 27.
- Images
- 40 files with open licenses from Wikimedia Commons. The set has 20 photos of 12 to 50 MP, 10 UI screenshots, 5 PNGs with transparency, and 5 edge cases, such as 16-bit grayscale.
- SSIMULACRA2
- Measures how similar the output looks to the source. Higher is better, and 100 means identical. At 90 and above, you cannot see a difference at 1:1 zoom.
- Butteraugli
- Measures how different the output looks from the source. Lower is better.
jpegli vs. Apple ImageIO
For each photo, each encoder searches for the lowest Quality that still reaches the target SSIMULACRA2 score. Then the file sizes are compared.
Result At the same quality, jpegli files are 11 to 14% smaller. At score 90, they are less than half the size.
- Score 7086%
- Score 8089%
- Score 9045%
| Target | Files | Size | Quality | Butteraugli |
|---|---|---|---|---|
| 70 | 20 | 0.862 | 57.5 / 41 | 3.17 / 3.85 |
| 80 | 20 | 0.890 | 84.5 / 59 | 2.08 / 2.44 |
| 90 | 9 reached by both | 0.447 | 98 / 100 | — |
- At score 90, ImageIO missed the target on 11 of 20 photos, even at Quality 100. jpegli missed the target on 7.
- At the same SSIMULACRA2 score, jpegli also has the lower Butteraugli distance. The size gain does not depend on one metric.
- The two Quality scales are different. At SSIMULACRA2 80, jpegli Quality 85 gives about the same result as ImageIO Quality 59.
quantizr vs. pngquant
Both quantizers were run on 10 screenshots and 5 PNGs with transparency. Each run used a palette of 16, 32, 64, 128, or 256 colors and a dithering level of 0, 0.5, or 1. oxipng then optimized each output. For each file, the result is the smallest output that reached the target.
Result Where both reach the target, quantizr files are 11 to 13% smaller at scores 80 and 85. quantizr also has a permissive license.
- Score 8087% · 11 files
- Score 8589% · 6 files
- Score 90102% · 2 files
| Colors | Dither 0 | Dither 0.5 | Dither 1 |
|---|---|---|---|
| 16 | 22 | 26 | 32 |
| 32 | 47 | 51 | 57 |
| 64 | 63 | 70 | 74 |
| 128 | 76 | 79 | 80 |
| 256 | 83 | 85 | 85 |
- A fixed palette size does not work. At 64 colors, the score goes from 33 to over 80, depending on the image.
- The app tries palettes from small to large. It keeps the first palette that reaches the score jpegli gets at the same Quality. If no palette reaches that score, or the file is not smaller, the image keeps full color.
- At the default Web Preset Quality, 4 of 10 screenshots keep full color. Text and UI gradients need more than 256 colors.
- pngquant has a GPL-3 license. It was used only for comparison and is not part of the app.
oxipng compression levels
oxipng compresses a PNG again without changing its pixels. Higher levels try more compression strategies. A pixel-by-pixel check found every output identical to its source.
Result Level 2 saves 28.4% in 17.6 s. Level max saves 29.9%, but takes 209.5 s.
- -o 217.6 s · 28.4% saved
- -o 432.8 s · 29.7% saved
- -o max209.5 s · 29.9% saved
| Level | Median saved | Total saved | Median ms, all cores | Median ms, one core |
|---|---|---|---|---|
| -o 2 (default) | 33.8% | 28.4% | 1,269 | 1,544 |
| -o 4 | 35.8% | 29.7% | 2,376 | 3,094 |
| -o max | 36.4% | 29.9% | 5,738 | 20,082 |
- Zopfli saves about 3% more than level max, but takes 40 to 85 s for each 1 MB file. The app does not offer it.
- The app processes several images at the same time, so the time on one core is the important number. At level 2, a typical file takes 1.5 to 3.7 s.
- A 37 MB 16-bit grayscale file gets 0.5% smaller at every level. On this file, higher levels only add time.
Resizing in linear light
Four ways to make an image smaller on macOS were compared: vImage in linear light, vImage in gamma space, ImageIO thumbnails, and Core Graphics. The target sizes were 2048 px and 1280 px on the long edge. The reference is a hand-written Lanczos-4 filter that works in linear light.
Result vImage Lanczos in linear light gives the best score on every image.
- vImage, linear light92.9
- vImage, gamma space82.1
- ImageIO thumbnail70.3
- Core Graphics68.8
| Method | Photos 2048 | Photos 1280 | Screenshots 1280 | Photo ms, 2048 |
|---|---|---|---|---|
| vImage, linear light | 92.9 (91.6) | 93.9 (90.7) | 90.8 (87.6) | 169 |
| vImage, gamma space | 82.1 (71.8) | 77.4 (63.2) | 61.7 (18.9) | 98 |
| ImageIO thumbnail | 70.3 (46.5) | 65.4 (34.7) | 53.8 (12.8) | 66 |
| Core Graphics | 68.8 (56.4) | 63.2 (47.9) | 54.5 (14.7) | 112 |
- Resizing gamma-encoded pixels makes fine detail darker or lighter, for example text, foliage, and highlights. A bigger size reduction gives a bigger error.
- Linear light adds 60 to 70 ms per photo. For a 50 MP photo, peak memory is 1.3 GB, against 602 MB in gamma space.
100 photos, start to finish
Underpressured decoded 100 photos of about 12 MP, four at a time. It resized each photo to 2048 px in linear light, encoded it with jpegli at Quality 80, and wrote it to disk.
Result Underpressured takes 4.45 s on M2. The target is 30 s on a base M1.
- Decode31.8 ms
- Resize95.0 ms
- Encode21.3 ms
- Write0.5 ms
| Run | Seconds |
|---|---|
| 1 | 4.359 |
| 2 | 4.452 |
| 3 | 4.492 |
| Median | 4.452 |
- This is about 22 photos per second. Peak memory is about 0.8 GB.
- For a base M1, the same batch is estimated at 5.3 s. If memory bandwidth slows it more than expected, the estimate is 6.7 s. Both are less than a quarter of the 30 s target.
- The queue is not a bottleneck. It schedules 10,000 items in 89 ms.
Method and limits
| Tool | Version | License |
|---|---|---|
| SSIMULACRA2, Butteraugli | libjxl 0.12.0 | BSD-3 |
| jpegli | commit 031a007 | BSD-3 |
| oxipng | 10.2.1 | MIT |
| quantizr | 1.4.3 | MIT-style |
| pngquant (comparison only) | 3.0.3 | GPL-3.0 |
- Most timings come from single runs on a machine that also ran other benchmarks. Treat differences under 10% as noise. The rankings stayed the same between runs.
- The scores measure how close each output is to its source. They do not measure taste. I also checked the PNG results by eye.
- All inputs were in the disk cache. The tests did not cover HEIC or RAW decoding.
The bench folder of the repository has the scripts, the image list with licenses and authors, and the full results. You can reproduce every result with those scripts.