Rendered at 14:38:03 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
jonathanlydall 23 hours ago [-]
I'm pretty sure the same issue happens with PNGs, which being lossless are generally good for icons as they don't land up with compression artifacts like what happens in JPEGs (which the author points out are really for photographs), they also support alpha blending.
When Chrome introduced this "optimization" and it made it through to an Electron release which we were upgrading to, it really messed up the icons in a lot of places in our product such that we had to hold off the upgrade until we had SVGs to replace them.
SVGs also have the advantage of being able to respond to light/dark mode. We just needed to put each icon in its own shadow DOM to avoid styles clashing between the different SVGs if they happen to be named the same which was a bit of an annoyance for our graphic designer.
polpo 22 hours ago [-]
I don't think it's possible to partially decode/render a PNG in a way similar to JPEG. The only exception I can think of is adam7 interlacing, which _does_ store the initial pass as 8x8 blocks, but I'm pretty sure PNGs with adam7 are rare in practice because they do result in larger files.
jonathanlydall 18 hours ago [-]
Perhaps it was a different optimisation, but it definitely made significantly downscaled PNGs look way worse under particular circumstances. It was especially noticeable on triangles we used on nodes of a tree view control.
bawolff 15 hours ago [-]
Yeah, images with sharp contrasts like line art tend to do badly with the scaling algorithms that work well with photos. It is why browsers introduced `image-rendering: pixelated` css, but as you noted in the other comment, SVG is almost always the best answer.
bawolff 21 hours ago [-]
its not possible to do this for pngs. Most likely though chrome is just using a quicker downscaling algorithm than your graphics editor is using (or just the wrong one. There is no universally correct algorithm, different ones work better for different image types).
Have you tried adjusting the image-rendering css property?
jonathanlydall 18 hours ago [-]
I can’t recall the details any more, but I recall seeing something like this in my research on the issue, it might have been a solution to the issue, or I was worried it had a noticeable performance impact.
We may also have moved to the SVGs anyway as they also look much nicer when running at different zoom levels like 125%.
jancsika 21 hours ago [-]
> We just needed to put each icon in its own shadow DOM to avoid styles clashing between the different SVGs if they happen to be named the same which was a bit of an annoyance for our graphic designer.
What do you mean by "named the same" here?
jonathanlydall 18 hours ago [-]
SVGs can define styles and element ids.
The problem is if these same element ids and styles are used in different places in your DOM, including other SVGs.
Our product is a bit of a platform with extensibility which includes icons for ui elements. It was very easy for us or our users to accidentally use the same style names or element ids when exporting from Adobe Illustrator, especially for icons which are similar to others and started as a copy.
When using SVGs as a background it’s not an issue, but we want our SVGs to be able to have different colours be usable in light or dark mode which CSS can do, except it doesn’t work when making the SVG a background.
Shadow DOM solves this issue perfectly.
jancsika 18 hours ago [-]
Why not use classes instead of element ids?
jonathanlydall 17 hours ago [-]
When I referred to “styles” I was referring to classes.
If the same class was defined in a multiple SVGs under their respective <style/> elements, they would affect each other.
So sometimes if two icons were in the DOM at the same time, the one’s colours might look wrong since both had the same class names they were using.
Element ids were also not used so much for styling but instead for common SVG components (or something, I don’t know the terminology), so would similarly cause rendering issues.
Like I said it was especially common if the one started as a copy of the other in Illustrator.
The “fix” was to ensure unique element ids and class names, but this was an annoying extra step.
Using a shadow DOM for each icon which is an SVG circumvents the entire problem as they ignore anything outside their isolated DOM.
jancsika 16 hours ago [-]
Ah, I see.
What's so funny about this is that XML is absolutely brimming with rich namespace solutions for all the problems you don't have. For example, if your users ever want to blithely roll their own "href" namespace, don't worry! The xmlns http://www.w3.org/1999/xlink is there for them to safely disambiguate all the custom:hrefs from the xlink:href things!
Meanwhile, in 2026 you've got two designers who each use their own "bgcolor" and XML is like, "Nope!"
Digression-- I just checked MDN and apparently xlink is deprecated! This makes me want to write alternate history from the mid-2000s about the href wars: XML namespacing parties where front-end designers would mash up dozens of href namespaces in the same document.
bawolff 15 hours ago [-]
That's not really the problem namespaces were intending to solve in XML. (XML namespaces are about extensibile semantics, not content isolation)
RunSet 17 hours ago [-]
Chrome puts artifacts in its canvas when you draw text and Firefox does not. Makes bitmap fonts ugly.
Ah this is weird! My understanding is that png doesn't store the images like jpg at all, they use a palette of all the colors in the image. Then each pixel is a reference to this palette. So it's not IDCT scaling, but I imagine there are other tricks to save on space/cpu when decoding pngs.
binaryturtle 21 hours ago [-]
PNG supports both, palette based (for 8 bit and less), and just straight direct/true color (RGB or ARGB).
(still different from JPEG's method, of course)
StilesCrisis 23 hours ago [-]
You were using bitmap icons that were more than 8x larger on each axis than necessary? Why?
aljgz 23 hours ago [-]
Guess: same icon used in very small size for lists/menus and much larger when you select the item. Also, if the same one is used for all resolutions, the actual size can vary widely between monitor sizes, DPIs, resolutions, scales. Good to have one that looks decent to people who see it large and/or care about quality
jonathanlydall 18 hours ago [-]
Pretty much.
The icon appeared bigger in some places than others and wanted it to look decent on higher DPI displays.
SVGs are so much better for this though, smaller in size and look great at pretty much any resolution.
21 hours ago [-]
advisedwang 20 hours ago [-]
> Really, the moral here is that you should not use JPEG for icons and the like
And, more importantly, you should use images that are an appropriate resolution for the size they will be displayed. Even if you switch to PNG, using a 2000x2000 image for a icon displayed 20x20 is a waste.
ryandrake 18 hours ago [-]
> using a 2000x2000 image for a icon displayed 20x20 is a waste
The number of web sites that do this is enraging.
AlienRobot 18 hours ago [-]
Some websites even do this for the favicon.
decimalenough 15 hours ago [-]
The favicon spec is a mess these days. The OG was 16x16 pixels, but now you're supposed to provide a whole slew of resolutions up to 192x192 and you can do SVG as well. And browsers have no consistency in how they pick the one to actually display.
> browsers have no consistency in how they pick the one to actually display
The same Firefox on macOS shows different favicons depending on if I'm using it on the laptop screen or an external one (probably because of different pixel densities). It's crazy!
Wow, this explains an issue I ran into for images from a certain vendor when we used idct scaling for the same reason:
Some encoders (and a meaningful share of JPEGs in the wild) leave zeros or garbage in the unused rows/columns of the trailing MCU. At full-size decode those samples are cropped away, but IDCT scaling mixes them into neighboring output pixels and the artifacts become visible. See https://crbug.com/890745 and https://github.com/libjpeg-turbo/libjpeg-turbo/issues/297
In our particular case we were able to just crop out the edges and it was worth the tradeoff
gutechh 23 hours ago [-]
Thanks, I knew it was much more efficient, but seeing the numbers in the benchmarks make me appreciate this "trick" even more!
debazel 24 hours ago [-]
Chrome and Firefox uses two different scaling algorithms that is probably contributing a lot more to this difference. Chrome is more blurry in general while Firefox is sharper but has slightly more ringing artifacts. Personally I prefer the Firefox version.
aidenn0 24 hours ago [-]
I've noticed that a lot of objective measures of image quality prefer blur to ringing, but many subjective measurements tilt the other way.
ryandamm 23 hours ago [-]
That's why you should prefer LPIPS to PSNR. LPIPS is a learned metric that uses subjective human input.
That said, it's limited due to the training set, iirc. So we still use PSNR regularly — it's an easily-understood metric with widely-understood limitations, which is in practice not too bad. Devil you know, etc.
m-schuetz 20 hours ago [-]
Ironically when we did compression quality evaluations, me and my colleague found that PSNR way better matches our perception of quality, than LPIPS or FLIP. Our assumption is that LPIPS is biased towards quality artifacts that is not representative of compression quality artifacts.
hatthew 11 hours ago [-]
It depends on the target audience. Many people prefer the oversharpened content. I always find it incredible how so many people never change the oversharpened and oversaturated default settings on TVs.
gutechh 24 hours ago [-]
I do prefer the firefox look too! I'd be curious to hear your take on what could be the root cause of this. I'm far from a image rendering expert. But I remember when inspecting the edges and curves of the images in a DCT visualizer they were all in the AC coefficients which is what led me to the conclusion of the post.
debazel 22 hours ago [-]
I'm far from an image rendering expert myself, but I don't think there is a single root cause here. I don't doubt the partial decoding would affect the final result just as the scaling algorithm itself, so in the end it's going to be a combination of it all.
The reason I believe the scaling algorithm is contributing more in your particular example, is because it looks almost exactly like a classic B/W comparison between a sharp ringing heavy algorithm and a more blurry one.
It would be interesting to see how your example would look if you used colors instead, where the ringing artifacts become a lot more obvious. Like this one: https://twinlens.app/compare?share=8fb890a5820e
gutechh 21 hours ago [-]
Thanks for these! Indeed I understand your point now. I'll add a little note on the post later today.
Another interesting bit is that It wasn't so easy to get an image showing the issue a clear as we had it. My hunch is that moiré patterns might have a role in this, as most images where the the issue showed, had some kind of grid/repetition. I suppose some images might be more affected by the IDCT and other more affected by the scaling algorithm, making it more or less visible. It's a mix like you said!
ack_complete 23 hours ago [-]
I wonder if this is a gamma correction related. JPEG encodes directly in a non linear color encoding (full-range YCbCr), so the partial decoding may be effectively scaling without gamma correction.
PetitPrince 24 hours ago [-]
So is Firefox doing a full rendering then scaling, or is it also doing a partial rendering but in another way? You're only telling one side of the story.
the8472 24 hours ago [-]
Firefox doesn't decode the image into a full-resolution buffer and then downscale either, instead it does a streaming decode and applies downscaling on the fly. They call it downscale-during-decode[0]. I guess it doesn't do partial decoding of the 8x8 blocks though.
Ah! It took a fair bit of digging to get there. Maybe I'll look into the firefox pipeline and make another post. In the mean time if anyone know the internals and how firefox does it, I'm eager to hear about it!
kccqzy 19 hours ago [-]
If you care about the rendered quality, you should never use the browser itself to downscale images. Browsers don’t consistently use the state-of-the-art algorithm for downscaling, which is Lanczos 3-lobe. This is independent of partial IDCT. Browsers generally favor performance. And the algorithm chosen could even depend on the specific GPU in use.
That said if you are using the web to develop UI, it’s accepted that different browsers and users get slightly different renderings, unless you write your own image renderer with canvas. If you do care that much you should probably write native code.
Matumio 6 hours ago [-]
Also the pixels were probably downscaled in sRGB gamma by JPEG instead of linear light, so the overall brightness will be wrong anyway, if there was lots of high-frequency content in the original.
qdotme 17 hours ago [-]
I'm actually curious if nowadays some ML-based algorithms are not beating Lanczos 3-lobe to it - at least in perceptual quality of the downscale. The have /long/ surpassed it in upscaling.
linzhangrun 5 hours ago [-]
Could this be related to the GPU? I remember that sites like Reddit use subtle differences in GPU rendering to fingerprint devices.
gwittel 16 hours ago [-]
Sometimes you can control this with the “image-rendering” CSS attribute. It lets you control the scaling algorithm used. Different browsers seem to choose the algorithm in different ways. Came across this when an image looked different between two browsers, but only on high DPI monitors.
altairprime 19 hours ago [-]
15px suggests that using an ICO file, horrifying as that sounds, might be a very successful solution here. Yes, I know PNG and GIF would also work, but if ICO format is still support for favicons at multiple resolutions with clean scaling, then wouldn’t it be support for doc images as well?
edflsafoiewq 18 hours ago [-]
The DCs giving you a 1/8 downscale is well known, but the interesting part to me was this generalizes so you can get 1/4 (resp. 1/2) downscales by using only the 2x2 (resp. 4x4) block of LF coefficients.
qdotme 17 hours ago [-]
This is even more interesting (full disclosure - I vibecoded a lib for it lately - lowzag) when you take into consideration working set restrictions.
In almost every case, downscaling-while-decoding is going to beat decode-then-scale on performance - merely keeping the uncompressed image in RAM is going to cost you, when you can scale it down in L1 or even vector registers.
talon8635 11 hours ago [-]
I had a hell of a time rendering svg to print sanely in chrome, no issues in ff
bluedino 23 hours ago [-]
Reminds me of digging into HTML Canvas scaling, especially when HiDPI Macs came out.
LoganDark 23 hours ago [-]
Not sure why one would use JPEG for a little digital icon anyway. It is indeed only acceptable for photos. SVG is a good choice for icons, PNG or WebP for other digital content.
gutechh 23 hours ago [-]
Yes! for a bit of backstory, it was an internal icon on a prototype that we were polishing up. So I get why the person did it at the time, running after the svg would have been too much effort for something that might have been tossed in the end.
LoganDark 22 hours ago [-]
I mean it's surprising someone would ever save a digital icon as JPG in the first place rather than something like PNG.
kevin_thibedeau 1 hours ago [-]
Digital natives are commonly unaware of how anything works.
smallnix 24 hours ago [-]
Would be nifty if they only did that partial scaling when there's little memory available.
adzm 20 hours ago [-]
It's not a matter of memory but speed; the difference is drastic since memory is slow (well compared to cpu cache)
gutechh 24 hours ago [-]
ahah, yeah surprising considering how Chrome is RAM hungry, 20mb isn't that much of a win. On another end when trying to reproduce the glitch, it was quite hard! basically invisible on photos and not all icons were affected. So it does work very well. It was kind of our fault to use a jpg for an icon ahah.
kristianp 17 hours ago [-]
I assumed Chrome is memory hungry due to the JavaScript VM, but there's probably many optimisations that trade memory for speed. (Unlike this one).
Theodores 23 hours ago [-]
You can over ride lib-jpeg-turbo with the moz-jpeg 'update' if you want much nicer jpeg things going on.
Mozilla wringed out more from JPEG for Meta/Instagram about a decade or so ago, and their library would be noteworthy in this article, for comparison. The basic idea was that JPEG was devised when CPU cycles were expensive and displays were analogue, meaning that only a limited effort was made to optimise, resulting in things like banding, that OG CRTs handled well, unlike modern digital displays (on systems with 1000000 more CPU).
Regardless of what tools you are using, going from 'gigapixels' to a thumbnail is going to be a two or more step process, with a colourful item rendered a grey mush, when you really needed some of the colours in the original.
Few respect the pixel blocks of JPEG (8 x 8) and most graphic artists have no knowledge of the powers of two. Once upon a time it was necessary to align image dimensions with important binary numbers, particularly if working with texture maps for early 3D graphics.
The other thing going on in the article is that practically every web screen should be considered 'retina' resolution, with everything apart from the lamest office PC having a pixel resolution equivalent to 1.5x the amount of pixels shown 'nominally'. If the web stats say '1280 x 720' as a oft-used resolution, regardless of device, that will be a 1920 x 1080 screen at 1.5x, with Chrome and other browsers packing in 1920 rather than 1280 pixels across, if the image has the pixels in it for that.
scotty79 15 hours ago [-]
People who usually complain about AI write ups apparently aren't interested in this topic.
iknowstuff 20 hours ago [-]
Thoroughly enjoyed reading this!
In other news, AVIF is at 95%: https://caniuse.com/avif so it's probably time to give it the first preference over jpg.
tosti 5 hours ago [-]
The matrix of sources for a large picture can be mind boggling huge.
We have jpeg, webp, avif and jpegxl. Portrait orientation and landscape. 3 or 4 different pixel ratios and of course, different screen resolutions. Dark mode support doubles all of that work. Hardly anyone is going to do all of this correctly, automated or not.
gutechh 20 hours ago [-]
Thank you! I wonder if we'll ever see avif/webp takeover or if it's gonna be like ipv4/ipv6.
theandrewbailey 17 hours ago [-]
I've primarily been using AVIF with JPG as fallback on my blog for a few years. The efficiency gain is wild. I wonder if AV2 will be integrated into AVIF at some point (AVIF is based on AV1).
adzm 20 hours ago [-]
Interesting, I don't seem to see options for this in avifenc for example, I suppose this needs fancier interaction with the library directly
When Chrome introduced this "optimization" and it made it through to an Electron release which we were upgrading to, it really messed up the icons in a lot of places in our product such that we had to hold off the upgrade until we had SVGs to replace them.
SVGs also have the advantage of being able to respond to light/dark mode. We just needed to put each icon in its own shadow DOM to avoid styles clashing between the different SVGs if they happen to be named the same which was a bit of an annoyance for our graphic designer.
Have you tried adjusting the image-rendering css property?
We may also have moved to the SVGs anyway as they also look much nicer when running at different zoom levels like 125%.
What do you mean by "named the same" here?
The problem is if these same element ids and styles are used in different places in your DOM, including other SVGs.
Our product is a bit of a platform with extensibility which includes icons for ui elements. It was very easy for us or our users to accidentally use the same style names or element ids when exporting from Adobe Illustrator, especially for icons which are similar to others and started as a copy.
When using SVGs as a background it’s not an issue, but we want our SVGs to be able to have different colours be usable in light or dark mode which CSS can do, except it doesn’t work when making the SVG a background.
Shadow DOM solves this issue perfectly.
If the same class was defined in a multiple SVGs under their respective <style/> elements, they would affect each other.
So sometimes if two icons were in the DOM at the same time, the one’s colours might look wrong since both had the same class names they were using.
Element ids were also not used so much for styling but instead for common SVG components (or something, I don’t know the terminology), so would similarly cause rendering issues.
Like I said it was especially common if the one started as a copy of the other in Illustrator.
The “fix” was to ensure unique element ids and class names, but this was an annoying extra step.
Using a shadow DOM for each icon which is an SVG circumvents the entire problem as they ignore anything outside their isolated DOM.
What's so funny about this is that XML is absolutely brimming with rich namespace solutions for all the problems you don't have. For example, if your users ever want to blithely roll their own "href" namespace, don't worry! The xmlns http://www.w3.org/1999/xlink is there for them to safely disambiguate all the custom:hrefs from the xlink:href things!
Meanwhile, in 2026 you've got two designers who each use their own "bgcolor" and XML is like, "Nope!"
Digression-- I just checked MDN and apparently xlink is deprecated! This makes me want to write alternate history from the mid-2000s about the href wars: XML namespacing parties where front-end designers would mash up dozens of href namespaces in the same document.
https://stackoverflow.com/questions/47139528/html5-canvas-te...
My inner paranoiac says it is something like yellow dots[0] for browsers but it could just be negligent developers.
[0] https://en.wikipedia.org/wiki/Printer_tracking_dots
(still different from JPEG's method, of course)
The icon appeared bigger in some places than others and wanted it to look decent on higher DPI displays.
SVGs are so much better for this though, smaller in size and look great at pretty much any resolution.
And, more importantly, you should use images that are an appropriate resolution for the size they will be displayed. Even if you switch to PNG, using a 2000x2000 image for a icon displayed 20x20 is a waste.
The number of web sites that do this is enraging.
https://en.wikipedia.org/wiki/Favicon#How_to_use
The same Firefox on macOS shows different favicons depending on if I'm using it on the laptop screen or an external one (probably because of different pixel densities). It's crazy!
Some encoders (and a meaningful share of JPEGs in the wild) leave zeros or garbage in the unused rows/columns of the trailing MCU. At full-size decode those samples are cropped away, but IDCT scaling mixes them into neighboring output pixels and the artifacts become visible. See https://crbug.com/890745 and https://github.com/libjpeg-turbo/libjpeg-turbo/issues/297
In our particular case we were able to just crop out the edges and it was worth the tradeoff
That said, it's limited due to the training set, iirc. So we still use PSNR regularly — it's an easily-understood metric with widely-understood limitations, which is in practice not too bad. Devil you know, etc.
The reason I believe the scaling algorithm is contributing more in your particular example, is because it looks almost exactly like a classic B/W comparison between a sharp ringing heavy algorithm and a more blurry one.
For reference here is a quick comparison between Lanczos/Bilinear I threw together: https://twinlens.app/compare?share=40973d84a174
It would be interesting to see how your example would look if you used colors instead, where the ringing artifacts become a lot more obvious. Like this one: https://twinlens.app/compare?share=8fb890a5820e
Another interesting bit is that It wasn't so easy to get an image showing the issue a clear as we had it. My hunch is that moiré patterns might have a role in this, as most images where the the issue showed, had some kind of grid/repetition. I suppose some images might be more affected by the IDCT and other more affected by the scaling algorithm, making it more or less visible. It's a mix like you said!
[0] https://bugzilla.mozilla.org/show_bug.cgi?id=1045926
That said if you are using the web to develop UI, it’s accepted that different browsers and users get slightly different renderings, unless you write your own image renderer with canvas. If you do care that much you should probably write native code.
In almost every case, downscaling-while-decoding is going to beat decode-then-scale on performance - merely keeping the uncompressed image in RAM is going to cost you, when you can scale it down in L1 or even vector registers.
Mozilla wringed out more from JPEG for Meta/Instagram about a decade or so ago, and their library would be noteworthy in this article, for comparison. The basic idea was that JPEG was devised when CPU cycles were expensive and displays were analogue, meaning that only a limited effort was made to optimise, resulting in things like banding, that OG CRTs handled well, unlike modern digital displays (on systems with 1000000 more CPU).
Regardless of what tools you are using, going from 'gigapixels' to a thumbnail is going to be a two or more step process, with a colourful item rendered a grey mush, when you really needed some of the colours in the original.
Few respect the pixel blocks of JPEG (8 x 8) and most graphic artists have no knowledge of the powers of two. Once upon a time it was necessary to align image dimensions with important binary numbers, particularly if working with texture maps for early 3D graphics.
The other thing going on in the article is that practically every web screen should be considered 'retina' resolution, with everything apart from the lamest office PC having a pixel resolution equivalent to 1.5x the amount of pixels shown 'nominally'. If the web stats say '1280 x 720' as a oft-used resolution, regardless of device, that will be a 1920 x 1080 screen at 1.5x, with Chrome and other browsers packing in 1920 rather than 1280 pixels across, if the image has the pixels in it for that.
In other news, AVIF is at 95%: https://caniuse.com/avif so it's probably time to give it the first preference over jpg.
We have jpeg, webp, avif and jpegxl. Portrait orientation and landscape. 3 or 4 different pixel ratios and of course, different screen resolutions. Dark mode support doubles all of that work. Hardly anyone is going to do all of this correctly, automated or not.