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.

## Arithmetic Mean

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

In Code, this looks like:

```
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:

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

## Geometric Mean

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

In Code, this looks like:

```
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:

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

## Quadratic Mean

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

In Code, this looks like:

```
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:

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

## HSV Value (Brightest Component)

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

In Code, this looks like:

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

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

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:

```
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:

Only the very bright colours work fine with this algorithm.

## 3D distance in RGB space

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

In Code, this looks like:

```
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:

Worse than using the V component of the HSV representation.

## Lightness From HSL

In Code, this looks like:

```
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:

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

## 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:

```
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:

Very good algorithm, works really well.

Source: https://www.w3.org/TR/AERT/#color-contrast

## 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:

```
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:

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

## 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.

## Leave a Reply