How we measure quality
Every result from this tool carries a number that says how far it has moved from your source. This page explains how that number comes about — and where it reaches its limits. The two belong together: a quality value whose method you cannot look up is a claim like any other.
What SSIM measures
SSIM stands for Structural Similarity. It does not compare two images pixel by pixel, but in small windows that move across the whole image. In each window, three things are compared: mean brightness, contrast and local pattern. The overall value is the mean across all windows.
The difference from a per-pixel error matters. An image that is uniformly two brightness steps lighter looks practically unchanged — a sum of squared errors still penalizes it. The other way round, a loss of fine edges barely registers in an average, while you see it at once. SSIM weights these cases closer to how perception works.
The measurement runs on brightness, not on the color channels: the eye is more sensitive to brightness structure than to color gradients, and common image formats work this way for the same reason. Our implementation uses equally weighted windows of 8 × 8 pixels.
What it is measured against — and why that is the most important sentence
The comparison is not with your original file, but with a frozen reference image: the decoded image at exactly the resolution at which the result is produced. If a preset asks for a smaller width, the image is scaled first, then frozen, then measured.
The reason: if the measurement ran against the unscaled original, the damage from scaling would flow into the quality value. It would then have two causes — scaling and compression — and the search could only control one of them. The value would get worse with every step of scaling, however well the image was compressed. It would no longer say anything about compression, and the search would find no optimum.
That is why every result says “SSIM against the scaled source”. That is not a limitation of the value; it is its definition.
How the search runs
- Determine the image type. Photo or graphic, based on the number of colors. This decides which formats compete at all.
- Freeze the reference. After any scaling, see above.
- Search roughly. On a version scaled to at most 512 pixels, the quality setting is narrowed down by bisection. This is the fast part — one encode at full resolution costs many times as much.
- Check at full resolution. The setting found is encoded and measured at full size. If it does not hold the target, bisection continues until the interval closes.
- Compare formats. The winner is the smallest file that holds the quality target. WebP has to be at least eight percent smaller than JPEG to win — otherwise the output would switch formats between similar images for no visible reason.
- Let your source compete. Your own file is a candidate like any other and is measured the same way. If no re-encode is at least two percent smaller, you get it back unchanged.
- Apply the target file size, if the preset names one: compress further first, then scale down in steps of ten percent. If the size is only reached below the quality target, the result says so.
What came out on the test corpus
The test corpus of this project consists of photos, product shots, screenshots, logos, transparent PNGs and JPEGs that were already compressed. The figures below are fixed runs — they are rerun on every change to the engine and checked against the stored values, with a tolerance of ± 0.003 for the quality value and ± 3 % for the bytes.
| Runs in the test series | 20 images |
|---|---|
| Quality target | SSIM ≥ 0.98 |
| Runs below the target | 0 |
| Lowest measured value | 0.98006 |
| Median of the measured values | 0.9812350000000001 |
| Data before | 15.1 MB |
| Data after | 11.0 MB |
| Saving overall | 27.5 % |
| Median per image | 43.8 % |
| Handed back unchanged | 0 of 20 |
| Result larger than the source | 0 |
Test series of September 19, 2026, generated from
tests/corpus.manifest.json.
Not counted: wide-gamut-p3.jpg — Decision 44: Display P3 profile, decoded without color conversion under Node. Its SSIM under Node is not the one the product delivers; the image tests ICC detection and counts in no statement about quality or size.
What the threshold 0.98 shows — and what it does not
A metric on its own says nothing about what a person sees. That is why the threshold was held against the eye on September 13, 2026: forced choice between two images, 1:1 view, crop on the highest-contrast part of the image, side of the original at random. 21 images, 84 decisions. An anchor step at 0.93 tests the setup itself — 76 % were identified correctly there (p = 0.013), so the test measures something at all.
| SSIM target | Decisions | Correct | p against guessing | Avg. file size |
|---|---|---|---|---|
| 0.93 (anchor) | 21 | 76 % | 0.013 | 67 KB |
| 0.97 | 21 | 86 % | < 0.001 | 143 KB |
| 0.98 | 21 | 67 % | 0.095 | 210 KB |
| 0.99 | 21 | 62 % | 0.192 | 392 KB |
0.98 passes — narrowly. At 67 % correct decisions, p is 0.095, and that is not enough to demonstrate a visible difference. That is exactly how it is stated here: what is shown is that a difference is not demonstrable, not that none exists. At 0.97 it clearly is — 86 % correct, p below 0.001 — and the file is only about two thirds the size. The threshold pays for its safety in bytes.
The step 0.99 stands out. There, too, the hit rate does not drop to chance but stays at 62 %. This suggests that there is not one threshold for all images: with hard edges — screenshots, logos, text — people apparently still see something for longer than with photos. Whether that is so will be decided by the split by image type, which is still open. The tool is prepared for it: a quality target per image type can be set as soon as it is measured. Until then none is set.
Limitation of the method: the comparison variants were produced with the browser’s WebP encoders, not with the library this tool uses through WebAssembly. The SSIM values are comparable; the structure of the artifacts may differ slightly.
Where this method is imprecise
The rough search measures differently from the check. It runs on a version scaled to 512 pixels, and its values differ from those at full resolution. We measured this on six images across all quality settings: the scaled version consistently measures lower, by 0.0155 on average and by up to 0.0562. That is why the setting found is checked at full resolution and not simply taken over — the value shown always comes from full resolution.
The quality setting found is not always the smallest possible. The quality value does not rise strictly monotonically with the quality setting; bisection assumes that it does. In our own measurement over 36 runs, the setting found was higher than necessary in 26 cases. The consequence is a file that is larger than it needs to be — never one that is worse than stated. The finding is written down and not fixed; it belongs to the performance work.
SSIM is not a model of perception. The value tracks what people see better than a sum of squared errors does — but it does not replace an eye. With noise it behaves oddly: to SSIM, noise looks like structure. Night shots and macros are therefore the hard cases in the test corpus.
One image does not count here. The photo with a Display P3 color profile is decoded without color conversion in the test environment, but with it in the browser. Its measured value would not be the one the product delivers. It stays in the corpus because it tests profile detection, and it is excluded from every statement about quality and size — visibly, not silently.
Why the number is there at all
Many tools compress images. The difference lies in whether the result is claimed or shown. A slider set to “quality: 80” tells you what the encoder was set to — not what became of your image. The same setting means one thing for a logo and another for a night shot.
That is why there is a measured number here, also when it comes out badly. If an image misses its target, the result says so. If your source is better than anything we can make of it, you get it back. And if a result gets larger than the source, that is shown too — hiding it would be easier and would break exactly the promise this tool exists for.