Escaping the sRGB Prison

Chris Lilley, W3C


I’m Chris or @svgeesus

Technical Director at W3C
Strategy lead, Core Web Technologies
W3C liaison to ICC
co-editor, CSS Color 3,4,5

Topics for today

What is Color

Color Science is very complicated!

But (don't worry!)

what do we need from a color model

Light and Dark

Opponent colors

Opponent colors

Opponent colors

Opponent colors

A pair of chromatic axes

Chroma and Hue

Now we can see a color gamut


Because 3D is hard

the Prison

ITU Rec BT.709 (1993)
Parameter values for the HDTV standards
Standard, narrow gamut
OETF, and non-matching EOTF (gamma 2.4)
D65 white, dim surround
sRGB (1996)
Same chromaticities, D65 white as BT.709
“Inverse OETF” with linear segment
Display referred
Overall system gamma 1.0 (normal surround)
Assumes 5% viewing flare

Lots of color formats


rgb(77.61% 36.34% 2.45%)

hsl(55.257 73.833% 35.465%)

hwb(94.22 22.35% 34.88%)



rgb(128, 0, 128)

 and so it remained … 

DCI P3 - SMPTE EG 432 (2005)
Digital Cinema Initiative
Projectors for digital cinema
Defines chromaticities, weird white
Monochromatic red, 615nm
White luminance 48 cd/m²
Dark surround
UltraHD Premium
Conforming devices required to display at least 90% of DCI P3
UHD Premium logo
(For tier 500 and greater)
Conforming devices required to display at least 90% of DCI P3
Display HDR logo
Apple Display P3
White, EOTF, viewing conditions same as sRGB
Primaries same as DCI-P3

Welcome to Web development

specifying color

Using display-p3 in CSS Color 4

New: color() function

color(display-p3 0.31 0.56 0.74);

color(display-p3 0.31 0.56 0.74 / 0.6);

Minimum 10 bit precision per component

Using display-p3 in CSS Color 4

color(display-p3 0 1 0);

Inside display-p3 gamut, outside sRGB

Will use gamut mapping if not displayable

color(display-p3 -0.34 -0.08 1.387)

color(display-p3 -0.1 1.2 0);

Yes you can do that

We will talk about gamut mapping


Predefined color spaces in CSS Color 4

color(display-p3 0.31 0.56 0.74);

color(prophoto-rgb 0.393 0.478 0.677);

color(rec2020 0.351 0.508 0.708);

oklch(0.623 0.112 235.8);

/*CIE*/ lch(56.29% 37.11 245.1);

P.S. Handy color space converter

Defensive CSS

			@media (color-gamut: p3) {
				// is this a wide-gamut screen?
				color: color(display-p3 0.31 0.56 0.74);
			@supports (background: color(display-p3 1 1 1)) {
				// is the syntax supported?
				color: color(display-p3 0.31 0.56 0.74);

Display-p3 in Canvas

	let canvas = document.getElementById("foo");
	let context = canvas.getContext("2d", { colorSpace: "display-p3" });
	context.fillStyle = `color(display-p3 0.43313 0.50108 0.37950)`;
	context.fillRect(10, 10, 50, 50);
	context.fillStyle = `color(sRGB 0.41587 0.503670 0.36664)`;
	context.fillRect(60, 10, 50, 50);

mixing colors

Mixing colors in CSS Color 5

Two colors can be mixed in any colorspace

color-mix(in oklab, teal 65%, olive);

Worked example, color-mix()

teal in oklab is color(oklab 0.5431 -0.09 -0.024)

olive in oklab is color(oklab 0.5807 -0.043 0.1191)

mix = color(oklab 0.5563 -0.073 0.0263)

= rgb(27.85% 50.81% 39.12%)

How color-mix() works

  1. Colors to mix can be in any color space
  2. Colors converted to mixing color space, without gamut mapping
  3. Mixed by specified percentage
  4. Result is in the mixing color space
  5. Result may be out of gamut for display (especially with polar color spaces)

Live color-mix()

color-mix(in oklab, lime 50%, lch(50% 100 40))
Show workings

[color1] in [interpolation_space] is [, interpolation_space) & ""]

[color2] in [interpolation_space] is [, interpolation_space) & ""]

mix = [, color2, (100 - percent)/100, space: interpolation_space), interpolation_space) & ""]

