Thermal Emission

All objects with a temperature greater than 0 Kelvin, and which are not perfectly reflecting, emit electromagnetic radiation. This emission of electromagnetic radiation by high temperature is called thermal emission, or incandescence.

For relatively low temperatures, say under 1500 Kelvin (≈ 1200ºC), this radiation is not directly visible; you might see the object, but only because it’s illuminated by ambient light. In the pitch dark, you wouldn’t see it all.

This all changes when an object’s temperature increases, in a pitch-dark environment. You can do this experiment if you have an old-fashioned electrical burner (the one with the metal spiral on which you put your pot), and switch it on in a completely dark room, preferably at night, and make sure you are completely dark adapted. When the temperature increases you will see initially a deep-dark red glow, which will get brighter when it heats up and changes its color too from a deep red to an orange-yellowish color later.

The emission of an object increases with the fourth power of its (absolute) temperature: $$ B(T) = \epsilon\sigma\mathrm T^4.$$ In physics, this is called the Stefan-Boltzmann law, and \(\epsilon\) is an object’s emissivity, which is 1.0 for a black object, and \(\sigma\) Stefan-Boltzmann constant [\(5.670374419\times 10^{-8}\ \mathrm{W\ m^{-2}\ K^{-4}}\)].

The color change can be characterized by the wavelength of maximum spectral emission, and this can be calculated using Wiens displacement law: $$ \lambda_{\mathrm{peak}} = \frac{b}{T},$$ with \(b\) Wien’s displacement constant [\(2.897771955\times 10^{-3} \mathrm m\ \mathrm K\)], and \(T\) the absolute temperature, in units of Kelvin, of blackbody thermal emission. Due to this spectral shift, the luminance of a hot object, which is the radiance of an object we can see, increases even faster, as the spectral emission moves to the visible part of the spectrum.

The spectral emission of hot objects, as a function of temperature, was measured in the late 1800s, and the German physicist Max Planck came up with the right model to describe it in 1900. This model is called Planck’s law, and predicts the spectral radiance \(B_\lambda\) [\(\mathrm W \mathrm{sr}^{-1} \mathrm m^{-2}\)] as a function of wavelength [\(\mathrm m\)] and temperature [\(\mathrm K\)] as: \[ B_\lambda(\lambda, T) =\frac{2hc^2}{\lambda^5}\frac 1{ \exp\left(\frac{hc}{\lambda k_\mathrm B T}\right) - 1}, \] with \(h\) Planck’s constant [\(6.62607015×10^{−34}\ \mathrm J\thinspace\mathrm{Hz}^{-1}\)] , \(c\) the speed of light [\(\mathrm m\thinspace \mathrm s^{-1}\)], and \(k_B\) Boltzmann constant [\(1.380649 × 10^{23}\ \mathrm m^2 \mathrm{kg } \thinspace\mathrm s^{-2} \mathrm K^{-1}\)].

Why is it called Blackbody emission?
Planck’s law predicts the thermal emissions spectrum for a perfectly black object or a blackbody, as it is referred to in Physics.

If an object is not black, it will reflect ambient radiation in addition to emitting thermal radiation. This makes their thermal emission very hard to measure, as even in the pitch-dark, all the objects around us will be at room temperature, and emit already a lot of radiation, which will be reflected by non-black objects. To eliminate the reflection of an object, we would need to measure it in a very cold room, or in a room with only perfectly white reflecting or silvery objects, to eliminate reflected radiation. In practice, it is easier to create a completely black object, which is typically done by making an almost completely enclosed oven, coated with black ceramic tiles on the inside, and with a small opening through which the black-body radiation is emitted and measured.

The reflectivity of objects, or its complement emissivity, is typically material-dependent and varies with wavelength and temperature; there are no general mathematical models for predicting the reflectivity of materials, similar to Planck’s law, and it has to be measured.

Sometimes people use the term Planckian emission, instead of Blackbody emission. This name refers to thermal emission’s model, and not its physics phenomenon, and would be similar to referring to gravity as “Newtonian attraction”. If a particular subject or item directly refers to Planck’s law, it may make sense to refer to it as “Planckian”. An example would be the line in a chromaticity diagram of coordinates of light emitted by a black object; such a line is often called Planckian Locus and refers to the model used to calculate the line, not the physics phenomenon.

