Tag: ColorUtils

Black or white text on a colour background?

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:

The quick brown fox jumps over the lazy dog

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:

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:

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

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:

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.

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:

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.

The quick brown fox jumps over the lazy dog

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

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:

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: http://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:

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

This is an improved version of the W3C fomula.
Source: http://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.

PHP ColorUtils

A neat collection of useful methods to convert colors between different color spaces is available on GitHub (MIT License). It can convert any values between RGB, HSL, CMYK, YUV and HEX colors.

I created a ready-to-use php class out of this method collection:

Usage

The methods itself are mostly self-explaining. To use the class, just include the php file into your script and you are ready to go:

<?php
require('ColorUtils.php');

The methods itself are static, they can be used like:

$hex = '#ffcc00';
$rgb = ColorUtils::hex2rgb($hex);

Which results in an array with three values (red, blue, green) in $rgb:

// [0 => 255, 1 => 204, 2 => 0]

Photo by Kelli Tungay on Unsplash