Calculating Color Contrast with PHP

An essential step in web design is choosing the right colors. Not only should they “look good”, but they should also be easy on the eye. This is especially important if you want your visitor to read longer texts.

A good designer will choose high contrast colors for backgrounds and texts without hesitation. But sometimes no human is involved in choosing the colors. For example when colors are assigned in a (pseudo) random manner.

After some googling I found a page explaining different color contrast algorithms. I used the data from that page and implemented 3 color check functions in PHP.

Some short description and the source code for the functions is below. The impatient can just try it at my test page:

All 3 functions expect the RGB values (0-255) of 2 colors and will return a number. The higher the number, the better the contrast is. Depending on your use case you can then set up a threshold you do not want to drop below.


Color Difference

This is a very simple algorithm that works by summing up the differences between the three color components red, green and blue. A value higher than 500 is recommended for good readability.

function coldiff($R1,$G1,$B1,$R2,$G2,$B2){
    return max($R1,$R2) - min($R1,$R2) +
           max($G1,$G2) - min($G1,$G2) +
           max($B1,$B2) - min($B1,$B2);
}

Brightness Difference

This function is a bit more advanced as it tries to compare the brightness of the colors. A return value of more than 125 is recommended. Combining it with the color difference above might make sense.

function brghtdiff($R1,$G1,$B1,$R2,$G2,$B2){
    $BR1 = (299 * $R1 + 587 * $G1 + 114 * $B1) / 1000;
    $BR2 = (299 * $R2 + 587 * $G2 + 114 * $B2) / 1000;
 
    return abs($BR1-$BR2);
}

Luminosity Contrast

The third function is most complex but seems to work best. It uses the luminosity to calculate the difference between the given colors. The returned value should be bigger than 5 for best readability.

function lumdiff($R1,$G1,$B1,$R2,$G2,$B2){
    $L1 = 0.2126 * pow($R1/255, 2.2) +
          0.7152 * pow($G1/255, 2.2) +
          0.0722 * pow($B1/255, 2.2);
 
    $L2 = 0.2126 * pow($R2/255, 2.2) +
          0.7152 * pow($G2/255, 2.2) +
          0.0722 * pow($B2/255, 2.2);
 
    if($L1 > $L2){
        return ($L1+0.05) / ($L2+0.05);
    }else{
        return ($L2+0.05) / ($L1+0.05);
    }
}

Update: Pythagorean Distance

Manni suggested to use the Pythagorean Distance in the comments. I ported his code to PHP. However I found it hard to find a good threshold. Sometimes well contrasting colors return a very low value, sometimes their value is very high. I settled for a threshold of 250 in my script, but suggest to use the luminosity contrast function above instead.

function pythdiff($R1,$G1,$B1,$R2,$G2,$B2){
    $RD = $R1 - $R2;
    $GD = $G1 - $G2;
    $BD = $B1 - $B2;
 
    return  sqrt( $RD * $RD + $GD * $GD + $BD * $BD ) ;
}
Tags:
php,
programming,
color,
colour,
contrast
Similar posts:

 
Posted on Thursday September the 18th, 2008 (3 months ago).

Comments

1
What about the (to me) most obvious way: calculating the Pythagorean distance between two points in a three-dimensional color space?
2008-09-19 11:12:47
Manni
2
Manni, yeah what about it? ;-)

Let's assume for a minute that I have no idea about pythagorean distances and especially not how to apply it to colors... would you care to share some pseudo code?
2008-09-19 11:46:30
3
Oh. Sorry. I thought it was obvious.

You can think of any color as a coordinate in a cube: r,g,b. Now when you want to compare to colors, you simply calculate the distance between the two coordinates.

Here's a little Perl taken from POPFile:

sub compute_rgb_distance {
   my ( $left, $right ) = @_;
   $left =~ /^(..)(..)(..)$/;
   my ( $rl, $gl, $bl ) = ( hex($1), hex($2), hex($3) );

   $right =~ /^(..)(..)(..)$/;
   my ( $r, $g, $b ) = ( $rl - hex($1), $gl - hex($2), $bl - hex($3) );

   return int( sqrt( $r*$r + $g*$g + $b*$b ) );
}
2008-09-19 13:37:20
Mann
4
Thanks, I updated the code and article.
2008-09-19 13:58:24
5
I decided to make it a class file that also has a hex converter in it..

class colorTests
{
 public function hexColorToDec($color)
  {
   // Stole it from: http://www.anyexample.com/prog … _color.xml
   if($color[0] == '#')
    {
     $color = substr($color, 1);
    }

   if(strlen($color) == 6)
    {
     list($r, $g, $b) = array($color[0].$color[1],
                              $color[2].$color[3],
                              $color[4].$color[5]);
    }
   elseif (strlen($color) == 3)
    {
     list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
    }
   else
    {
     return false;
    }

   $r = hexdec($r);
   $g = hexdec($g);
   $b = hexdec($b);

   return array($r, $g, $b);
  }

 public function colorDiff($c1, $c2)
  {
   $c1 = $this->hexColorToDec($c1);
   $c2 = $this->hexColorToDec($c2);

   return max($c1[0],$c2[0]) - min($c1[0],$c2[0]) +
          max($c1[1],$c2[1]) - min($c1[1],$c2[1]) +
          max($c1[2],$c2[2]) - min($c1[2],$c2[2]);
  }

 public function brightDiff($c1, $c2)
  {
   $c1  = $this->hexColorToDec($c1);
   $c2  = $this->hexColorToDec($c2);
   $BR1 = (299 * $c1[0] + 587 * $c1[1] + 114 * $c1[3]) / 1000;
   $BR2 = (299 * $c2[0] + 587 * $c2[1] + 114 * $c2[3]) / 1000;

   return abs($BR1-$BR2);
  }

 public function lumDiff($c1, $c2)
  {
   $c1 = $this->hexColorToDec($c1);
   $c2 = $this->hexColorToDec($c2);
   $L1 = 0.2126 * pow($c1[0]/255, 2.2) +
         0.7152 * pow($c1[1]/255, 2.2) +
         0.0722 * pow($c1[2]/255, 2.2);

   $L2 = 0.2126 * pow($c2[0]/255, 2.2) +
         0.7152 * pow($c2[1]/255, 2.2) +
         0.0722 * pow($c2[2]/255, 2.2);

   if($L1 > $L2)
    {
     return ($L1+0.05) / ($L2+0.05);
    }
   else
    {
     return ($L2+0.05) / ($L1+0.05);
    }
  }

 public function pythDiff($c1, $c2)
  {
   $c1 = $this->hexColorToDec($c1);
   $c2 = $this->hexColorToDec($c2);
   $RD = $c1[0] - $c2[0];
   $GD = $c1[1] - $c2[1];
   $BD = $c1[2] - $c2[2];

   return sqrt($RD * $RD + $GD * $GD + $BD * $BD) ;
  }
}
2008-09-19 19:56:00
EllisGL
CAPTCHA

No HTML allowed. URLs will be linked with nofollow attribute. Whitespace is preserved.

 
 

Blog

Older Weblog articles are available in the Archive, subscribe to the
Full Content RSS Feed
to stay tuned. (learn more)

Subscribe to the Feed

Recent Blog Entries

 

This is the personal web site of Andreas Gohr - human being, blogger and web geek from Berlin, Germany.

This page was last updated at 2008/09/19 14:00.
Imprint/Impressum

Advertising:

Advertise Here
advertise here

Recent readers: