Web color is still broken

7 years after Computer Color is Broken, the web is still doing color wrong.

Correct color mixing

Correctly rendered gradients between primary colors

Physically correct color gradients (as you would get, for example, along an out of focus edge between colors) are equally bright around the midpoint, representing the average between the two colors. Incorrect rendering turns the middle into murky dark colors.

There are other ways to do gradients in subtly different ways that are useful for design purposes, but the incorrect way browsers do them is not.

Your browser

CSS gradients
SVG gradients

Correct transparency

Green and white at 25% opacity, black at 75% opacity

Overlaying bright colors at 25% transparency is supposed to tint underlying dark areas properly, washing them out like a light shone onto a projection screen. Incorrect rendering results in excessive contrast, as if we just turned up the camera exposure or stuck on an unlit filter.

Overlaying black at 75% shouldn't completely overpower bright areas nor crush the blacks. Think about how bright the scene would be if you had 4 lights and turned off 3.

There are reasons to want a different effect, which is why we have blend modes, but those are an explicit design choice. Alpha blending needs to work as in this example for things like font and shape anti-aliasing to work properly and look right, with consistent weight and smooth edges across different background colors.

Your browser

PNG transparency
CSS rgba() background

CSS opacity
SVG fill-opacity

Correct scaling

A test image scaled down by powers of two.

The outer and inner parts of the grey square should be the same overall brightness, since they both emit an average amount of light half as bright as white. Incorrect rendering makes the outer part darker.

The image should retain the same overall brightness as it is scaled down. Incorrect rendering makes the smallest copies too dark.

Note: this demo only works properly if the image is displayed at 1:1 pixel size. If you have a HiDPI display or are using the zoom feature, your browser is already scaling it (incorrectly) and the full-size image will look wrong.

Your browser

<img> with dimensions
CSS background-image

CSS transform
SVG image

What's going on?

Almost all colors on the web (from the data in your average PNG file to hex values in CSS and SVG) are represented not as actual color intensities, but using a lossy compression algorithm called "8-bit sRGB". Most people call this a "color space", but its primary purpose is to represent color data in fewer bits than would be required for decent quality if you were storing actual, numerical brightness values corresponding to light intensity. Therefore, it is more useful to think of it as a lossy compression technology.

Unfortunately, by calling it a "color space", we've misled the vast majority of developers into believing that you can do math on sRGB colors, and by exposing the raw sRGB numbers to users, we've misled them into thinking those numbers have a reasonable meaning. Just like you can't mix the bits of two MP3 files without uncompressing them* and expect to get something that sounds like both sounds mixed together properly, you can't take two sRGB color values, mix them, and expect to get the right color. And yet, this is what every major browser does.

The correct way to process sRGB data is to convert it to linear RGB values first, then process it, then convert it back to sRGB if required. If you are doing any mathematical operations on sRGB color data directly, your code is broken. Please don't do that. It's 2022; it's about time we make computer graphics work properly.

There is a color-interpolation SVG attribute that fixes this problem, and this page attempts to use it. Unfortuntely, no browser implements it yet. That attribute was first specified in SVG 1.1 from 2003. We're coming up on 20 years of browsers not bothering to implement correct color blending.

It's worth noting that GPU manufacturers and most game developers have had this figured out for a long time now, since linear processing is required for realistic environments, especially with complex effects. Modern GPUs can transparently convert from/to sRGB when loading and rendering images, without any performance penalty. This ends up causing pain when designers using broken design tools find things look different in a correctly implemented game engine.

Reference images created in GIMP 2.10.30 (which is one of the few open source image editing apps that actually does blending and gradients properly). GIMP 2.10 was the first release to do this right, back in 2018.

* Technically, the equivalent audio compression algorithm is µ-law encoding. This is what happens when you try to mix two µ-law audio files together without proper decoding (though with some bit manipulation to make the encoding monotonic, so it's fair). That's what we're doing to our colors.