= [, color2, (100 - percent)/100, space: interpolation_space), interpolation_space).to('srgb') & ""]

Gradients, mixing in different color spaces

gamma-encoded sRGB is a poor space for mixing


Gradients, mixing in different color spaces

CIE Lab has poor hue linearity for blues


Specifying interpolation color space

New: color-interpolation method

linear-gradient(to right, #F01, #081)

linear-gradient(in oklab to right, #F01, #081)

Polar color spaces can also specify the shorter or longer hue direction

linear-gradient(in oklch longer to right, #F01, #081)

modifying colors

Relative Color Syntax

--mycolor: teal;
background: oklch(from var(--mycolor) l calc(0.8 * c) calc(h -120));

Predefined variables per color space: here l, c and h

Relative Color Syntax

--mycolor: teal;
color: color(xyz-d65 from var(--mycolor) calc(0.8 * x) y calc(1.2 * z));
color: color(prophoto-rgb from var(--mycolor) calc(0.8 * r) calc(0.7) calc(0.4 * b));

Beware, in RGB spaces, values are gamma-encoded

Gamut Mapping

Colors can be specified out of gamut

or calculations give out of gamut results

Those colors must be modified

(by the browser, not by you)

to look similar but in gamut of display

Constant-hue chroma reduction

Change in hue very noticeable

Change in lightness less so

Reduction in hue is least noticeable

Color space matters (hue uniformity, linearity)

(see talk)

Contrasting colors

Most contrasting color in CSS Color 5

Base color, list of alternates

--myAccent: #b22222;
color-contrast(wheat vs tan, sienna, var(--myAccent), #d2691e);

WCAG 2.1 Contrast = (Ylight + 0.05) / (Ydark + 0.05)

color-contrast(wheat vs tan, #d2691e, oklch(0.3 0.2 64.2) to AA);
 Color   Luminance   Contrast 
    0.482 1.501
    0.137 4.273
    0.107 5.081
    0.305 2.249

Can I Use It ???

Current browser support

Feature Chrome 102 Edge 100 Firefox 100 Safari 15.4
lab(), lch()
oklab(), oklch()
display-p3 canvas
*-gradient(in colorspace )
Relative Color Syntax

Interop 2022

Color spaces and CSS color functions

High Dynamic Range

Need for HDR

Consumer WCG, HDR films, series, news, sports (BT.2100 PQ, HLG)

Consumer HDR gaming consoles

Consumer WCG, HDR-ready TVs commonplace

Consumer WCG (P3) laptops, tablets, phones; HDR coming

WCG, HDR still images coming (AVIF)

High Dynamic Range

and ultra-wide gamut

ITU Rec BT.2020 (2012)
Ultrawide gamut
Monochromatic primaries (630, 532, 467nm)
10 or 12 bits per component
OETF, and non-matching EOTF (gamma 2.4)
D65 white, dim surround
UltraHD (4k, 8k) broadcast, streaming
BT.2020 in practice
Content mostly mastered in DCI-P3
HDMI 2.0 supports BT.2020, 12bit
Content thus delivered in BT.2020 container
For HDR, metadata declares the mastering gamut volume
ITU Rec BT.2100 (2016)
BT.2020 gamut
10 or 12 bits per component
D65 white, dim surround
Hybrid Log Gamma
Scene-referred, relative luminance
Diffuse white at 0.75
2.5 stops highlights
Range of viewing environments (dim to bright)
Brighter displays for brighter environments
Perceptual Quantizer
Reference display referred, absolute luminance
Diffuse white varies (0.54, 0.58, 0.66)?
5.5 stops highlights
Dim viewing environment
Brighter displays for more highlights

HDR in HTML Canvas

Proposal in Color on the Web Community Group

Experimental implementation in Chrome

HDR in HTML Canvas