I answered a similar question on Game Development Stack Exchange a while ago, so let me summarize the answers there.
You idea of picking either black or white, depending on which one contrasts better with the background color, is sound. However, simply averaging the RGB components will not give a good indication of how light a particular color will appear to the human eye, for (mostly) two reasons:
First of all, for reasons related to human color perception, the red, green and blue channels do not have the same relative luminance; in fact, pure blue (
#0000ff) is only about 10% as bright (and pure red only about 30% as bright) as pure green (
#00ff00). Thus, e.g. black text on pure blue will never have a particularly good contrast.
For example, the images below show examples of black and white text on pure red, green and blue backgrounds (
You can clearly see the differences in contrast, even though all these colors have the same average RGB value.
The other complication is that the RGB color spaces normally used on computer screens (like sRGB) are not linear, but have a display gamma around 2 or so. That means that e.g. the RGB color
#7f7f7f = "50% gray" does not actually appear half as bright as pure white (
#ffffff), but rather only about 25% as bright, whereas the color that actually has a relative luminosity halfway between black and white is actually closer to "75% gray" (
Conveniently, though, the human eye is also non-linear, and is more sensitive to differences in darker shades. In fact, for shades of gray, the non-linearities roughly cancel out, so that the RGB 50% gray (
#7f7f7f) is still perceptually about equally far from both black and white. As a demonstration, here is some black and white text on 25%, 50% and 75% gray backgrounds (
However, to accurate calculate the luminosity of an arbitrary RGB color, we do need to account for the display gamma, since it's the linear luminosity values that need to be averaged.
To put it all together, to determine whether black or white text would have better contrast on a background, you need to:
- convert the gamma-compressed RGB color values into linear RGB,
- take a weighted average of the linear R, G and B components, and
- compare the resulting average against a suitable threshold.
The threshold value that gives best results may be determined experimentally, but one would generally expect it to lie close to the luminance of 50% RGB gray. In any case, it's worth noting that the perceived contrast will vary depending on the viewer's display settings and viewing conditions, so there is no single optimal value for everybody, but rather a fairly broad range of more or less equally good choices.
If you just want a simple formula to plug numbers into, try this:
- if 0.2126 × Rγ + 0.7152 × Gγ + 0.0722 × Bγ > 0.5γ, choose black; else choose white,
where γ is the approximate display gamma value (γ = 2.2 is typical).
For an even simpler approximation, you can ignore the gamma correction (i.e. effectively assume that γ = 1) and just use the condition 0.2126 × R + 0.7152 × G + 0.0722 × B > 0.5. For shades of gray, this will make no difference (since we're applying the same gamma to both sides of the inequality), but it does somewhat overestimate the luminance of saturated colors. Fortunately, on such colors, both black and white tend to have decent contrast anyway, so the error may not matter much in practice.
Finally, note that, if the background color is not uniform, it's possible that no single text color may look good. In such cases, you may consider e.g. using black text with a white outline, or vice versa, or perhaps surrounding your text with a semitransparent box of the opposite color.