Flip For Color
Experimental Site Notice
This is not a production site. Some pages may have unusual formatting or colors as part of a series of live experiments in visual perception of self illuminated displays. If you have any problems reading any pages, or if you find any are particularly easy to read, we'd love to hear about it. Please discuss at the SAPC GitHub discussions tab
Automatic Font Color based on Background Lightness
Each patch has data which also serves as an example. The font is smaller than optimum to stress the critical importance of spatial frequency.
The top few lines of text are automatically set based on the background color, and should always be easilly readable. The bottom line is the opposite color for comparison purposes, and may often be unreadable.
The 36% Flip
If you are only looking for a basic point to flip the text from black to white or vice versa, the cheat is to use the **Y** we just derived, and make the flip point about ` Y = 0.36; `. so for colors higher than 0.4 Y, make the text black `#000` and for colors darker than about 0.36 Y, make the text white `#fff`. Colors in between 0.36 Y and 0.40 Y make for weak contrast backgrounds, and are not recommended for content.
let textColor = (Ys < 0.36) ? "#fff" : "#000"; // Low budget down and dirty text flipper.
Why 36% and not 50%? Our human perception of lightness/darkness and of contrast is not linear. For a self illuminated display, it so happens that 0.36 Y is _about_ middle contrast under most typical conditions involving small text.
Yes it varies (between about 0.34Y and 0.42Y), and yes this is an over simplification. But if you are flipping text black or white, the simple answer can be a useful one, such as for auto-text color in a spreadsheet.
But I heard 18% was middle?
18% is a middle grey known to photographers in particular, associated with Ansel Adams's Zone V, and a useful target for mid-density of the negative. And it's the middle grey between black and white in terms of Munsell value. And for low spatial frequency patches of color under a specific adaptation environment, arranging a collection of uniform tiles, sure, 18.4% is the middle.
But this is "middle grey between a black which is really just very very dark grey, about 2%, and a reference white which is not actually 100%, but less, like 90% usually." Ah human perception. There's a lot of wiggly room in some of these metrics. In particular, the farther we get away from the "threshold" of the "just noticeable difference" (JND) the less accurate are our models of our perception, and the more variables apply to defining that perception.
So, to prepare you as we dig deeper here, I'll let you in on a well kept secret: contrast is not about two colors so much as it's about "spatial frequency" which for our purposes means the font weight and font size. Our luminance processing in our visual cortex is extremely sensitive to changes in spatial frequency, and high spatial frequency items, meaning small thin text like the text you are reading now, are perceived at a much lower contrast and therefore require much more lightness/darkness difference to be readable. (Please see my article "Stop Using Grey Text")
Perceptual Bonus Round
Predicting the perception of a given color and lightness is still a subject of active research, and not entirely settled science. The L* (Lstar) of CIELAB or CIELUV has been used to predict perceptual lightness, and even to predict perceived contrast. However, while L* works well for surface colors in a very defined/controlled environment, we've discovered that it does not work as well for self-illuminated displays and small bits, like text.
This varies depending on not only the display type and calibration, but also your environment, the overall page content, the polarity of the text (dark mode or light mode), size, etc. If you take the standard CIE Y from above, and raise it by around ^0.678, you'll find that 0.5 is typically the middle point to flip the text from white to black.
Perceptual Contrast Note: APCA uses a slightly different calculation to create Ys, estimated screen luminance. The flip point as used in the
maxContrast() function is 34.8 Ys, and the exponent to raise that to Ls is 0.646