Black vs White Text

Posted: 12/7/2007 4:01:31 AM

Whether it is a heat map or a simple legend on a graph, color has the uncanny ability to instantly convey to users the intricacies of their data. But like most powerful constructs, color can easily be misunderstood and abused. I highly recommend that all GUI developers read up on all the crazy (and cool) things you can do to manipulate colors.

Last year I spent a fair amount of time scouring the net trying to find a solution to a frequently experienced problem: background - foreground color readability. And for the most part I came up empty handed, so I did even more digging and researching and have found the optimal way of determining whether white or black text will look best on an arbitrary background color. At first I tried solely looking at the "V" component of the HSV color space, but that would dictate the I use black text on a blue background (which doesn't work).

I tried making my own custom metrics like if (R + G + B)/(255 * 3) was greater than 1/2 then use black, else white, but when you test the results, it just doesn't seem to work all that well. The problem lies with the human eye, while the monitor has a certain color addressing space, people actually perceive color differently. It makes sense when you think about it, for most of homo sapiens history have primarily looked at all the green vegitation surrounding us; so naturally the human eye is more perceptive to shades of green than red or blue. After some extra long digging, I stumbled upon the YIQ color space, where "Y" is the luma a.k.a. perceived luminance. The formula for calculating Y from RGB is Y = 0.299 * R + 0.587 * G + 0.114 * B

We can now calculate the maximum perceived luminance of white (0.299 * 255 + 0.587 * 255 + 0.114 * 255) and the midpoint at which something is "half-bright" by dividing the max into 2. Because I'm calculating values relative to one another, I simply made each color coefficient a whole integer to make an infinitesimally small difference in performance. You should try it out, it works really well.


//perceived luminance
private const int RED_LUMINANCE = 299;
private const int GREEN_LUMINANCE = 587;
private const int BLUE_LUMINANCE = 114;
//calculated from http://en.wikipedia.org/wiki/Luminance(video)
private const int MAX_LUMINANCE = (RED_LUMINANCE * 255 + GREEN_LUMINANCE * 255 + BLUE_LUMINANCE * 255);
private const int MID_LUMINANCE = MAX_LUMINANCE / 2;

/// <summary>
/// Finds the foreground (white or black) that will be easiest to read
/// with the given background
/// </summary>

public static Color CalculateForeColor(Color backColor)
{
    int totalCustomBrightness =
        ((backColor.R * RED_LUMINANCE) + (backColor.G * GREEN_LUMINANCE) + (backColor.B * BLUE_LUMINANCE));

    if (totalCustomBrightness <= MID_LUMINANCE)
        return Color.White;
    else
        return Color.Black;
}




Tags: Design;Tips and Tricks