I created a program to convert the temperature (in Kelvin) of a blackbody to RGB color. However, it is slightly inaccurate, and the deviations increase for values greater than 10000K and less than 1500K. Is there an accurate mathematical function that generates RGB values for given temperature, ranging from ~500K to 20000+ K?

  • 2
    $\begingroup$ How do you know that is inaccurate? What result do you get, and what are you comparing it to? Have a look at this answer for how to transform a spectrum first to " CIE XYZ color space, then transform to linear RGB and then sRGB". Color is hard, so good luck! $\endgroup$
    – uhoh
    Commented Nov 23, 2020 at 1:44
  • 3
    $\begingroup$ I made such a program a few years ago. You're welcome to steal what you want from it; it's on GitHub :) $\endgroup$
    – pela
    Commented Nov 23, 2020 at 13:53
  • 1
    $\begingroup$ @pela You can use that in an answer, if you'd like. $\endgroup$
    – WarpPrime
    Commented Nov 23, 2020 at 15:18
  • 1
    $\begingroup$ fasterthanlight: There are several different ways to calculate perceived colors, and I don't know enough about this to be confident in writing up an answer. Also, it's a while since I wrote the code, so I would have to read up on the theory behind my code. To quote the famous @uhoh: "Color is hard, so good luck!" $\endgroup$
    – pela
    Commented Nov 24, 2020 at 9:52
  • 1
    $\begingroup$ @uhoh Looks interesting! I also found a freely available book here that I wish I had the time to read. $\endgroup$
    – pela
    Commented Nov 25, 2020 at 11:13

1 Answer 1


Ok, here's my take on calculating the color of a blackbody, or any spectrum in fact:

Disclaimer: I'm not a color theorist, and there may be more accurate methods. But the result, shown in the bottom, looks about right.


First note that since color is a function of the relative intensity in various wavelength bands, it doesn't matter whether we express our spectrum in flux, flux density, intensity, luminance, or something else that only differs by constant values.

Let's consider a spectrum $S(\lambda)$ as a function of wavelength $\lambda$. If you're interested in a Planck spectrum, first calculate this from the temperature $T$:

$$ S(\lambda) = B(\lambda,T) = \frac{2hc^2}{\lambda^5} \frac{1}{e^{hc/\lambda k_\mathrm{B}T} - 1}, $$ where $h$, $c$, and $k_\mathrm{B}$ are Planck's constant, the speed of light, and Boltzmann's constant, respectively.

CIE 1931 color space

The CIE 1931 color space was the first attempt to quantify the relation between the distribution of wavelengths and the color perceived by humans. It makes use of the so-called color matching functions $\bar{x}$, $\bar{y}$, and $\bar{z}$, which are functions of $\lambda$, seen here:

xyzbar Credit: Wikipedia

You can find tabulated values of these functions on the internet.

The color (which is 3D because humans have three different cones) of any spectrum can be mapped to a 2D position $\{x,y\}$ in the CIE 1931 color space chromaticity diagram seen here:

chromdia Credit: Wikipedia

Tristimulus values

The pair $\{x,y\}$ are normalized versions of the tristimulus values $\{X,Y,Z\}$ defined by $$ X = \int S \, \bar{x} \, d\lambda\\ Y = \int S \, \bar{y} \, d\lambda\\ Z = \int S \, \bar{z} \, d\lambda. $$

(Actually they're defined using the spectral radiance), but this is equal to the intensity to within a constant factor.)

The lower-case $\{x,y\}$ are then given by: $$ \begin{array}{rcl} x & = & \frac{X}{X+Y+Z}\\ y & = & \frac{Y}{X+Y+Z}\\ z & = & \frac{Z}{X+Y+Z} = 1-x-y, \end{array} $$ and the last of these relations is the reason only two values are needed.


To go from $\{x,y\}$ or $\{X,Y,Z\}$ to RGB is a linear transformation which can be conducted by matrix multiplication. There are many variations here; my understanding is that is has to do with whatever device you're using to display you colors, but there might be more to it.

The sRGB color space assumes that the $\{X,Y,Z\}$ set has been scaled to $\{0.9505, 1, 1.0890\}$ for the CIE illuminant D65 ("white"); that is, $Y\equiv 1$.

With the above values, this new set can be calculated as $$ \begin{array}{rcl} Y & \rightarrow & 1\\ X & \rightarrow & x/y\\ Z & \rightarrow & z/y, \end{array} $$ and the RGB values are then, using the conversion matrix from here which cites this, $$ \left[ \begin{array}{l} R\\ G\\ B \end{array} \right] = \left[ \begin{array}{rrr} 1.656492 & -0.354851 & -0.255038 \\ -0.707196 & 1.655397 & 0.036152 \\ 0.051713 & -0.121364 & 1.011530 \end{array} \right] \left[ \begin{array}{l} X\\ Y\\ Z \end{array} \right] $$

Gamma correction

I'm not 100% sure, but I think this set of RGB values needs to be gamma-corrected, which is a non-linear correction that account for the fact the the device displaying the colors shows different relative luminances than the human eye perceives. At least my result, shown in the bottom, looks about right, whereas it doesn't if I don't perform this correction.

The gamma correction is given by the function $$ \gamma(u) = \left\{\begin{array}{ll} 12.92 u & \mathrm{if\,} u \leq 0.0031308 \\ 1.055 u^{1/2.4} - 0.055 & \mathrm{otherwise} \end{array}\right. $$

and the perceived RGB values should then be $$ \begin{array}{rcl} R & \rightarrow & R\times\gamma(R)\\ G & \rightarrow & G\times\gamma(G)\\ B & \rightarrow & B\times\gamma(B) \end{array} $$

These values are mostly in the range $[0,1]$, but if they fall outside, they're usually clipped. If you want your values to be in the range $[0,255]$ you just multiply them by 255.

Result: the color of the Universe through time

I implemented the above algorithm in Python, as a part of a larger code called timeline that calculates various properties of the Universe as a function of time after the Big Bang. You can find it here on GitHub.

With that code I calculated the color of the Universe as a function of time after the Big Bang, and hence as a function of temperature. Until star formation kicks in around 200 million years after the Big Bang, the color is given by the temperature of the cosmic microwave background.

When stars arrive, the average cosmic spectrum becomes more complicated and can't be calculated (yet) from first principles, but must be observed. I haven't found such observations at early times, but Baldry et al (2002) presents the spectrum at various times after an age of 11 billion years, at which time the Universe is a dull "cosmic latte". So in this figure, I grayed out the color between $t=200\,\mathrm{Myr}$ and $t=11\,\mathrm{Gyr}$:


But please note the disclaimer in the top of this answer :)


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .