40
votes

I'm trying to find a method for determining whether to use black or white text, given a background color (as a hex value). Has anyone dealt with this before? Is there an effective way to do this?

In my case, I would be using PHP to implement the logic (though any experience with this in other languages is welcome).

7

7 Answers

37
votes

Take a look at this page: Calculating Color Contrast with PHP

Keep in mind that if black and white are your only choices you're bound to have cases where neither of them works particularly great.

32
votes

Luminosity Contrast algorithm

I think the best way is the Luminosity Contrast algorithm:

function getContrastColor($hexColor) 
{

        // hexColor RGB
        $R1 = hexdec(substr($hexColor, 1, 2));
        $G1 = hexdec(substr($hexColor, 3, 2));
        $B1 = hexdec(substr($hexColor, 5, 2));

        // Black RGB
        $blackColor = "#000000";
        $R2BlackColor = hexdec(substr($blackColor, 1, 2));
        $G2BlackColor = hexdec(substr($blackColor, 3, 2));
        $B2BlackColor = hexdec(substr($blackColor, 5, 2));

         // Calc contrast ratio
         $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($R2BlackColor / 255, 2.2) +
              0.7152 * pow($G2BlackColor / 255, 2.2) +
              0.0722 * pow($B2BlackColor / 255, 2.2);

        $contrastRatio = 0;
        if ($L1 > $L2) {
            $contrastRatio = (int)(($L1 + 0.05) / ($L2 + 0.05));
        } else {
            $contrastRatio = (int)(($L2 + 0.05) / ($L1 + 0.05));
        }

        // If contrast is more than 5, return black color
        if ($contrastRatio > 5) {
            return '#000000';
        } else { 
            // if not, return white color.
            return '#FFFFFF';
        }
}

// Will return '#FFFFFF'
echo getContrastColor('#FF0000');

Some results:

enter image description here

NOTE: The font color is determined by the previous function. The number in brackets is the contrast ratio.



YIQ algorithm (less precise)

Another simpliest and less precise way called YIQ (because it converts the RGB color space into YIQ):

public function getContrastColor($hexcolor) 
{               
    $r = hexdec(substr($hexcolor, 1, 2));
    $g = hexdec(substr($hexcolor, 3, 2));
    $b = hexdec(substr($hexcolor, 5, 2));
    $yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
    return ($yiq >= 128) ? 'black' : 'white';
}                   
8
votes
  function getTextColour($hex){
    list($red, $green, $blue) = sscanf($hex, "#%02x%02x%02x");
    $luma = ($red + $green + $blue)/3;

    if ($luma < 128){
      $textcolour = "white";
    }else{
      $textcolour = "black";
    }
    return $textcolour;
  }
2
votes

Here is an algorithm that can be used to calculate a luminosity contrast ratio of your text:

http://juicystudio.com/services/aertcolourcontrast.php

You could use this formula with white and black values to calculate which gives you the higher ratio, and thus more readable text.

2
votes

You should take a look at the CSS Color library. It's implemented in PHP and does all the hard work for you.

1
votes

A simple but not perfect solution would be to sum the individual components (RGB) and the larger this value the 'lighter the color'. So for a high value you could use black as the foreground, and for a low value, use white.

You could then improve this method, making specific cases for greyscale colors (R = G = B), which, except for very dark grey, won't display white text well.

Edit: This of course means you need to know the format of RGB storage in your hex value, standard 24bpp storage is 0x00RRGGBB for the 8 hex digits.

0
votes

i would calculate the average value of rgb components and then decide whether to use black or white, e.g. white up to 0x66