Why Tiny JPEGs Look Different in Chrome
The Mystery of the "Thick" Tiny JPEGs in Chrome
By Guillaume Técher | August 3, 2026
What initially seemed like a rendering glitch in Google Chrome turned out to be a sophisticated optimization technique for decoding JPEGs.
An Unexpected Visual Discrepancy
While looking at a colleague's screen, I noticed a logo didn't quite match the version on my own machine. On their screen, the image appeared thinner and more accurate to the source file.
To investigate, I created a reproduction image. When you squint or view it from a distance, the Chrome-rendered version looks noticeably thicker.
I thought it was a bug. I wondered: Why is Chrome rendering this differently?
The answer lies in how Chrome handles JPEGs when they are displayed at very small scales.
The Inefficiency of Standard Scaling
The "obvious" way to render a small version of a JPEG is to fully decompress the image into memory and then shrink it. However, this is incredibly wasteful.
Consider a scenario where a large image is shrunk to a tiny icon:
| Metric | Full Resolution | Target Scale |
|---|---|---|
| Dimensions | px | px |
| Memory Usage |
Most of the data in the bitmap is discarded during the downscaling process. Crucially, the data being lost isn't random; it is primarily high-frequency information.
Understanding Frequency in Images
Imagine a detailed photo of a tree:
- High-Frequency: The jagged edges of bark and individual leaves (rapid pixel changes).
- Low-Frequency: The general shape of the canopy or the trunk (slow pixel changes).
When you scale that tree down to pixels, the leaves vanish, leaving only a green blob and a brown line. The high-frequency details are effectively erased.
A Primer on JPEG Storage
To understand the optimization, we need a basic grasp of how JPEGs work. I'll avoid heavy math, but here are the core concepts:
- Block Splitting: The image is divided into pixel blocks.
- Frequency Conversion: Each block is converted into the frequency domain.
- Low Frequency: A flat, solid color (the constant component).
- High Frequency: A checkerboard pattern (maximum change).
The process of JPEG compression essentially asks: "How much of each of these patterns exists in this block?"
The JPEG Pipeline:
- Divide image into blocks.
- Convert to frequency domain.
- Quantize coefficients (this is where the lossy part happens).
- Store coefficients efficiently.
The "1/8 Scale" Shortcut
If you need to shrink an image by a factor of 8, an entire block can be represented by a single pixel.
Since high-frequency details disappear during heavy downscaling, Chrome can simply ignore the high-frequency coefficients and only decode the low-frequency data.
This approach is significantly faster and consumes far less memory because the browser skips a huge portion of the decompression work. This works for any scale that is a fraction with a denominator of 8 (for more, see jpegclub.org).
Technical Note: This process involves the Inverse Discrete Cosine Transform (IDCT), which moves data from the frequency domain back into the image domain.
How Chrome Implements This
Chrome doesn't handle this alone; it delegates the work to Skia, which in turn uses libjpeg-turbo.
The logic follows this pseudo-code flow:
if (target_size < threshold) {
float scale = find_closest_fraction_of_8(target_size);
decode_partial_idct(scale); // Skip high-frequency coefficients
apply_final_downsample(target_size);
} else {
decode_full_image();
scale_image(target_size);
}
In the case of the "thick" logo, the image was so small that Chrome decoded it at scale. Only the constant component (the lowest frequency) remained, while the subtle gradients and edge-softening (higher frequencies) were discarded.
The Final Takeaway
Moral of the story: Do not use JPEG for icons, logos, or simple graphics.
The JPEG format—and the optimizations Chrome uses—is specifically engineered for photographs. As the name implies (Joint Photographic Experts Group), it is designed for photos, not precise vector-like imagery.