Calculating the Perceived Brightness of a Color

I needed a way to test if a background color is light or dark in order to choose an appropriate text color (black on light colors and white on dark colors), you can find yourself in the same problem if you try to convert an image to grayscale.

I found many approaches that didn’t work well, I’ll describe them and the problems I discovered below – but first – the successful solution.

I finally found a solution that actually works on this web page the formula is:

brightness  =  sqrt( .241 R2 + .691 G2 + .068 B2 )

Or, in C# using the WPF's Color structure System.Windows.Media.Color (the same exact code should also work with the System.Drawing.Color structure used in WinForms and WebForms):

private static int Brightness(Color c)
   return (int)Math.Sqrt(
      c.R * c.R * .241 + 
      c.G * c.G * .691 + 
      c.B * c.B * .068);

This method will return a number in the rage of 0 (black) to 255 (White) and to set the foreground color based on the Brightness method:

Color textColor = Brightness(backgroundColor) < 130 ? Colors.White : Colors.Black;

I selected cutoff value of 130 by trial and error and it reflects my taste, every value in the rage 128-145 will give acceptable results.

Here is a table of all the named colors in .net 3.0, each with its name in readable text on it based on the code above, the number in parenthesis is the brightness – click on the image to view it in full size (in a new browser window).

Usually, when talking about color theory the range 0-1 is used for each component – so (0,0,0) is black, (1,1,1) is white and (0.5,0.5,0.5) is 50% gray, I’m not using this range, I’m using the 0-255 range used in most programming environments.

Why Not HSL or HSV?

You may think that the luminance (or lightness) component of the HSL color system or the value component of HSV will solve this problem, I did, but I was wrong.

The L component of the HSL and the V component of HSV describe the brightness of a color relative to a base color, if this base color is blue you will get a darker color than if this base color is yellow, HSL and HSV are very useful if you need to create a lighter or darker version of a color but aren’t very useful if you want to know how bright a color is.

The Naïve RGB Algorithm

If RGB 0,0,0 is black and RGB 255,255,255 is white then R+B+G should give us a good approximation of the color brightness, shouldn’t it?

The problem is that some colors are brighter than others, red is brighter then blue and green is brighter then red, maybe we just need to find the right coefficients for them?

The W3C Algorithm

The W3C working draft on accessibility has a formula for the perceived brightness of a color (based on the YIQ color system):

