Top: mean encoded WebP size at each quality, one line per sharpening strength, same eight Lanczos thumbnails every time. The dotted line is the unsharpened file at quality 80, and the medium line starts right on it at quality 60. Bottom: edge energy measured on the decoded WebP. Charts generated from the measurement run described below.
Almost every thumbnail script ends with a quick sharpen, because a downscaled image looks soft and a little unsharp mask makes it pop. The encoder does not throw that sharpening away. It keeps nearly all of it, and it charges you for every edge.
Here is how nearly every thumbnail script I have ever read ends. Resize the image down, notice it looks a bit soft, run an unsharp mask over it, save. It is one line of code and it makes the gallery look crisper, so nobody questions it.
Yesterday's piece on this site found that Lanczos, the sharpest resize filter, ships the biggest WebP, because sharp detail is exactly what a lossy encoder has to spend bytes on. The obvious next question is what happens when you add even more sharpness on purpose, after the resize. So I measured that too.
The same eight gallery images used in every measurement piece here. Each one downscaled to a 512 pixel box with Lanczos, then given one of five treatments: no sharpening, or Pillow's UnsharpMask at four strengths from light to heavy. Every result was encoded as WebP at quality 60, 70, 75, 80 and 85 with method 4. That is 200 encodes.
To check whether the sharpening survives compression or just gets smoothed away by the encoder, I also measured edge energy, the variance of a Laplacian filter over the luma channel, once on the sharpened image and once on the decoded WebP. Bigger numbers mean more hard edges.
| Pass | Unsharp mask settings | Mean bytes | vs no sharpen | Edge energy in | Edge energy after WebP |
|---|---|---|---|---|---|
| none | no filter | 28,972 | 0.00% | 1,143 | 1,124 |
| light | radius 1, percent 50, threshold 3 | 33,390 | +15.25% | 2,150 | 2,098 |
| medium | radius 2, percent 100, threshold 3 | 40,451 | +39.62% | 3,658 | 3,555 |
| strong | radius 2, percent 150, threshold 2 | 44,843 | +54.78% | 4,922 | 4,763 |
| heavy | radius 3, percent 200, threshold 2 | 49,759 | +71.75% | 5,972 | 5,746 |
A light sharpen made the average file 15.25 percent bigger. Medium made it 39.62 percent bigger. Heavy, 71.75 percent. The bytes climb in step with the edge energy, and the edge energy does not go anywhere when the file is compressed. The light pass keeps 97.6 percent of its edges through WebP, and even the heavy pass keeps 96.2 percent. The encoder is faithfully preserving the thing you added, which is precisely why it costs so much.
| Quality | none | light | medium | strong | heavy |
|---|---|---|---|---|---|
| 60 | 22,426 | 26,140 (+16.56%) | 32,067 (+42.99%) | 35,748 (+59.40%) | 39,763 (+77.31%) |
| 70 | 25,136 | 29,168 (+16.04%) | 35,450 (+41.03%) | 39,368 (+56.62%) | 43,876 (+74.55%) |
| 75 | 26,662 | 30,758 (+15.37%) | 37,540 (+40.80%) | 41,712 (+56.45%) | 46,247 (+73.46%) |
| 80 | 31,862 | 36,692 (+15.16%) | 44,158 (+38.59%) | 48,908 (+53.50%) | 54,362 (+70.61%) |
| 85 | 38,773 | 44,190 (+13.97%) | 53,042 (+36.80%) | 58,477 (+50.82%) | 64,546 (+66.47%) |
No crossover anywhere. The penalty shrinks a little as quality rises, because a high quality setting is already spending bytes on fine detail, but even at quality 85 a light sharpen still costs 13.97 percent. Across the eight individual images the light pass at quality 75 landed between +13.3 and +16.8 percent, so this is not one busy picture dragging the average.
Look at two cells in that table. Medium sharpening at quality 60 averages 32,067 bytes. No sharpening at quality 80 averages 31,862. Those are the same file size, 0.64 percent apart.
So when a pipeline runs a medium unsharp mask and then turns quality down to keep files small, it is making a trade it probably never meant to make. It spends the entire budget of quality 80 on artificial edge contrast, then pays for it with quality 60 compression artifacts everywhere else in the picture. The same bytes could have bought a clean, unsharpened image at quality 80.
The light pass is gentler about it. Light at quality 75 comes in at 30,758 bytes, still a little under plain quality 80. If you genuinely like the crisper look, that is the pairing that pays for itself.
The filter itself is cheap. On a 512 pixel image the unsharp mask took between about 11 and 23 milliseconds, best of three runs, with the light pass at 11.33. Run time is not the reason to skip it. The reason is that the cost does not land on your server once, it lands on every visitor who loads the gallery, every time.
No sharpening on gallery thumbnails. At a 512 pixel box on AI art that is already clean, I cannot see the medium pass as an improvement at normal viewing distance, and it was 40 percent of the transfer.
If a set really needs crispness, light only, and drop quality one step. Radius 1, percent 50 at quality 75 looks sharper than plain quality 80 and still comes in smaller.
Strong and heavy nowhere. A 55 to 72 percent size penalty for halos around every edge is not a trade I can defend.
If you swapped Lanczos for bilinear after yesterday's piece, do not undo the saving with a sharpen. A light unsharp mask adds back more bytes than bilinear took off.
Laplacian variance measures how much hard edge contrast is in an image. It is a fair way to prove the sharpening survived compression, and it is not a quality score. A heavy unsharp mask scores five times higher than no sharpening and looks worse, with bright halos on every outline. So read that column as how much edge you are paying the encoder to keep, not as how good the picture looks.
Pillow 12.3, Image.resize to a 512 pixel long edge with LANCZOS, then ImageFilter.UnsharpMask with the radius, percent and threshold listed in the first table, then save to WebP with method=4 at each quality. Sizes are exact byte counts of the encoded buffer. Filter time is the best of three runs timed with perf_counter and covers the unsharp mask alone. Edge energy is the variance of a four neighbour Laplacian over the luma channel, excluding the one pixel border. The unsharpened baseline reproduces yesterday's Lanczos figures exactly, 26,662 bytes at quality 75 and 31,862 at quality 80, so the two runs are directly comparable.