Six numbers make a space
Assumes A hex code is not a colour and What a gamut costs.
An RGB colour space sounds like a large object. It is eight numbers.
Three pairs of chromaticity coordinates, one for each primary. One more pair for the white point. A transfer function, which is a curve and a couple of constants. That is the entire specification, and everything anybody ever quotes about the space — the 3×3 matrix, the luminance coefficients, the gamut volume, the coverage percentages — is derived from those eight numbers by arithmetic that fits on a page.
The three corners are six numbers and the diagram they are drawn on is a choice, so the same six numbers can be redrawn without touching a single specification.
The luminance the slice is taken at is the other free argument, and it decides how much of the diagram is reachable by anything at all.
Two more slices complete the stack, and between them they say that the six numbers are the only thing in the picture that has not moved.
The derivation
The problem is: find the matrix that takes linear RGB to XYZ.
Each primary’s chromaticity gives its direction in tristimulus space but not its magnitude. So write the matrix as three columns, each a primary’s chromaticity scaled by an unknown:
where and the are the three unknown scalings.
One constraint fixes them. RGB = (1, 1, 1) must be the white point. That is the definition of the white point: it is the colour the space produces when every channel is at maximum. So
and the matrix follows. A 3×3 inverse and a matrix multiply. There is nothing else in it.
Why derive it rather than copy it
The published sRGB matrix is in every specification and every graphics library, and copying it works. Deriving it buys three things, and the third is the one that matters.
It buys a check. The derived matrix agrees with the published one to better than 5 × 10⁻⁴, and that agreement is asserted rather than eyeballed. A transposed row or a swapped primary produces a matrix that looks entirely plausible and renders every image with a subtle cast.
It buys the luminance row for free. The middle row of is the coefficients that turn linear RGB into relative luminance — 0.2126, 0.7152, 0.0722 for sRGB, quoted everywhere as though they were measured. They are not measured. They fall out of the white-point constraint, and quoting them without the primaries they came from is quoting an answer without its question. They are different numbers for P3 and different again for Rec. 2020, which is why the midpoint of a gradient depends on which space it is computed in.
And it buys the ability to ask what if. A matrix is a table; a derivation is a function, and a function can be handed primaries nobody has standardised, or a white point nobody uses, and asked what happens. Every comparison in this essay is that function called twice.
The three luminance rows, which the essay says differ and does not print
The derivation is short enough to run from the eight numbers alone, and running it does two things the essay stops short of: it checks the claim independently, and it supplies the numbers the argument turns on.
Building the matrix from sRGB’s primaries and D65 gives a luminance row of 0.212639, 0.715169, 0.072192 against the published 0.2126, 0.7152, 0.0722 — agreeing to 3.9 × 10⁻⁵, an order of magnitude inside the 5 × 10⁻⁴ the assertion demands. RGB = (1, 1, 1) maps to (0.95046, 1, 1.08906), which is D65 to five decimal places, as the constraint requires.
The same three lines run on the other two spaces give the rows the essay describes as different numbers and never states:
| space | red | green | blue |
|---|---|---|---|
| sRGB | 0.2126 | 0.7152 | 0.0722 |
| Display P3 | 0.2290 | 0.6917 | 0.0793 |
| Rec. 2020 | 0.2627 | 0.6780 | 0.0593 |
From sRGB to Rec. 2020 the red coefficient rises by 23.5 per cent and the blue falls by 17.9, while the green moves by 5.2. So the disagreement is not spread evenly: it is almost entirely a transfer of weight from blue to red, with green nearly untouched — which follows from Rec. 2020’s green being a monochromatic primary near the peak of the luminance function, where moving it changes its chromaticity a great deal and its luminous efficiency very little.
That is the concrete form of the essay’s remark that a gradient’s midpoint depends on which space computed it. A grey ramp that is half-luminance in sRGB is not half-luminance in Rec. 2020 by nearly a quarter in its red channel, and a saturated red at the same code value is a quarter brighter.
A shared primary is not a shared column
The most quotable observation in the comparison table is that P3 and sRGB share a blue primary exactly, so P3 widens the gamut in red and green and not at all in blue. That is true of the chromaticity and not of the matrix.
The scaling constants come out at 0.21264, 0.71517, 0.07219 for sRGB and 0.22897, 0.69174, 0.07929 for P3 — so the blue channel’s scaling is 9.8 per cent larger in P3, and the whole blue column of the matrix grows with it:
| space | blue column |
|---|---|
| sRGB | 0.180481, 0.072192, 0.950532 |
| Display P3 | 0.198217, 0.079287, 1.043944 |
The same blue chromaticity contributes a tenth more tristimulus in P3, because the scalings are fixed jointly by the white-point constraint and moving the other two primaries changes how much blue is needed to reach D65.
So the practical consequence the essay draws — that blue-heavy content looks identical on both — is right about a colour at the blue primary and wrong about the encoding. A blue-heavy image with the same code values in the two spaces is a different colour in each, by ten per cent in the blue channel’s contribution, because the code value is scaled differently even though the primary it points at is the same.
The eight numbers do not decompose. Each primary’s contribution depends on all three, through one matrix inverse, which is the whole reason the derivation is a derivation rather than three independent lookups — and it is easy to lose sight of precisely when two spaces share a corner.
The area figure checks against a neighbour
One small cross-check, worth making because the collection quotes the number elsewhere.
The three triangles’ areas on the chromaticity diagram come out at 0.11205, 0.15200 and 0.21187 — P3 is 1.36 times sRGB and Rec. 2020 is 1.89. The essay on gamut population quotes P3 at 1.357 times sRGB by area, computed by an entirely different route in an entirely different file.
Agreement to three figures between a triangle’s area taken from three chromaticity pairs and a figure computed from a gamut solid is the kind of check that costs one line and confirms that the two essays are describing the same object. Eight numbers really do determine everything, and the evidence for it is that two independent computations from them agree without being made to.
Where the observer enters
The derivation takes primaries as chromaticities and a white point as tristimulus values, and one of those two is observer-dependent in a way the other is not.
The primaries are defined as chromaticities. sRGB’s red is at (0.640, 0.330) because the standard says so; that is a definition, not a measurement, and no change of observer moves it.
The white point is different. D65 is a spectrum — a daylight reconstruction from the CIE’s basis functions — and its chromaticity is that spectrum integrated against the colour-matching functions. Change the observer and the white point’s tristimulus values change, and therefore the scaling vector changes, and therefore the matrix changes.
This is why the same space has slightly different luminance coefficients depending on which observer computed its white, and why specifications pin D65 by chromaticity rather than by spectrum — (0.3127, 0.3290) is a number, and it is observer-independent because it has already had an observer applied to it. Pinning the number rather than the spectrum makes the space well-defined and quietly freezes in a choice of observer that the specification does not mention.
What the three spaces actually differ in
Laying the eight numbers side by side is more informative than any prose summary.
| sRGB | Display P3 | Rec. 2020 | |
|---|---|---|---|
| Red | (0.640, 0.330) | (0.680, 0.320) | (0.708, 0.292) |
| Green | (0.300, 0.600) | (0.265, 0.690) | (0.170, 0.797) |
| Blue | (0.150, 0.060) | (0.150, 0.060) | (0.131, 0.046) |
| White | D65 | D65 | D65 |
| Transfer | sRGB | sRGB | Rec. 2020’s own |
Three things are visible immediately. P3 and sRGB share a blue primary exactly — P3 widens the gamut in red and green and not at all in blue, which is why the practical gain from P3 is in saturated reds and greens and why blue-heavy content looks identical on both. All three share a white point, so no chromatic adaptation is involved in converting between them; the conversion is a pure matrix product. And Rec. 2020’s green is at (0.170, 0.797), which is on the spectral locus at about 532 nm, meaning it is a monochromatic primary and no display that is not a laser can produce it.
The last of those is worth dwelling on because it changes what the specification is doing. sRGB’s primaries described a display that existed. Rec. 2020’s describe one that does not and largely cannot, so every Rec. 2020 display is by construction covering some fraction of its own space — the specification is a container rather than a description.
The transfer function is not a gamma
The eighth number, or rather the eighth object, is the transfer function, and it is the part most often described wrongly.
sRGB’s is frequently called “gamma 2.2”. It is not a power law. It is a linear segment near black joined to a power function of exponent 2.4 with an offset, and the composite is approximately a 2.2 power over most of its range. The linear toe exists because a pure power law has infinite slope at zero, which quantises catastrophically in the darkest codes and is numerically awkward everywhere.
Getting this wrong is one of the commonest defects in graphics code, and it is invisible in the midtones and obvious in shadows. Rec. 2020 has yet another transfer function, and the HDR encodings have transfer functions that are not relative at all — PQ maps code values to absolute luminance in cd/m², which changes what a colour space is rather than how it is encoded.
What the eight numbers do not fix
A colour space specifies what its numbers mean. It says nothing about whether a display can produce them.
That distinction is the whole content of colour management and is the thing a hex code cannot carry. A file tagged Display P3 containing (255, 0, 0) is asking for P3’s red primary. A display that cannot reach it will show something else, and the something else is decided by whatever gamut mapping sits in the path, which is not specified by the colour space and varies between operating systems.
Nor do the eight numbers say anything about appearance. Two spaces with the same white point produce the same tristimulus values for their white, and a white viewed in two different rooms looks different, which is a matter for an appearance model and has no representation anywhere in this arithmetic.
That probe is the only honest way this site can say anything about the reader’s own space. Everything else in this essay is arithmetic that produces the same answer everywhere; the probe produces a different answer on a wide-gamut display than on an sRGB one, and neither the site nor the reader is told which happened. It is a small demonstration of the gap between a colour space, which is eight numbers and a page of arithmetic, and a display, which is a physical object nobody has measured.
What was computed, and how
Four assertions, and each one has a specific error in mind.
The derived sRGB matrix equals the published one to 5 × 10⁻⁴. Catches a transposed matrix, a swapped primary, or a computed as .
The luminance row is the published luminance row. Checked separately, because it is the row most often copied out of context and the one whose being wrong is hardest to see.
Every primary round-trips. Feeding RGB = (1, 0, 0) through the matrix and back must return (1, 0, 0), for all three primaries and for white, in all three spaces.
RGB = (1, 1, 1) maps to the white point. The constraint the whole derivation is built on, checked as an output rather than assumed as an input — because a derivation that used the constraint and then failed it has an arithmetic error rather than a conceptual one.
And the gamut test must still reject: a colour known to be outside a space is fed in and the machinery is required to say so, because everything on this site that is outside the gamut is marked rather than clipped and a gamut test that had stopped rejecting would silently turn every hatched region solid.
Why the matrix is not the conversion
There is a common shortcut that is wrong and looks right: converting between two RGB spaces by multiplying by one matrix and the inverse of the other.
That works exactly when the two spaces share a white point, which sRGB, P3 and Rec. 2020 all do. It fails when they do not — converting to ProPhoto RGB, whose white is D50, or to any of the print-oriented spaces — because the tristimulus values of a colour under D65 are not the tristimulus values of the same appearance under D50. A chromatic adaptation transform has to sit in the middle, and which transform is not a detail: the naive one is provably wrong and is used anyway, and the good ones disagree with each other by amounts that are visible.
So the honest statement of a conversion is: linearise, matrix to XYZ, adapt if the whites differ, matrix from XYZ, apply the destination transfer function. Five steps, of which the two matrices are the ones everybody remembers and the adaptation is the one that gets dropped.
Where the model stops
The whole derivation assumes the display is additive — that the light from the three channels adds, so that a mixture’s tristimulus values are the sum of the components’. That is a very good assumption for LED and OLED panels, was a fair one for CRTs, and is a poor one for anything with a shared backlight and crosstalk between subpixels.
It also assumes the primaries are fixed, which is false for most real panels: a display’s primaries drift with temperature, with age, and with the average picture level, because power supplies sag. A calibrated display is one where somebody has measured the actual primaries and built a correction, and the correction goes stale.
And it assumes the transfer function is what the specification says. A display’s actual response is measured, not specified, and the gap between them is the whole reason display calibration exists.
The eight numbers as a checklist
The practical value of knowing a space is eight numbers is that it turns “which colour space is this” from a vague question into a list with eight blanks, and a file, a device or a specification either fills them or does not.
An untagged image fills none of them. It is a grid of triples with no statement of what the triples mean, and every piece of software that opens it guesses — almost always sRGB, which is right often enough that the guess survives and wrong often enough to produce the perennial complaint that a photograph looks different in two applications.
A device profile fills all eight and then some, because a real device is not exactly a set of primaries and a power law and the profile carries measured tables for the residual. A profile is what an eight-number specification looks like once somebody has measured the object it was supposed to describe.
The gap between those two — an idealised space and a measured device — is the entire subject of colour management, and it exists because the eight numbers are a model of a display rather than a description of one.
Who found it, and when
The derivation is old enough that nobody is credited with it — it is a direct consequence of Grassmann’s laws and the definition of chromaticity, and it appears in essentially this form in the colorimetry literature from the 1950s onward.
What is more recent, and more interesting, is the standardisation of the specific eight numbers. sRGB was proposed by Hewlett-Packard and Microsoft in 1996, and its primaries were chosen to match the phosphors of a typical CRT of that era rather than for any perceptual reason. Every image on the internet is encoded against the phosphor chemistry of mid-1990s television tubes, and will be for the foreseeable future, because comparability outlasts correctness.
Display P3 borrows its primaries from DCI-P3, a cinema projection standard, and pairs them with sRGB’s white point and transfer function — a hybrid assembled for compatibility rather than derived from anything. Rec. 2020’s primaries are on the spectral locus itself, which means they cannot be produced by any real emitter and the space is aspirational at its corners by construction.
What the picture cannot show
The hero figure draws three triangles, two of which enclose chromaticities the reader’s display probably cannot produce, and the region outside the display’s own gamut is hatched rather than filled.
That marking is doing something specific in this essay. The argument is that eight numbers fully determine a space, and the figure is evidence that they determine it including the parts nothing can display — the triangle for Rec. 2020 has a corner on the spectral locus, the arithmetic is perfectly happy there, and no physical object is. A colour space is a mathematical object, and the hatching is where the mathematical object stops corresponding to anything a reader could be shown.
The other thing the figure cannot show is the white point, which is the number that does the most work in the derivation and is invisible on a diagram of triangles. Three spaces with identical primaries and different whites are three different spaces with three different matrices, and they would be drawn here as one triangle.
Where this goes next
The corners of Rec. 2020 lead directly to what a wider gamut costs, which is the rung below this one and where the trade between reach and quantisation is made explicit.
The observer dependence of the white point leads to the choice this site keeps arriving at — that a specification pinning D65 by chromaticity rather than by spectrum has frozen in an observer without saying which.
What this makes readable
Essays that name this one as a prerequisite.
Named alongside this one
Essays reaching for the same objects. Nobody chose these; they are what the index of named objects makes visible.
- The triangle is a shadow display p3 · gamut · primaries · rec. 2020 · white point
- A gamut has a population display p3 · gamut · primaries · standard observer
- Not every colour has a wavelength gamut · primaries · standard observer · white point
- Why colour is exactly three-dimensional gamut · primaries · white point · xyz
- A display in a room is a smaller display colour management · gamut · white point
- A gradient is a path colour space · gamut · transfer function
What links here
Every essay whose body links to this one.
The objects this essay names
Each one links to every other essay that touches it.
Colour managementColour spaceDisplay P3GamutPrimariesRec. 2020Standard observerTransfer functionWhite pointXYZ