Illuminants

To see — and in specific to see color — we need light. During the day, when outdoors, that light is produced by the sun. In the evening light is mostly produced by electric lamps, such as incandescent, fluorescent, or LED lamps. How we see color depends on the way the objects around us are illuminated, and illuminants play an important role in colorimetry. You might have experienced “bad lighting” yourself, when buying clothes in a store, for example, and noticed that the colors of a shirt look quite different outdoors than how they looked in the store.

In colorimetry, an illuminant represents the spectral power distribution of the light illuminating an object. Typically, in colorimetric modeling, a single source of light is used, such as daylight, light from a fluorescent lamp, or LED lamps for newer installations.

The Toolbox contains mathematical illuminants, such as Blackbody, which uses Planck’s equation to calculate a spectral distribution, and illuminants represented by an array of data, which are available on this site in form of JSON data files.

All illuminants in this library implement the illuminate method, which takes a wavelength domain as an argument, and will produce an array of 64-bit floating point numbers in form of a Float64Array-type. They also implement a domain method, which produces the native wavelength domain for the data in the illuminant, or results in an undefined type, when the data has no inherent or native domain. This is for example the case with function model data, such as the Blackbody illuminant which is based on Planck’s law, or the Led illuminant, which is using a Gaussian type of function to calculate spectral distribution.

Here is an example of a Blackbody illuminant, with a color temperature of 3000K, which spectral power distribution is obtained by:


    // create a blackbody illuminant with a color temperature of 3000K
    const bb3000 = new cie.Blackbody(3000.0)

    // get its spectral power distribution in 256 points, 
    // over a wavelength range from 0.3 to 3 micrometer
    const domain = new cie.Domain(0.3E-6, 3E-6, 256);
    const spd = bb3000.illuminate(domain);

    // check first 5 values
    const want = [ 3824.39, 5545.37, 7802.22, 10684.53, 14279.18 ];
    for (const [i,w] of want.entries()) {
        assert.assertAlmostEquals(w, spd[i], 5E-3);
    }

The Blackbody illuminant is based on a mathematical model and is implemented and contained within the Cie-library. In this example, as in all the examples in this book, I have left out the common import and initialization lines, which are:

    import * as assert from "https://deno.land/std@0.156.0/testing/asserts.ts";
    import init, * as cie from "https://www.gerardharbers.com/cie.js";
    await init();

To run these examples, please add these three lines on top of your TypeScript or JavaScript file, and run them with the deno --allow-net <filename>.ts command in your terminal. I use the Deno assert functions and the Deno test features to ensure all the examples in this book work correctly; if the assert function fails, I know something is wrong.

Besides model-based illuminants, the library also has a large collection of illuminants directly defined by data. Typically these are measured directly, or derived from measured data. These illuminants are pulled into the library as a DataIlluminant, using its fetch method. In the example below we calculate the tri-stimulus values of the CIE D65 illuminant, which is pulled from the server using the fetch-API and using the standard CIE 1931 2º observer which is also fetched from the server (see Observer).


    // Get D65 illuminant using the DataIlluminant directly
    let d65 = await cie.DataIlluminant.fetch("cie/d65.json");

    // or use a convenience constructor function
    d65 = await cie.fetchD65();

    // calculate its tristimulus values using the CIE 1931 observer
    const c31 = await cie.fetchCIE1931();
    const got = d65.xyz(c31);
    const want = [95.04, 100, 108.86];
    
    for (const [i,w] of want.entries()) {
        assert.assertAlmostEquals(got[i], w,  5E-3);
    }

The data is contained in JSON files, and located on the same server as this site. To fetch these data-based illuminants, you have to use the await keyword, as they are fetched from the server asynchronously. In this library, all the functions and methods containing the word fetch need the await keyword, and if used in a function you write, it requires the keyword async too. When these async functions are invocated, they also need the await keyword. For example:

    // load the CIE 1931 standard observer, 
    // used to calculate the tri-stimulus values
    const c31 = await cie.fetchCIE1931();

    async function tristimulusValues(locs: string[]) {
        const v: number[][] = [];
        for (const loc of locs) {
            let d = await cie.DataIlluminant.fetch(loc);
            v.push(d.xyz(c31))
        }
        return v
    }

    // use await keyword here too
    const got = await tristimulusValues(
        ["cie/d50.json", "cie/d55.json", "cie/d65.json", "cie/d75.json"]
    );
    const want = [
        [ 96.42, 100, 82.50],
        [ 95.68, 100, 92.12],
        [ 95.04, 100, 108.86],
        [ 94.97, 100, 122.59]
      ];

    const got_values = got.flat();
    for (const [i,w] of want.flat().entries()) {
        assert.assertAlmostEquals(got_values[i], w,  5E-3);
    }

The library has the following illuminants and illuminant libraries (for details see the later sections in this chapter):

  • Illuminant Blackbody, using Planck’s law to calculate the spectral emission of a blackbody for a given temperature.

        const bb3000 = new cie.Blackbody(3000.0);
    
  • CIE Standard D illuminant, for correlated color temperatures in the range from 4000 to 25000K:

        const d5000 = new cie.Daylight(5000.0);
    
  • CIE Standard Data Illuminants C, D50, D55, D65, D75, F1 to F12, F3.1 to F3.15, HP1 to HP5, and LED series. For example, to get the CIE LED-B1 and the CIE F3.1 standard illuminants, use fetchLED_B1 and fetchF3_1:

        const ledB1 = await cie.fetchLED_B1();
        const f31 = await cie.fetchF3_1();
    
  • The IES TM30 illuminant collection is available in the library too. This is a collection of 318 illuminants, all defined as data illuminants in the range from 380 to 780nm with 1nm steps. This collection was used to test and optimize the TM30 and CIE color fidelity metrics. The individual illuminants can be obtained directly, using their ordinal number as listed in the TM30 Excel sheet:

        
        // get the first illuminant in this series, which is the CIE F1 illuminant;
        const f1 = await cie.DataIlluminant.fetch("tm30lib/tm30lib001.json");
    
        // illuminant 50 is a measured example of the F40T12/41U Fluorescent lamp:
        const f40t12 = await cie.DataIlluminant.fetch("tm30lib/tm30lib050.json");
    
        // get the last illuminant in this series, "Tri-band Gaussian [2)", 
        // an example of a theoretical illuminant using
        // Gaussian shaped compoents
        const tribandGauss2 = await cie.DataIlluminant.fetch("tm30lib/tm30lib318.json");
    

    Due to the size of this collection, it is also possible to search for illuminant emission types and categories, using the IlluminantsLib class methods. See for more information in the TM30 Illuminants Example library section.

  • A generic LED Array Illuminant,