((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000

This formula and references to it dominate the search results, probably because the W3C has high search engine rank.

This is better than using HSL, but it tends to give wrong results for many colors, especially shades of yellow.

RGB Color Difference

The W3C also has an algorithm to calculate the difference between colors:

(maximum (Red value 1, Red value 2) - minimum (Red value 1, Red value 2)) + (maximum (Green value 1, Green value 2) - minimum (Green value 1, Green value 2)) + (maximum (Blue value 1, Blue value 2) - minimum (Blue value 1, Blue value 2))

Or, in shorter form:

dR + dG + dB

Where dR, dG and dB are the difference in the Red, Green and Blue component.

If you calculate difference from white and difference from black – and compare them (if difference from black>difference from white then the color is dark) you actually get better results than using the brightness formula, but then you get wrong results for light greens and dark blues (probably because this formula doesn’t take into account the brightness difference of the colors).

3D Distance in RGB Space

You can think of the RGB color space as a cube where each of the 3 colors are axis, in one corner you have black (RGB 0,0,0) and in the opposite corner you have white RGB (255,255,255), so if a color is closer to black it should be darker.

The formula for 3D distance is:


Where dx, dy and dz are the difference on the x, y and z axis.

This algorithm also gives mixed results because it doesn’t take into account the fact that some colors look brighter than others -  and that’s gets us back to …

Weighted Distance in 3D RGB Space (or the HSP algorithm)

If you scroll up to the beginning of this post and look at the brightness formula there you will see it’s just like the 3D distance formula except it gives different weight to each axis.

So now we finished our tour to color brightness land we have a good formula for calculating perceived brightness and (if my explanations where clear) we can actually understand why it works.

posted @ Sunday, April 27, 2008 6:30 PM

Comments on this entry:

# re: Calculating the Perceived Brightness of a Color

Left by Atiyab at 4/29/2008 2:55 PM

This has been an awesome help to me... was on a whimsical quest at work to make the font color selection (black or white) automated. And what luck that you posted this just two days back.. cant believe my luck really..

Think I made a small improvement on your method which I wanted to share.. the formula still doesnt work for some colours like HSL(0,240,30) for example. Thus I think its more effective if you have the following algo:
If Abs(Luminosity - 120) < 75 Then
If CBrightness(backColor) >= 130 Then
txtColor = vbBlack
txtColor = vbWhite
End If
ElseIf Luminosity > 120 Then
txtColor = vbBlack
txtColor = vbWhite
End If

Thanks again for all the info..


# re: Calculating the Perceived Brightness of a Color

Left by Giga at 1/2/2009 12:11 AM

Thanks for your help, I used this!

# re: Calculating the Perceived Brightness of a Color

Left by f at 1/23/2009 1:09 PM

Will use this in a flash project.
Many thanks for the well written info.

# re: Calculating the Perceived Brightness of a Color

Left by Alan at 2/13/2009 2:05 AM

Here's the function in javascript;
Brightness = function(color) {
var R = parseInt(color.substring(0,2),16);
var G = parseInt(color.substring(2,4),16);
var B = parseInt(color.substring(4,6),16);
return Math.sqrt(R * R * .241 + G * G * .691 + B * B * .068);
var sColorText = Brightness(sColor) < 130 ? '#FFFFFF' : '#000000';

# re: Calculating the Perceived Brightness of a Color

Left by pavan at 3/30/2009 1:10 PM

this is wrong

# re: Calculating the Perceived Brightness of a Color

Left by ivpotter at 5/28/2009 2:04 PM

Well done pavan, for one of the most useless comments I've ever seen!

# re: Calculating the Perceived Brightness of a Color

Left by reza at 6/16/2009 9:09 AM

very good.thank you

# re: Calculating the Perceived Brightness of a Color

Left by Zbyszek at 7/8/2009 8:49 PM

Nice text, it was very helpful.

But there is one issue when working with colors - for example on my laptop LCD's many colors are wrong. And it differs pretty much when changing the angle of view.

For example "Blue" is pretty badly positioned when I look normally, but it's good when vieving from the 60 degrees angle to the top. But there are some red colors that are good positioned when looking normally, but bad when I look from some angle.

# re: Calculating the Perceived Brightness of a Color

Left by Joe Taylor at 5/26/2010 10:32 PM

Nice one! This was just the calculation I needed to correct my own auto white text/black text script.

# re: Calculating the Perceived Brightness of a Color

Left by Poncho at 6/23/2010 9:01 PM

Thanx man!
that helped me a lot ;)

# re: Calculating the Perceived Brightness of a Color

Left by Rich at 11/4/2010 12:42 AM

Thanks...very helpful. I was using a helper method in Java - Color.RGBToHSV() - but doing this manually saves me a TON of precious CPU cycles on large images

# re: Calculating the Perceived Brightness of a Color

Left by Guilherme Campos Hazan at 1/13/2011 2:58 PM


I use this much faster formula that gives me values almost identical to yours:

((r << 5) + (g << 6) + (b << 2)) / 100

# re: Calculating the Perceived Brightness of a Color

Left by Tel Smith at 3/25/2011 5:02 PM

Guilherme Campos Hazan offers the formula ((r<<5)+(g<<6)+(b<<2))/100 and suggests it gives almost identical values to the SQRT model on this page. It doesn't for two reasons. Firstly, it seriously underweights B. R-0,G-0,B255 returns values of 10 and 66 respectively. Secondly, it suffers from the same problem as other linear models such as the W3C algorithm (also on this page) that there is no allowance made for the fact that brightness not only depends on the values of the individual three colors but also the proportion in which they are mixed. For example, contrary to what might be expected, R-240,G-0,B-0 is substantially brighter than R-80,G-80,B-80. However, W3C returns values of 72 and 80 respectively. Compare this with SQRT values of 118 and 80 respectively. This model is considerably more accurate and may be compared to the Colour Contrast Analyser which can be found on the visionaustralia website. In short, although W3C is widely used to determine brightness it is significantly flawed.

# re: Calculating the Perceived Brightness of a Color

Left by Rick at 6/3/2011 6:01 AM

You are an absolute genius.
This algorithm is far superior
to every other method of determining
the true brightness of a color.

# re: Calculating the Perceived Brightness of a Color

Left by Nicholas J Ingrassellino at 11/18/2011 2:48 AM

Very useful indeed. Here is a PHP example:
$rgb = imagecolorat($image, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
return sqrt((pow($r, 2) * .241) + (pow($g, 2) * .691) + (pow($b, 2) * .068));

Obviously you will need to add your own values for $x and $y after creating and loading an image.

# re: Calculating the Perceived Brightness of a Color

Left by khan at 3/8/2012 12:13 PM

awsome...jus awsome..the phrase "perfect" is made for these kind of answer...!!

Thanks a lot.

# re: Calculating the Perceived Brightness of a Color

Left by Harvey at 3/9/2012 5:36 AM

The author of the web page linked at the top of this page now claims the following constants produce a more accurate result: 0.299, 0.587, and 0.114.

# re: Calculating the Perceived Brightness of a Color

Left by Devin at 7/24/2012 1:02 AM

Thanks so much for this! I had to write a Java class that rendered cells in a list of colors based on the value of each color. In order to make the text stand out from the background, I originally tried making it the inverse color, but that looked ugly and didn't work for the more greyish colors. I found this method and now it works perfectly.

# re: Calculating the Perceived Brightness of a Color

Left by Fiona at 11/14/2013 1:19 PM

Thanks so much, this is exactly what I wanted, a way of working out whether to use black or white text

# re: Calculating the Perceived Brightness of a Color

Left by Dave Collier at 12/16/2013 7:21 PM

The formula gives a better result than the unsatisfactory W3C suggestion, as other commenters have pointed out, and will work well for determining whether the text should be black or white. But it just isn't quite there for determining relative brightness, as I explain on my web pages

# re: Calculating the Perceived Brightness of a Color

Left by dk at 3/24/2014 8:04 AM

Thank you man.

# re: Calculating the Perceived Brightness of a Color

Left by Michel Rouzic at 5/10/2015 4:56 PM

I find it funny that you would do brightness = sqrt( .241 R2 + .691 G2 + .068 B2 ) without any stated reason why. The squaring is actually a very crude way to approximate a linear value. See you're not actually supposed to do math on pixel values in gamma compressed format (like the familiar 256-valued sRGB format you use), you have to make it linear first, do the math, then gamma compress it again.

You do the conversion using the formulas here but it approximates to a gamma of 2.2, which you yourself approximated with a gamma of 2.0 (the square in your formula). So you'd be better off using the proper gamma correction formula.

As for the weights I was just experimenting myself and I'm finding that 0.16 0.73 0.11 are the best weights I can find.

Your comment:

 (will not be displayed)

Please add 6 and 7 and type the answer here: