David Baron's Weblog

Hue-preserving color inversion with SVG filters

Saturday, 2011-04-30, 14:42 -0700

Back in January of 2010, I had a little fun with SVG filters that I realize I probably should have written about at the time, other than a single link in a blog entry on painting performance in foreignObject.

It all started when we got some large monitors on the wall in the office, and started using the one in the platform and Firefox development area to display Tinderboxpushlog. Shawn (who sits closest to the monitor) didn't like the overwhelming bright white display. Rather than write a user style sheet, I decided to take a more fun approach: invert the colors of the page using SVG filters.

I didn't want the effect of a photographic negative, since we're all used to what red, orange, and green mean on tinderbox, and a standard color inversion would turn the red into aqua, the green into fuchsia, etc.

So at first, I experimented with combining the inversion with a type="hueRotate" feColorMatrix SVG filter. This approach was good at preserving lightness, but not very good at preserving hue, which in my case was more important. (With linearRGB, it was not as good at preserving lightness.)

(Note that all of these examples only work in Gecko-based browsers such as Firefox, since they apply SVG filters to HTML elements, which is not supported by other browsers. Also, in all of the examples, you can edit the URL to look at other pages through the filter, or you can interact with the pages and click on links as normal.)

So then I figured out a way to implement my initial naïve idea: think of the colors as HSL (hue, saturation, lightness), and just invert the lightness. Once I figured out a (somewhat hacky) way to do it using SVG filters, it actually turned out to work quite well for the case I was interested in (tinderboxpushlog, compare to original), and also produces interesting effects on other web sites (mozilla.org, compare to original). See the comments in the source of the page for a detailed explanation.

This approach has the disadvantage that it's not very good at preserving relative lightness. This is because human perception of lightness is strongly skewed between the red, green, and blue components: in sRGB, 72% of the lightness comes from the green, 21% from the red, and 7% from the blue. This is why blue text on a white background and lime (the CSS term for full green) text on a black background tend to be quite readable, while the inverses are not.