Glossary

Color gamut

Color gamut is the complete range of colors that a device, medium or color space can represent, and for web work it usually means comparing sRGB with Display P3. A color that is valid but cannot be produced by a given display is called out of gamut. The standard CSS check is the color-gamut media feature, which accepts srgb, p3 and rec2020.

How it works

A gamut is defined by its three primaries plus a white point. The triangle those primaries form on a chromaticity chart contains every color the space can mix. A larger triangle means more saturated colors are possible.

Chromaticity coordinates from the CSS Color 4 spec:

  • sRGB: red 0.640, 0.330; green 0.300, 0.600; blue 0.150, 0.060; white D65.
  • Display P3: red 0.680, 0.320; green 0.265, 0.690; blue 0.150, 0.060; white D65, with the sRGB transfer curve.
  • Rec. 2020: an ultra-wide broadcast space that can represent almost all visible real-world colors, per the spec.

P3 and sRGB share the same blue and white, so the extra range is mostly in saturated reds and greens. In CSS you write wide-gamut colors with color(display-p3 r g b). When a color is outside the screen's gamut, a browser applies gamut mapping. CSS Color 4 defines gamut mapping to an RGB destination as a binary search that reduces chroma in OkLCh, while plain clipping is the cheaper alternative.

In-gamut RGB coordinates are inside 0 to 1. A color is out of gamut for a space when converting into it gives a channel below 0 or above 1.

const inv = m => { const [a,b,c,d,e,f,g,h,i] = m.flat(); const D = a*(e*i-f*h) - b*(d*i-f*g) + c*(d*h-e*g);
  return [[(e*i-f*h)/D,(c*h-b*i)/D,(b*f-c*e)/D],[(f*g-d*i)/D,(a*i-c*g)/D,(c*d-a*f)/D],[(d*h-e*g)/D,(b*g-a*h)/D,(a*e-b*d)/D]]; };
const mul = (M, v) => M.map(r => r[0]*v[0] + r[1]*v[1] + r[2]*v[2]);
const xy = (x, y) => [x / y, 1, (1 - x - y) / y];
function toXYZ(p) { const W = xy(0.3127, 0.3290); const P = p.map(([x, y]) => xy(x, y));
  const M = [0,1,2].map(r => P.map(c => c[r])); const S = mul(inv(M), W); return M.map(r => r.map((v, j) => v * S[j])); }
const SRGB = toXYZ([[0.64,0.33],[0.30,0.60],[0.15,0.06]]);
const P3 = toXYZ([[0.68,0.32],[0.265,0.69],[0.15,0.06]]);
const dec = v => Math.sign(v) * (Math.abs(v) <= 0.04045 ? Math.abs(v)/12.92 : ((Math.abs(v)+0.055)/1.055)**2.4);
const enc = v => Math.sign(v) * (Math.abs(v) <= 0.0031308 ? 12.92*Math.abs(v) : 1.055*Math.abs(v)**(1/2.4) - 0.055);
const p3ToSrgb = c => mul(inv(SRGB), mul(P3, c.map(dec))).map(enc);
for (const c of [[1,0,0],[0,1,0],[0,0,1],[0.5,0.5,0.5]]) {
  const o = p3ToSrgb(c); const ok = o.every(v => v >= -1e-6 && v <= 1 + 1e-6);
  console.log('color(display-p3 ' + c.join(' ') + ') -> srgb ' + o.map(v => v.toFixed(4)).join(' ') + (ok ? '  in gamut' : '  OUT of sRGB gamut'));
}
color(display-p3 1 0 0) -> srgb 1.0931 -0.2267 -0.1501  OUT of sRGB gamut
color(display-p3 0 1 0) -> srgb -0.5116 1.0183 -0.3107  OUT of sRGB gamut
color(display-p3 0 0 1) -> srgb -0.0000 0.0000 1.0420  OUT of sRGB gamut
color(display-p3 0.5 0.5 0.5) -> srgb 0.5000 0.5000 0.5000  in gamut

How do you detect a wide-gamut display in CSS?

Use the color-gamut media feature: (color-gamut: p3) is true when the browser and output device support approximately the Display P3 gamut or more. The srgb value is true on nearly every color display, and p3 is a superset of srgb. The check is approximate, because a device can claim Rec. 2020 but render much less of it.

Common pitfalls

  • Treating out-of-range numbers as errors: color(display-p3 1 0 0) becomes red above 1 and green and blue below 0 in sRGB. The color is valid, just not displayable there.
  • Clipping channels naively: clamping each channel to 0 to 1 can shift hue. Reduce chroma instead, as CSS gamut mapping does.
  • Assuming P3 is only brighter: it adds saturation. White stays the same D65 white as sRGB, as the gray row above shows.
  • Serving P3 colors with no fallback: older browsers ignore color(), so declare an sRGB value first.
  • Comparing gamut by percent claims: a monitor's coverage figure depends on the space and method, so check the primaries.

Related terms

  • sRGB — the default web gamut
  • RGB — the additive model whose primaries set the gamut
  • Color management — how colors are converted between gamuts
  • Oklab — the space CSS gamut mapping works in
  • CMYK — a print gamut with a different shape
  • Lab color — a device-independent space that can describe every visible color

See also