How hot can objects get?
Most materials will burn or melt before they start to emit light. Tungsten is a metal with the highest melting point we know and is a good material for generating thermal emissions. A tungsten filament can emit bright white light when placed in a lightbulb, depleted from any oxygen. It can be heated by running a current through it.

For non-metal objects, or plasma and gasses, temperatures and Radiant Emittance can become so high that they can damage the eyes. The emission of a welding arc, or the emission of our Sun, are examples of this, which both require very dark protective screens when looking at them.

Blackbody Illuminant

The library’s Blackbody illuminant class implements the equations given above. Peak wavelengths and CIE 1931 chromaticity coordinates of a blackbody thermal emitter, for example, in the range from 2750 to 6500K in 250K steps, can be calculated using the following:

    // load CIE 1931 standard observer
    const c31 = await cie.fetchCIE1931();

    const output: string[] = [];
    for (let t=2750.0; t<=6500.0; t+=250.0) {
        const bb = new cie.Blackbody(t);
        const peak = bb.peakWavelength * 1.0E9;
        const [_, x, y] = bb.lxy(c31);
    // check first two lines
    const want = [
    for (const [i,w] of want.entries()) {
        // remove all white space in output before comparison
        assert.assertEquals(output[i].replace(/\s+/g,''), w);

Its output is formatted as rows of an HTML table and is included in the table below.

CIE 1931 xCIE 1931 y

Irradiance and Luminous Efficacy of Thermal Radiation

The total power for blackbody thermal emission of an object is given by Stefan-Boltzmann law and is used in the Blackbody class. It is used to calculate the radiant emittance and irradiance of a blackbody radiator. In this library, where Blackbody is used as an illuminant, irradiance is set to 1 Watt per square meter by default. It can be set by direct assignment; in this example, we set it to 100 Watt per square meter:

    // create a blackbody illuminant, with a temperature of 3000K
    const bb = new cie.Blackbody(3500.0);

    // set irradiance to 100W per square meter
    bb.irradiance  = 100.0;

Blackbody radiation has a very broad emission spectrum, ranging far in the infrared: to calculate the total power in this example, the wavelength domain is set from 200 nanometers to 50 micrometers, to capture the output completely:

    // get spectral irradiance distribution over a wavelength range
    // from 200 nanometer to 50 micrometer, with 1000 points
    const domain = new cie.Domain(200E-9, 50E-6, 1000);
    const sid = bb.illuminate(domain);

    // Confirm irradiance in this spectrum to be 100.0
    // by integration with Trapezoidal rule
    const calcIrr= cie.integrate(sid, domain.step());
    assert.assertAlmostEquals(calcIrr, 100.00, 5E-3);

Adding the following lines to the previous script reveals that the total power of a blackbody radiator with a color temperature of 3500 Kelvin in the visible part of the spectrum — here represented by a wavelength domain from 380 to 780 nanometers with 1-nanometer steps — is only 20.69%.

    // Confirm irradiance in the visible part of the spectrum to be 20.69W/m2,
    // or 20.69%, as we have set the irradiance to 100W/m2.
    const domainVis = new cie.Domain(380E-9, 780E-9, 401);
    const sidVis = bb.illuminate(domainVis);
    const powVis = cie.integrate(sidVis, domainVis.step());

    assert.assertAlmostEquals(powVis, 20.69, 5E-3);

Here is another example, where we calculate the Luminous Equivalent of Radiation, or LER, for blackbody emission, for a range from 2000 to 3500 Kelvin, in steps of 250 Kelvin:

    const c31 = await cie.fetchCIE1931();

    const output:string[] = [];
    for (let t = 2000.0; t<=3500.0; t+=250.0) {
        const bb = new cie.Blackbody(t);
        const lm = bb.illuminance(c31);

    const want = [
    for (const [i, w] of want.entries()) {
        assert.assertEquals(output[i], w);

The output of this script shows that the Luminous Efficacy of Radiation for thermal emission is strongly dependent on temperature:

2000K 1.6lm/W
2250K 4.0lm/W
2500K 8.0lm/W
2750K 13.6lm/W
3000K 20.7lm/W
3250K 28.8lm/W
3500K 37.4lm/W

Used as lamps, intended to produce light — defined as electromagnetic radiation we can see — blackbody emitters are not very efficient. Most of the power of an incandescent lamp is emitted as infrared radiation, and not as light. LED and fluorescent lamps, optimized to emit light only and render color with high fidelity, achieve LER values in the range from 300 to 350 lumens per watt; see for more on color rendering in the sections on Color Rendering Index (CRI) and Color Fidelity Index (CFI).

The Luminous Efficacy of Radiation (LER) is different from the Luminous Efficacy of a light source or lighting system (LES). Luminous Efficacy of Radiation is the luminous flux in a beam of light divided by its radiant power, while Luminous Efficacy is the total luminous flux emitted by a source or a lighting system, divided by its power input. To use an example of the difference: you can calculate the Luminous Efficacy of Radiation of sunlight using its spectral distribution, but it is impossible to calculate its Luminous Efficacy, as long as you don’t know the Sun’s total luminous flux output and find a way to calculate its power input.

Second Radiative Constant C2

Planck’s law can also be written using the radiative constants c1 and c2, in this example describing spectral radiant exitance \(M\): $$M(\lambda,T) =\frac{c_{1}}{\lambda^5}\frac{1}{\exp\left(\frac{c_2}{\lambda T}\right)-1}$$ with the first radiative constant defined as: $$c_1 = 2\pi h c^2,$$ and the second radiative constant defined as: $$c_2 = \frac{h c}{k_B},$$ and with \(h\) Planck’s constant, \(c\) the speed of light, and \(k_B\) Boltzmann’s constant. With changes in the definition of fundamental constants over time, the radiative constants changed a bit. Changes in the first constant changed the output as described by Planck’s law which in general is often not a critical parameter. However, changes in radiative constant c2 changed the international temperature scale too, and definitions of standard illuminants in colorimetry.

For example, the standard CIE illuminant D65, intended to represent daylight with a correlated color temperature of 6500 K, and specified by the CIE as a table of spectral values, was calculated in 1967 with a value of \(c_2\) of $$ c_2=1.438 0 \times 10^{-2} \text{m·K}.$$ The current value, as of 2020, is: $$ c_2=1.438 776 877… \times 10^{-2} \text{m·K}.$$ The effect of this redefinition is that the international temperature scale has changed. For example, instead of using a value of 6500 K as a correlated color temperature for the D65 illuminant, a value of $$ T = \frac{c_2}{c_{2,\text{1948}}}\times 6500.0 = 6503.51 $$ has to be used to calculate the spectral distribution values of the D65 illuminant, which is still the recommended illuminant used in many standards.

The temperature scale change also affected illuminants D50, D55, and D75. The new values for the Dxx-illuminants can be calculated like this:

    const c2 = cie.c2Value(cie.PlanckC2.Exact);
    const c2_48 = cie.c2Value(cie.PlanckC2.Ipts1948);
    const want = [

    for (const [i,t_prev] of [5000.0, 5500.0, 6500.0, 7500.0].entries()) {
        const t_now = c2/c2_48 * t_prev;
        const got = `<tr>
        // remove white space in string
        assert.assertEquals(got.replace(/\s+/g,""), want[i]);

The output here is are rows of data, which are included in the following table:


The radiative constant c2 can also be directly set in a Blackbody instance, using the PlanckC2 enum type:

  • PlanckC2.Exact, the current and exact values defined by SI base units,
  • PlanckC2.Nbs1931, set by the US National Bureau of Standards in 1931, and used to calculate the spectral distribution of the A illuminant,
  • PlanckC2.Ipts1948, value as used in the International Practical Temperature Scale in 1948, and used in the definition of the Dxx-illuminants,
  • PlanckC2.Its1968, value as set in the Internation Temperature Scale in 1968.

And here is a, somewhat contrived example, in which we calculate the chromaticity coordinates of CIE standard A Illuminant, which had at its time of definition, a color temperature of 2848 Kelvin. The standard A illuminant’s spectrum is calculated using Planck’s law, using the CIE 1931 NBS value for c2. In this example, we confirm that the chromaticity coordinates of the A illuminant calculated this way, are the same as those obtained with the current c2 definition, but using a temperature of 2856K.

    const aNbs = new cie.Blackbody(2848.0);
    aNbs.c2 = cie.PlanckC2.Nbs1931;

    const c31 = await cie.fetchCIE1931();

    // A-Illuminant CIE 1931 Chromaticity values (CIE.14.2004)
    const want = "[0.447576,0.407448]";

    // A-Illuminant at 2848.0K using NBS 1931 temperature scale
    const [_l1, x1, y1] = aNbs.lxy(c31);
    const got =`[${x1.toFixed(6)},${y1.toFixed(6)}]`;
    assert.assertEquals(got, want);

    // A-Illuminant at 2848.0K using the exact temperature scale
    const tNew = cie.c2Value(
        cie.PlanckC2.Exact)/cie.c2Value(cie.PlanckC2.Nbs1931) * 2848.0;
    const aNow = new cie.Blackbody(tNew);
    const [_l2, x2, y2] = aNow.lxy(c31);
    const got2 =`[${x2.toFixed(6)},${y2.toFixed(6)}]`;
    assert.assertEquals(got2, want);

Incandescent Lamps

Incandescent lamps typically have Tungsten filaments —Tungsten has the highest melting point of all metal elements— placed within a glass envelope, or the bulb, filled with a gas, such as argon, krypton, or nitrogen, but without oxygen. The emission of these lamps is different from a blackbody radiator, as the Tungsten element is partially reflective, and its emissivity is dependent on wavelength and temperature. In addition, part of the light emitted in a coiled filament is reflected, which alters its spectral distribution. And the glass envelope is also not completely transparent and has often a small tint which affects the spectral distribution of its output too. And last but not least, incandescent lamps are typically placed into a lighting fixture, which can have reflectors, diffusers, and even filters in form of lampshades, which tend to completely change their spectral illumination properties.

Incandescent lamps are getting more and more replaced with so-called “LED Retrofit lamps”, which have more or less the same shape as the traditional incandescent lamp, which come with a much larger variety with regards to color temperature, from warm-white to cool-white, larger variation in color rendering properties, and, due physics principles and driver constraints, much higher ‘flicker’. More on that in the LED Lamp section. Incandescent filament lamp output varies with filament temperature, which tends to vary only a little when driven at 50 or 60 Hz AC drive currents.

Incandescent-Filament Lamp NameLocation
Halogen [1)tm30/tm30lib076.json
Halogen [2)tm30/tm30lib077.json
Halogen [3)tm30/tm30lib078.json
Halogen MR16 [1)tm30/tm30lib079.json
Halogen MR16 [2)tm30/tm30lib080.json
Halogen MR16 [3)tm30/tm30lib081.json
Incandescent [60WA19)tm30/tm30lib082.json
Incandescent [75WA19 Halogena)tm30/tm30lib083.json
Incandescent [75WA19 Neodymium)tm30/tm30lib084.json
Incandescent [75WA19 Rough House)tm30/tm30lib085.json
Incandescent [75WA19 Softer White)tm30/tm30lib086.json
Krypton Incandescenttm30/tm30lib087.json
Neodymium Incandescenttm30/tm30lib088.json
Filtered Halogentm30/tm30lib089.json

The TM30 example library has a small collection of measured incandescent filament lamp examples, as shown above; it was generated using the script below:

    import init, {
        IlluminantsLib, EmissionType, IlluminantCategory
        } from "";
    await init();

    const index = await IlluminantsLib.fetch("tm30", 
        EmissionType.IncandescentFilament, IlluminantCategory.All);
    const data = new Map([...index.index()].sort());

    // print out as html table
    console.log("<table><tr><th span=2>Incandenscent Data Illuminants</th></tr>");
    for (const [k,v] of data) {

    deno run --allow-net examples/illuminants/tm30/incandescent.ts
    <tr><td>Halogen [1)</td><td>tm30/tm30lib076.json</td></tr>

These illuminants can be used with the DataIlluminant.fetch method; for example, to get the CIE 1931 chromaticity coordinates of a 60W ‘regular’ lightbulb, use:

    import init, {
    DataIlluminant, fetchCIE1931, AppearanceModel
        } from "";
    await init();

    const a19Illuminant = await DataIlluminant.fetch("tm30/tm30lib082.json");
    const c31 = await fetchCIE1931();

    const [l, x, y] = new AppearanceModel(c31, a19Illuminant).lxyRef();
    console.log(, l.toFixed(), x.toFixed(5), y.toFixed(5));
        deno run --allow-net examples/illuminants/tm30/a60w.ts
        Incandescent [60WA19) 100 0.45072 0.40803