colorful painting

Black or white text on a colour background?

By

in

How to calculate programmatically whether black text or white text is more readable on a colour background?

Here are some algorithms to calculate the brightness of a color. The example code is written in PHP, but you should be able to adapt the code examples to any other programming language. All of the algorithmns are applied on the following color map to have a first impression how they perform.

complete spektrum

Arithmetic Mean

f'(x)= \frac{a+b+c}{3}

In Code, this looks like:

PHP
function arithmeticMean($hex)
{
    $rgb = ColorUtils::hex2rgb($hex);
    return ($rgb[0] + $rgb[1] + $rgb[2]) / 3;
}

This results in an output for black/white text like:

spektrum for an arithmetic mean distribution

This is a very simple and effective algorithm. Unfortunately it fails between the green and turquoise region:

The quick brown fox jumps over the lazy dog

Geometric Mean

f'(x)=\sqrt[n]{rgb}

In Code, this looks like:

PHP
function geometricMean($hex)
{
    var $rgb = ColorUtils::hex2rgb($hex);
    return pow($rgb[0] * $rgb[1] * $rgb[2], 1/3);
}

This results in an output for black/white text like:

spektrum for a geometric mean distribution

Now the text is much better with the green background, but now the brightness of the yellow is wrong:

The quick brown fox jumps over the lazy dog

Quadratic Mean

f'(x)= \sqrt{\frac{r^2 +g^2+ b^2}{3}}

In Code, this looks like:

PHP
function quadraticMean($hex)
{
    var $rgb = ColorUtils::hex2rgb($hex);
    return sqrt( (pow($rgb[0], 2) + pow($rgb[1], 2) + pow($rgb[2], 2)) / 3);
}

This results in an output for black/white text on a colour background:

spektrum for a quad mean distribution

Works really well for all dark colours. Fails on purple.

The quick brown fox jumps over the lazy dog

HSV Value (Brightest Component)

f'(x)= max(r,g,b)

In Code, this looks like:

PHP
function valueFromHSV($hex)
{
    var $hsv = ColorUtils::hex2hsv($hex);
    return $hsv[2] * 255;
}

This results in an output for black/white text like:

spektrum for a brightness distribution

Dark colours are ok, Fails when the r, g or b value is close to 0x99.

Darkest Component

f'(x)= min(r,g,b)

In Code, this looks like:

PHP
function valueFromHSV($hex)
{
    var $rgb = ColorUtils::hex2rgb($hex);
    return min($rgb[0], $rgb[1], $rgb[2]);
}

This results in an output for black/white text like:

spektrum for a darkest distribution

Only the very bright colours work fine with this algorithm.

The quick brown fox jumps over the lazy dog

3D distance in RGB space

f'(x)= \sqrt{r^2 +g^2+ b^2}

In Code, this looks like:

PHP
function distanceIn3D($hex)
{
    var $rgb = ColorUtils::hex2rgb($hex);
    return sqrt( pow($rgb[0], 2) + pow($rgb[1], 2) + pow($rgb[2], 2));
}

This results in an output for black/white text like:

spektrum for a dist3d distribution

Worse than using the V component of the HSV representation.

The quick brown fox jumps over the lazy dog

Lightness From HSL

In Code, this looks like:

PHP
function lightnessFromHSL($hex)
{
    var $hsl = ColorUtils::hex2hsl($hex);
    return $hsl[2] * 255;
}

This results in an output for black/white text on a colour background:

spektrum for a lightness distribution

The best unweighted formula. Fails on yellow around 0xeeee00.

The quick brown fox jumps over the lazy dog

Weighted W3C Formula

Natural formulas don’t take into consideration that the human eye perceives some of the primary colours darker than others.

Eg pure green(0xff0000) is perceived brighter than pure blue(0x00ff00).
According to the W3C consortium, this biased perception can be modelled with the following weights:

r *= .299
g *= .587
b *= .111
f'(x)= r\cdot0.299 +g\cdot0.587+ b\cdot0.111

In Code, this looks like:

PHP
function weightedW3C($hex)
{
    var $rgb = ColorUtils::hex2rgb($hex);
    return $rgb[0] * 0.299 + $rgb[1] * 0.587 + $rgb[2] * 0.114;
}

This results in an output for black/white text like:

spektrum for a weighted distribution based on w3c

Very good algorithm, works really well.
Source: https://www.w3.org/TR/AERT/#color-contrast

The quick brown fox jumps over the lazy dog

Weighted Distance in 3D RGB Space

f'(x)= \sqrt{r^2\cdot0.241 +g^2\cdot0.691+ b^2\cdot0.068}

In Code, this looks like:

PHP
function weightedDistanceIn3D($hex)
{
    var $rgb = ColorUtils::hex2rgb($hex);
    return sqrt(pow($rgb[0], 2) * 0.241 + pow($rgb[1], 2) * 0.691 + pow($rgb[2], 2) * 0.068);
}

This results in an output for black/white text on a colour background:

spektrum for a weighted distribution

This is an improved version of the W3C fomula.
Source: https://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx

The quick brown fox jumps over the lazy dog

ColorUtils

The used ColorUtils class above consists of methods that are used to convert colors between different color spaces. The library is available here.

If you are interested in the source code to generate the color map examples above:


Photo by Markus Spiske on Unsplash. Image map by Wikimedia Commons.



Comments

One response to “Black or white text on a colour background?”

  1. […] time ago, there was an article about Black or white text on a colour background? In this one, I described different algorithms to calculate the best text color (black or white) for […]

Leave a Reply

Your email address will not be published. Required fields are marked *