# How to calculate the colour a human eye sees when looking at a light spectrum?

I have to do a presentation about colourants in Chemistry class (grade 12, advanced) and want to write a program that calculates and visualizes the colours of some simple molecules.

What I need is a formula to calculate the visible colour (e.g. RGB-values of the colour that would look the same like the given spectrum).

Should be able to handle both monochromatic spectral lines and wide spectres. (e.g. with integrals or something like that?)

• It is not guaranteed that there is a monochromatic light source that would duplicate whatever color you would see. Notice that there is no brown in the rainbow. Mar 7, 2015 at 17:42
• @SirElderberry Ouch. You're right. How can I then calculate the resulting color of a spectrum? You're right that it is not possible with wavelengths, but what about RGB values? Mar 7, 2015 at 17:48
• Actually, one time I got interested in working out the color of the sky and I wrote a Mathematica notebook that did exactly this. It's kind of hand-wavy and I wouldn't claim that it's VERY rigorous, but maybe it'd work for you? Here's the blog post; at the bottom is a link to my Dropbox hosting the notebook I wrote. waywardcuriosity.blogspot.com/2013/01/sky-entific-method.html Mar 7, 2015 at 17:55
• Ehm, thanks, but that is not really what I am looking for... Or I am too dumb to read the right parts. I can calculate some wavelengths the spectrum of a molecule will have and now need something to know how the result will look to the human eye. Mar 7, 2015 at 18:11
• Oh, most of the post isn't relevant, but at one point I have a spectrum for the sky and I turn it into an RGB color value. Ctrl-f "color vision" and see if the next few paragraphs sound helpful. Mar 7, 2015 at 18:14

Trying to break my bad habit of answering in comments, putting my previous stuff down here now. As I said, I'm largely drawing on a blog post I did a couple years ago, working out the color of the sky. I had a spectrum, and I wanted a color. As I mentioned above, you can't just take a spectrum and output an equivalent wavelength--only some colors are so called "spectral" colors. These are the ones in the rainbow.

To get a color from a spectrum, we need to know how the eyes respond to the incoming light. You have three kinds of cells, as you know, with different regions of sensitivity. Let's say we have a function $f_i(\omega)$ that characterizes the response to a frequency $\omega$, with 1 being the maximum and 0 being the minimum. The spectrum intensity is given by $I(\omega)$. Then we can talk about the total response: $$R_i = \frac{\int_0^\infty \mathrm{d} \omega \ f_i(\omega) I(\omega)}{\int_0^\infty \mathrm{d} \omega \ f_i(\omega)}$$ I've normalized it such that a flat spectrum gives you 1. You can see here that there are two ways to get a large response: either have some intensity at a place where the response $f$ is strong, or a lot of intensity in a place where it isn't. Now, the subscript is because there will be three of these: $R_1, R_2, R_3$, one for each cell. You can now take those responses (which are roughly corresponding to red, blue, and green, but not really) and run it through a matrix that transforms it into the computer-image RGB basis. Now, the computer defines colors there's actually a fourth, brightness parameter. To get this from a spectrum you'd need to know the actual measured intensity as well as a lot more about how the eye responds, but with the RGB value you can get a range of colors that corresponds to what the spectrum might look like.

Now, you specifically asked about how one would get this from a series of spectral lines. The answer here is that the spectrum is zero in most places, but very strong in others. In this case the mathematical structure you want would be called Dirac delta functions, but we can be a little simpler. Basically, the integral will now become a sum over all the discrete spectral lines: $$R_i = \frac{\sum_{j} \ f_i(\omega_j) I(\omega_j)}{\int_0^\infty \mathrm{d} \omega \ f_i(\omega)}$$ Here we're now summing over every line that's present (labeled $j$), and just getting that one value out of what was previously the integral, but still normalized the same way. You could now use the intensity function to adjust the strength of the various spectral lines. Once you have the response values you proceed exactly as in the continuous case.

• This is far too complicated, since 1931 the CIE published Color Matching Functions to straightforwardly convert a spectrum to RGB values! Jan 23, 2017 at 15:33
• @adrienlucca.wordpress.com zeldredge describes how this is done: CMFs multiplied by the spectrum, then integrated to get some kind of tristimulus values, then convert these to RGB. Don't you say the same in your answer? Jan 23, 2017 at 17:50
• @doetoe Practically speaking we don't need integral terms. This is overkill, that's all I meant. Also it makes it unreadable for people who do not read math. Jan 23, 2017 at 19:00

You will not be able to get the proper color of the narrow bands because they fall outside of the RGB gamut. However you can get their correct hue.

You need color matching functions (usually we use 1931 2° CMFs from the CIE, check online... or here under CMFs category http://www.cvrl.org/)

CMFs multiplied by your spectrum, then summed, will give you the proprtions of primaries X, Y, Z that match the color for a human observer.

Once you have this, you can scale your results and convert from XYZ to sRGB for example (or any other RGB system you'd like), the conversion is explained there:

To properly scale your XYZ values, I advice you to get a "white reading" using only solvent for your spectrophotometer. The Y value of this reading will be your white reference for scaling the other XYZ values.

In order to get good hues for the narrow bands falling outside sRGB gamut, you can convert to CIE LCH and reduce C* until you fall inside RGB gamut.

See here: XYZ2LAB >> LAB2LCH

XYZ 2 LCH