It seems most references showing a CIE Yxy diagram can be reduced to very few (one?) sources. If I wanted to construct the diagram myself from some published measurement values, how would I do it?

I'm assuming there is some parametric function f(wavelength) giving a vector (x, y) for each f. But how would that function f look like?

Actually I wanted to construct a CIE Luv* diagram, but when knowing the (x, y) vectors for CIE Yxy, I could easily convert those to (u*, v*), right?

Update (2019-09-03):

Drawing the outline (spectral line) is one part of the problem. Another part of the problem is filling in the correct colors (I'm aware that no existing computer display device can actually present them): Is it OK to put the C white at its position and use Gouraud-Shaded Triangle Meshes to interpolate the area between two points on the spectral line and that white point (Example Image connecting 470nm with 500nm and with Illuminant C)? That seems to be OK.

In case it's OK, how would I fill the "non-spectral triangle" outlines by both ends of the spectral line and the white-point? Trying that I found out that the ends of the spectral line practically represent black, so the triangle came out much more black than purple, and opposed to all diagrams I've seen before, there's a discontinuity where the non-spectral colors are (Example Image)

As an experiment, I cut off the end of the spectrum to 435-645nm, and the transition from spectral to non-spectral colors is rather smooth then. (Example Image)

However when you use normalized data the dark colors become bright, and the brighter colors become darker. Effectively I realized that the "white point" actually is a gray point (several diagrams seem to have that wrong), and it seems that Illuminant C is the correct one, too , so this is my final version so far.

  • \$\begingroup\$ For each wavelength, get the CIEXYZ values from a table, Add them together and divide X and Y by the sum which yields x and y. Plot the point. Do the same for each wavelength. Connect the dots. Possible duplicate of photo.stackexchange.com/questions/53510/… \$\endgroup\$
    – doug
    Jun 3, 2019 at 14:36
  • 4
    \$\begingroup\$ I'm voting to close this question as off-topic because as presented it has nothing to do with actually taking a photograph. \$\endgroup\$
    – Michael C
    Jun 3, 2019 at 17:13
  • 1
    \$\begingroup\$ @Michael C: Please suggest where to ask the question: There is no generic "color" or "colormetry" site, so this is the site where most questions about CIE Yxy diagrams had been asked before. Also many questions are not about fundamentals, but very specific (individual) problems. Why not allow a generic question? I think any answer to this question will give significant insights to what the CIE Yxy diagram means... \$\endgroup\$
    – U. Windl
    Jun 3, 2019 at 22:19
  • \$\begingroup\$ @doug: So I would need the XYZ CMFs (not the RGB CMFs) to start with? Those from "2-deg coordinates from 2-deg XYZ CMFs" at cvrl.org/ciexyzpr.htm? \$\endgroup\$
    – U. Windl
    Jun 3, 2019 at 22:30
  • \$\begingroup\$ The 2 degree, 1931 chromaticity coordinates tables at your link have already been normalized. That is the XYZ values add up to 1. As a consequence, the XY values correspond to the xy coordinates of the classic, horseshoe CIE Chromaticity graph. Just plot the X and Y as dots for each wavelength and connect the dots. Then draw a straight line between the lower left and lower right points (violet to magenta ro red) which is just mixtures of the lowest and highest wavelengths. \$\endgroup\$
    – doug
    Jun 3, 2019 at 22:47

2 Answers 2


The conversion path from CIE xyY to a given RGB colourspace, e.g. sRGB, is typically as follows:

CIE xyY --> CIE XYZ --> RGB

With that in mind, generating the diagram can be done by generating a regularly spaced 2D grid of samples in domain [0, 1] and apply the conversion path to them.

As you found out, no display device is capable of representing the visible spectrum, so you will be limited by its colorimetry boundaries.

Colour implements the three common Chromaticity Diagrams:

CIE 1931 Chromaticity Diagram CIE 1931 Chromaticity Diagram CIE 1960 UCS Chromaticity Diagram CIE 1960 UCS Chromaticity Diagram CIE 1976 UCS Chromaticity Diagram CIE 1976 UCS Chromaticity Diagram

The Python code for drawing the colours and bounding them with the spectral locus is as follows:

def plot_chromaticity_diagram_colours(
        cmfs='CIE 1931 2 Degree Standard Observer',
        method='CIE 1931',
    Plots the *Chromaticity Diagram* colours according to given method.

    samples : numeric, optional
        Samples count on one axis.
    diagram_opacity : numeric, optional
        Opacity of the *Chromaticity Diagram* colours.
    diagram_clipping_path : array_like, optional
        Path of points used to clip the *Chromaticity Diagram* colours.
    cmfs : unicode, optional
        Standard observer colour matching functions used for
        *Chromaticity Diagram* bounds.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.

    Other Parameters
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

        Current figure and axes.

    >>> plot_chromaticity_diagram_colours()  # doctest: +SKIP

    .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png
        :align: center
        :alt: plot_chromaticity_diagram_colours

    settings = {'uniform': True}

    _figure, axes = artist(**settings)

    method = method.upper()

    cmfs = first_item(filter_cmfs(cmfs).values())

    illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint

    ii, jj = np.meshgrid(
        np.linspace(0, 1, samples), np.linspace(1, 0, samples))
    ij = tstack([ii, jj])

    # NOTE: Various values in the grid have potential to generate
    # zero-divisions, they could be avoided by perturbing the grid, e.g. adding
    # a small epsilon. It was decided instead to disable warnings.
    with suppress_warnings(python_warnings=True):
        if method == 'CIE 1931':
            XYZ = xy_to_XYZ(ij)
            spectral_locus = XYZ_to_xy(cmfs.values, illuminant)
        elif method == 'CIE 1960 UCS':
            XYZ = xy_to_XYZ(UCS_uv_to_xy(ij))
            spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values))
        elif method == 'CIE 1976 UCS':
            XYZ = xy_to_XYZ(Luv_uv_to_xy(ij))
            spectral_locus = Luv_to_uv(
                XYZ_to_Luv(cmfs.values, illuminant), illuminant)
            raise ValueError(
                'Invalid method: "{0}", must be one of '
                '{{\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\'}}'.format(

    RGB = normalise_maximum(
        XYZ_to_plotting_colourspace(XYZ, illuminant), axis=-1)

    polygon = Polygon(
        if diagram_clipping_path is None else diagram_clipping_path,
    # Preventing bounding box related issues as per
    # https://github.com/matplotlib/matplotlib/issues/10529
    image = axes.imshow(
        extent=(0, 1, 0, 1),

    settings = {'axes': axes}

    return render(**kwargs)

It is obviously dependent on a lot of Colour functions but it should be enough to illustrate the algorithm.

  • \$\begingroup\$ I found out that I actually use different colors as the rest (it seems): If you look at my plot, it even seems to look a bit three-dimensional, like viewing the RGB-cube from the white corner. That is, because I projected the XYZ colors to the X + Y + Z = 1 plane instead of using the colors in that plane. \$\endgroup\$
    – U. Windl
    Jun 9, 2019 at 21:39
  • \$\begingroup\$ Note that we are normalising the RGB colours on the maximum value of each RGB triplet but there are no defined rules on how the normalisation should be done. Colour scientists tend to prefer to not use colours at all in chromaticity diagrams because they can be misleading. \$\endgroup\$
    – Kel Solaar
    Jun 10, 2019 at 1:41
  • \$\begingroup\$ Actually after all my experience I'd say all of the above three diagrams are wrong regarding the white point: It is white in the diagrams, but it should be gray (as bright as the neighbors). Maybe compare with i.stack.imgur.com/n5x4w.png. \$\endgroup\$
    – U. Windl
    Sep 2, 2019 at 22:35
  • \$\begingroup\$ No they are correct, the whitepoint which has unit Luminance is sitting at the top of a given RGB colourspace gamut given in the CIE xyY (or any colour model for that matter). Here is a 3D view of it to convince you, the summit of the volume is the whitepoint: imgur.com/a/2iAb3l4, rotate the camera to be normal to the x,y plane and you will see the sRGB gamut in the CIE 1931 Chromaticity Diagram. \$\endgroup\$
    – Kel Solaar
    Sep 3, 2019 at 7:54
  • 1
    \$\begingroup\$ The CIE does not say it has to be a specific plane. CIE xyY is a projective transformation of CIE XYZ, whether you decide to take the colours at the CIE XYZ simplex for the diagram or something else is a matter of taste. The advantage of using the maximum Luminance, i.e. conversion from a grid of chromaticity coordinates, is that is obvious where the whitepoint is located in the diagram, it becomes important when you are studying other whitepoints. \$\endgroup\$
    – Kel Solaar
    Sep 4, 2019 at 7:52

You can find the coordinates to plot for all of the CIE Standards on the Web at the Color & Vision Research Laboratory
They are available as CSV (Comma separated variables) suitable for Excel or other applications.

Good luck

  • \$\begingroup\$ Actually I've been there (Color & Vision Research Laboratory) weeks before asking this question. Which data exactly are you referring to, or do you just guess that data might be there? \$\endgroup\$
    – U. Windl
    Jun 3, 2019 at 22:14
  • \$\begingroup\$ @U.Windl Navigate to the site, locate the plot you want, and click on the blue encircled "i" to reveal (in a drop-down page) the formulas to construct the plot data for each wavelength you wish. \$\endgroup\$
    – Stan
    Jun 3, 2019 at 22:47
  • \$\begingroup\$ Unfortunately the site has at least two issues: 1) Navigating does not change the URL displayed (due to using frames). 2) The formula display seems to work correctly only with Microsoft IE: With Firefox I need to scroll to see the formulas. \$\endgroup\$
    – U. Windl
    Jun 3, 2019 at 23:41

Not the answer you're looking for? Browse other questions tagged or ask your own question.