01CIE 1931 XYZ
XYZ is the root of colorimetry. It is defined empirically from the CIE 1931 color matching experiments, where human observers matched spectral colors to mixtures of three primaries. The resulting color matching functions x̄(λ), ȳ(λ), z̄(λ) define how to convert any spectral power distribution to a tristimulus value:
X = ∫ S(λ) × x̄(λ) dλ
Y = ∫ S(λ) × ȳ(λ) dλ
Z = ∫ S(λ) × z̄(λ) dλ
Y corresponds directly to luminance. Chromaticity coordinates project out the brightness:
x = X / (X + Y + Z)
y = Y / (X + Y + Z)
Everything else in this document ultimately derives from XYZ.
02sRGB / Linear RGB
IEC 61966-2-1:1999. The standard color space for the web and consumer displays.
Authoritative definition:
The sRGB specification defines the color space in terms of chromaticity coordinates, not matrices. These are the normative values from which all else is derived:
| Primary | x | y |
|---|---|---|
| Red | 0.640 | 0.330 |
| Green | 0.300 | 0.600 |
| Blue | 0.150 | 0.060 |
| White Point (D65) | x | y |
|---|---|---|
| 0.3127 | 0.3290 |
Linear RGB is the intermediate state before the transfer function is applied. It shares these primaries and white point with a linear (identity) transfer function.
Derived XYZ matrices:
The RGB↔XYZ matrices are derived from the chromaticity coordinates above through standard colorimetric calculation. The 4-decimal versions in IEC 61966-2-1 are rounded for presentation to 8-bit precision:
The specification explicitly notes these rounded matrices are insufficient for higher bit depths. For N-bit encoding where N > 8, IEC 61966-2-1 Amendment 1 recommends deriving matrices with additional precision.
On "exact" values:
Some sources incorrectly treat the rounded 4-decimal matrices as the authoritative definition of sRGB. The specification defines sRGB by its chromaticity coordinates; the matrices are derived values, rounded for 8-bit workflows. A matrix that doesn't map (1,1,1) to D65 cannot be the definition of a color space whose white point is D65.
Higher precision matrices (rounded to 7 decimal places for presentation):
Derived from the authoritative chromaticity coordinates:
Note: The 7-decimal inverse matrix in IEC 61966-2-1 Amendment 1 is the inverse of the rounded 4-decimal forward matrix, intended for 16-bit intermediate calculations in 8-bit workflows. It does not recover the precision lost by rounding. What it demonstrates is the amendment's recommendation: invert the exact matrix you use one way to go the other way. Hardware can then use a cheap low-precision unit in one direction and a more precise one in the other to prevent drift (its coefficients are likely not the 8-bit reference values anyway, but whatever fits a limited binary representation; ICC profiles, in s15Fixed16, are further still from the rounded references). The standard accepts matrices that are off, as long as the round trip holds within the final 8-bit precision. The matrices above are derived directly from the chromaticity coordinates and produce bit-identical results at 8-bit depth.
For maximum precision, derive the matrices directly from the chromaticity coordinates, carrying full floating-point precision throughout the calculation.
Derive the matricesDetails
Derived white point XYZ (normalized to Y=1, rounded to 7 decimal places for presentation):
| X | Y | Z |
|---|---|---|
| 0.9504559 | 1.0000000 | 1.0890578 |
Transfer function (encode: linear → sRGB):
if linear ≤ T:
srgb = K × linear
else:
srgb = (1 + a) × linear^(1/γ) − a
Transfer function (decode: sRGB → linear):
if srgb ≤ K×T:
linear = srgb / K
else:
linear = ((srgb + a) / (1 + a))^γ
The linear segment exists because a pure power curve has infinite slope at zero, which would amplify noise in darks.
Transfer function constants:
The curve shape is defined by two primary parameters:
| Constant | Value |
|---|---|
| γ (gamma) | 2.4 |
| a (offset) | 0.055 |
The remaining constants are derived by requiring C¹ continuity (matching both value and slope at the junction between the linear segment and the power curve):
| Constant | Derived Value | Formula |
|---|---|---|
| K (slope) | 12.9232101807... (rounds to 12.92) | (1+a)^γ × (γ−1)^(γ−1) / (a^(γ−1) × γ^γ) |
| y₀ (encoded threshold) | 11/280 = 0.0392857142857... | a / (γ − 1) — exact rational |
| T (linear threshold) | 0.00303993463977... | y₀ / K |
Historical note on the constants:
The 1996 HP/Microsoft sRGB proposal gave K = 12.92 with thresholds T = 0.00304 and y₀ = 0.03928: the derived T rounded, and the encoded threshold following from the rounded slope (12.92 × 0.00304 = 0.03928). The proposal does not say how it obtained them. Rounded this way the slopes still nearly meet, but the two segments miss each other by 1 × 10⁻⁵ in the encoded value. When IEC standardized sRGB in 1999, it kept K = 12.92 and moved the junction to where the line of slope 12.92 crosses the power curve (encoded 0.04045, linear 0.04045 / 12.92 = 0.0031308): the segments now meet to within 3 × 10⁻⁸, but the slope breaks, 12.92 against the curve's 12.70. Neither set is C¹ continuous; each rounding traded one condition for the other, which has no practical effect at 8 bits.
The IEC standard was explicitly designed for 8-bit encoding, where the rounded constants produce correct results. The standard also provides XYZ matrices rounded to 4 decimal places that don't produce the correct D65 white point, with the note that "the advantages of optimising encoding outweigh the disadvantages of this mismatch."
For floating-point implementations, this page uses the mathematically derived constants. These are within specification: a slope of 12.9232 rounds to 12.92, which is the precision the spec defines for derived values. The full-precision values provide:
- Perfect C¹ continuity (no value or slope discontinuity)
- Exact invertibility
- Clean extension to values outside [0, 1]
ICC profiles cannot represent 12.92 exactly anyway; the s15Fixed16 format quantizes 1/K to approximately 1/12.921.
Transfer functions, side by sideDetails
| Curve | u8 in, decoded to linear | Linear, Δ vs derived | Re-encoded with the derived curve | u8 out |
|---|
| Variant | K | T, linear | Encoded threshold | Value gap | Slope gap | Max Δ vs derived | u8 codes exchanged with derived |
|---|
| Curve | Values that no longer return | Largest change | Settles after |
|---|
Luminance coefficients:
The second row of the forward matrix gives the luminance coefficients: Y = 0.2126R + 0.7152G + 0.0722B (or with higher precision as derived).
Round trips, one precision at a timeDetails
Switch the constants to the 8-bit reference values and leave the intermediate at full precision, then run ×1000: the ramp drifts by more than a whole u8 code and most steps would land on another code. Set the intermediate to 8-bit, rounded, and nothing moves however long you run: that is the workflow the reference values are for, and the rounding to a whole code hides the truncation every trip. Then choose 8-bit, error diffusion: the effective precision rises, the rounding no longer hides it, and the drift is back. The derived constants hold still under all three.
03D65 Illuminant
The standard daylight white point used by most display-oriented color spaces. Multiple representations exist due to rounding at different stages of standardization.
D65 (CIE)
Derived from the D65 spectral power distribution and CIE color matching functions. Extended-precision calculations yield values like x = 0.31272, y = 0.32903 (or further: 0.3127205252, 0.3290306850), but these are computed from source data with only 4-5 significant digits of precision. The extra decimal places are computational artifacts, not genuine accuracy.
D65 (sRGB / Rec.709)
The white point defined by IEC 61966-2-1 and ITU-R BT.709:
| x | y |
|---|---|
| 0.3127 | 0.3290 |
These are the authoritative values for sRGB. Any implementation claiming sRGB conformance must use exactly these coordinates when deriving transformation matrices.
Practical impact: Using "more accurate" D65 values from CIE sources (e.g., 0.31272, 0.32903) produces matrices for a subtly different color space that is not sRGB. The sRGB standard's declared values take precedence over external references.
04Gamma 2.2 RGB
No formal standard. Often conflated with sRGB but technically distinct: the same encoded value decodes to slightly different linear values between the two, primarily in darks.
Definition: Linear RGB (sRGB primaries and white point) with γ=2.2.
Transfer function:
encoded = linear^(1/2.2)
linear = encoded^2.2
05Y'CbCr (Rec.709)
A luma-chroma color space used for video transmission and compression. Separates a brightness-like channel (Y') from two color difference channels (Cb, Cr).
Definition: Operates on gamma-encoded R′G′B′, not linear light. BT.709 encodes with its own transfer function (4.5 L below 0.018, 1.099 L0.45 − 0.099 above), not the sRGB curve; applying the same matrix to sRGB-encoded values, which share the primaries and white point, is common practice in computer graphics, and it is how this page's labs use it.
The prime notation (Y') indicates gamma-encoded. This is not a linear luminance measurement.
Forward transform (R′G′B′ → Y'CbCr):
Y' = 0.2126 R' + 0.7152 G' + 0.0722 B'
Cb = (B' - Y') / 1.8556
Cr = (R' - Y') / 1.5748
Or as a matrix:
Y' ranges [0, 1], Cb and Cr range [−0.5, 0.5].
Inverse transform (Y'CbCr → R′G′B′):
R' = Y' + 1.5748 Cr
G' = Y' - 0.1873 Cb - 0.4681 Cr
B' = Y' + 1.8556 Cb
BT.709 states the forward transform with those four-decimal coefficients and divisors; they are its 8-bit reference values. The matrix and the inverse coefficients are derived and rounded to 4 decimal places for presentation. At full precision everything here follows from the luminance coefficients, the Y row of the derived Linear RGB → XYZ matrix: the divisors are 2(1 − KB) and 2(1 − KR), and the G' coefficients are those scaled by KB/KG and KR/KG.
Y′CbCr color pickerDetails
On the coefficients:
The coefficients (0.2126, 0.7152, 0.0722) are derived from the second row of the Linear RGB → XYZ matrix. When applied to Linear RGB, they yield true CIE luminance, the Y in XYZ:
Y = 0.2126 R + 0.7152 G + 0.0722 B
This is correct for linear light. Y'CbCr takes these coefficients and applies them to gamma-encoded values instead, which is physically incorrect but was standardized for video.
Caveats:
CRT gamma is a physical property of electron guns, not a perceptual encoding. The fact that γ≈2.2 vaguely resembles perceptual response is a coincidence. Actual perceptual lightness uses curves like CIELAB's L* or OKLab's cube root, derived from psychophysical experiments.
Y'CbCr exists because video signals were always gamma-encoded for CRT displays, and the standards were built around that. The common form, above, takes its luma from encoded values. BT.2020 also defines a constant luminance form, Y′CC′BCC′RC, whose luma is the luminance computed from linear R, G, B and then encoded. If you need actual luminance, linearize first or use XYZ's Y channel.
Note on Rec.601 coefficients:
The classic "30/59/11" formula:
Y' = 0.299 R' + 0.587 G' + 0.114 B'
These coefficients originate from the original NTSC 1953 color space (BT.470 System M) with Illuminant C white point, not from the modern BT.601 color spaces, which use D65.
| Source | KR | KG | KB | White Point |
|---|---|---|---|---|
| NTSC 1953 | 0.2989 | 0.5866 | 0.1144 | Illuminant C |
| Legacy Y'CbCr | 0.299 | 0.587 | 0.114 | (rounded from above) |
| BT.601-625 (PAL) | 0.222 | 0.707 | 0.071 | D65 |
| BT.601-525 (NTSC) | 0.212 | 0.701 | 0.087 | D65 |
The legacy values were carried forward for backward compatibility, even though BT.601 redefined the primaries and white point.
Y'CbCr and JPEG:
JPEG (JFIF) always uses BT.601 Y'CbCr for compression (the same coefficients, at full range: Y′ from 0 to 255 and Cb, Cr centered on 128, not BT.601's 16 to 235 and 16 to 240), but the input RGB can come from different color spaces (sRGB, ProPhoto, Display P3, etc.). The ICC profile declares which RGB space to interpret the decoded values as, while the Y'CbCr compression stage remains BT.601 throughout. This effectively creates non-standard Y'CbCr color spaces: the BT.601 transform applied to a different RGB base than it was designed for.
06Apple RGB
A legacy color space from classic Macintosh systems (pre-OS X era). The primaries were based on the phosphors in Macintosh CRT monitors. The γ=1.8 transfer function was chosen to approximate the dot gain of Apple LaserWriter printers.
Authoritative definition:
| Primary | x | y |
|---|---|---|
| Red | 0.6250 | 0.3400 |
| Green | 0.2800 | 0.5950 |
| Blue | 0.1550 | 0.0700 |
| White | 0.3127 | 0.3290 |
| Constant | Value |
|---|---|
| γ (gamma) | 1.8 |
Transfer function:
encoded = linear^(1/1.8)
linear = encoded^1.8
Derived XYZ matrices (rounded to 7 decimal places for presentation):
Full conversion (Apple RGB → XYZ):
- Decode: linear = encoded^1.8
- Matrix multiply
Converting Apple RGB to sRGB:
Because the primaries differ, gamma adjustment alone is incorrect. The correct path:
- Decode Apple RGB (γ=1.8)
- Matrix: linearized Apple RGB → XYZ
- Matrix: XYZ → Linear RGB
- Encode sRGB (piecewise function)
Apple RGB to sRGBDetails
| Step | Values |
|---|
07CIELAB
CIE 1976 (L*a*b*). A perceptual color space defined in terms of XYZ.
CIELAB requires a reference white (Xn, Yn, Zn) but does not specify which illuminant to use; that is application-dependent. Common choices:
- D50 is standard for ICC profiles and print workflows
- D65 is used for display-oriented workflows
Mixing reference whites produces incorrect results.
Definition (XYZ → Lab):
L* = 116 × f(Y/Yn) − 16
a* = 500 × (f(X/Xn) − f(Y/Yn))
b* = 200 × (f(Y/Yn) − f(Z/Zn))
where:
f(t) = t^(1/3) if t > δ³
f(t) = t / (3δ²) + 4/29 otherwise
The constant δ = 6/29, giving:
- Threshold: δ³ = (6/29)³ = 216/24389 ≈ 0.008856
- Linear slope: 1/(3δ²) = (29/6)²/3 = 841/108 ≈ 7.787
- Linear offset: 4/29 ≈ 0.137931
Inverse (Lab → XYZ):
Y = Yn × f⁻¹((L* + 16) / 116)
X = Xn × f⁻¹((L* + 16) / 116 + a*/500)
Z = Zn × f⁻¹((L* + 16) / 116 − b*/200)
where:
f⁻¹(t) = t³ if t > δ
f⁻¹(t) = 3δ²(t − 4/29) otherwise
08CIELAB Color Difference Formulas
CIELAB defines a color space, but measuring the "distance" between two colors requires a separate formula. Three standard formulas exist, each improving on the previous:
CIE76 (ΔE*ab)
The original CIELAB color difference formula (CIE 1976). Simple Euclidean distance in L*a*b* space.
Definition:
ΔE*ab = √((L₂* - L₁*)² + (a₂* - a₁*)² + (b₂* - b₁*)²)
Constants: None. This is pure Euclidean distance.
Limitations: CIELAB is not perfectly perceptually uniform. CIE76 over-weights chromatic differences relative to lightness differences, especially for saturated colors. Equal ΔE values do not represent equal perceptual differences across the color space.
CIE94 (ΔE*94)
CIE 1994 color difference formula. Introduces weighting factors that scale chromatic differences based on chroma magnitude, improving perceptual uniformity.
Definition:
ΔE*94 = √((ΔL*/(kL·SL))² + (ΔC*/(kC·SC))² + (ΔH*/(kH·SH))²)
Where:
- ΔL* = L₂* - L₁*
- C₁* = √(a₁*² + b₁*²)
- C₂* = √(a₂*² + b₂*²)
- ΔC* = C₂* - C₁*
- ΔH* = √((a₂* - a₁*)² + (b₂* - b₁*)² - ΔC*²)
Primary constants (graphic arts application):
| Constant | Value | Description |
|---|---|---|
| kL | 1 | Lightness weighting (2 for textiles) |
| kC | 1 | Chroma weighting |
| kH | 1 | Hue weighting |
| K₁ | 0.045 | Chroma scaling coefficient |
| K₂ | 0.015 | Hue scaling coefficient |
Weighting functions:
SL = 1
SC = 1 + K₁ × C₁* = 1 + 0.045 × C₁*
SH = 1 + K₂ × C₁* = 1 + 0.015 × C₁*
Note: CIE94 uses the first color's chroma (C₁*) as the reference for weighting. This makes the formula asymmetric: swapping the two colors may yield a different result. For consistent results, use the reference/standard color as color 1.
CIEDE2000 (ΔE₀₀)
CIE 2000 color difference formula. The most accurate perceptual color difference metric, incorporating corrections for:
- Lightness dependence
- Chroma dependence
- Hue dependence
- Interaction between chroma and hue differences
- Hue rotation term for the blue region
Definition:
ΔE₀₀ = √((ΔL'/(kL·SL))² + (ΔC'/(kC·SC))² + (ΔH'/(kH·SH))² + RT·(ΔC'/(kC·SC))·(ΔH'/(kH·SH)))
Primary constants:
| Constant | Value | Description |
|---|---|---|
| kL | 1 | Lightness parametric factor |
| kC | 1 | Chroma parametric factor |
| kH | 1 | Hue parametric factor |
| 25⁷ | 6103515625 | Chroma correction threshold (exact) |
Step 1 — Calculate CIELAB chroma and mean chroma:
C₁* = √(a₁*² + b₁*²)
C₂* = √(a₂*² + b₂*²)
C̄* = (C₁* + C₂*) / 2
Step 2 — Calculate G factor (a* axis adjustment):
The G factor stretches the a* axis to improve uniformity for low-chroma colors.
G = 0.5 × (1 - √(C̄*⁷ / (C̄*⁷ + 25⁷)))
Step 3 — Adjusted a' values and chroma:
a₁' = a₁* × (1 + G)
a₂' = a₂* × (1 + G)
C₁' = √(a₁'² + b₁*²)
C₂' = √(a₂'² + b₂*²)
Step 4 — Hue angles (in degrees, 0-360):
h₁' = atan2(b₁*, a₁') mod 360° (undefined if C₁' = 0)
h₂' = atan2(b₂*, a₂') mod 360° (undefined if C₂' = 0)
Step 5 — Differences:
ΔL' = L₂* - L₁*
ΔC' = C₂' - C₁'
For Δh' (hue difference):
If C₁'×C₂' = 0: Δh' = 0
Else if |h₂'-h₁'| ≤ 180°: Δh' = h₂' - h₁'
Else if h₂'-h₁' > 180°: Δh' = h₂' - h₁' - 360°
Else: Δh' = h₂' - h₁' + 360°
ΔH' = 2 × √(C₁'×C₂') × sin(Δh'/2)
Step 6 — Mean values:
L̄' = (L₁* + L₂*) / 2
C̄' = (C₁' + C₂') / 2
For h̄' (mean hue):
If C₁'×C₂' = 0: h̄' = h₁' + h₂'
Else if |h₁'-h₂'| ≤ 180°: h̄' = (h₁' + h₂') / 2
Else if h₁'+h₂' < 360°: h̄' = (h₁' + h₂' + 360°) / 2
Else: h̄' = (h₁' + h₂' - 360°) / 2
Step 7 — T factor (hue-dependent weighting):
T = 1 - 0.17×cos(h̄' - 30°) + 0.24×cos(2h̄') + 0.32×cos(3h̄' + 6°) - 0.20×cos(4h̄' - 63°)
Angles in the T factor, in radians:
| Angle (degrees) | Radians (derived) |
|---|---|
| 30° | π/6 = 0.52359877559829887307... |
| 6° | π/30 = 0.10471975511965977461... |
| 63° | 7π/20 = 1.09955742875642894663... |
Step 8 — SL, SC, SH weighting functions:
SL = 1 + (0.015 × (L̄' - 50)²) / √(20 + (L̄' - 50)²)
SC = 1 + 0.045 × C̄'
SH = 1 + 0.015 × C̄' × T
Primary constants in weighting functions:
| Constant | Value | Appears in | Notes |
|---|---|---|---|
| K₁ | 0.045 | SC | Shared with CIE94 |
| K₂ | 0.015 | SL, SH | Shared with CIE94 |
| L* midpoint | 50 | SL | Perceptual midpoint |
| SL denom offset | 20 | SL | Empirical constant |
Step 9 — RT rotation term (blue region correction):
Δθ = 30° × exp(-((h̄' - 275°)/25°)²)
RC = 2 × √(C̄'⁷ / (C̄'⁷ + 25⁷))
RT = -sin(2Δθ) × RC
Constants as they appear in the formula:
| Expression | Value |
|---|---|
| 25⁷ | 6103515625 (exact) |
| 275° in formula | 275 (exact, degrees) |
| 25° in formula | 25 (exact, degrees) |
| 30° in Δθ | 30 (exact, degrees) |
Step 10 — Final calculation:
With kL = kC = kH = 1:
ΔE₀₀ = √((ΔL'/SL)² + (ΔC'/SC)² + (ΔH'/SH)² + RT×(ΔC'/SC)×(ΔH'/SH))
Comparison of CIELAB Distance Formulas
| Formula | Year | Complexity | Perceptual Accuracy | Use Case |
|---|---|---|---|---|
| CIE76 | 1976 | O(1), trivial | Poor for saturated colors | Fast approximation |
| CIE94 | 1994 | O(1), simple | Good | General purpose |
| CIEDE2000 | 2000 | O(1), complex | Best available | Color-critical applications |
For dithering applications, CIE94 offers a good balance of accuracy and computational cost. CIEDE2000 is more expensive due to trigonometric functions but provides the most accurate perceptual distance. OKLab with Euclidean distance is a simpler alternative, but it was fitted to lightness, chroma and hue data rather than to color difference data; the lab below shows how often the measures disagree.
Measure a color differenceDetails
09OKLab
Designed by Björn Ottosson (2020). A perceptual color space fitted to color pairs of equal lightness and of equal chroma generated with CAM16, and to the uniform hue data used to derive IPT.
The published matrices are the definition used here, as in CSS Color 4: the OKLab → LMS′ and LMS → Linear RGB matrices, printed to ten decimals, are authoritative, together with the cube root, and the RGB → OKLab direction is their exact inverse. Ottosson fitted the space in XYZ and computed the Linear RGB matrices from that fit, with a white-point correction that makes them a slightly different space from the XYZ one; the derivation is at the end of this section. The published values as they stand are sufficient for single precision math: the printed matrices in either direction, their exact inverses and CSS Color 4's matrices give the same OKLab values to within 4 × 10⁻⁸, below what a 32-bit float resolves. The distinction only reaches a double precision pipeline, and there only as drift over repeated round trips.
Forward (Linear RGB → OKLab):
Step 1 — Approximate cone response:
Step 2 — Cube root:
l' = ∛l
m' = ∛m
s' = ∛s
Step 3 — Opponent channels:
Definition (OKLab → Linear RGB):
Step 1:
Step 2:
l = l'³
m = m'³
s = s'³
Step 3:
OKLab color pickerDetails
How the published matrices were made:
Ottosson's post defines OKLab from XYZ with a D65 white: M₁ to an approximate cone response, the cube root (fitted at 0.323 and fixed at 1/3), and M₂ to the opponent channels, with D65 constrained to L = 1, a = b = 0. The fit ran in single precision, and every printed digit of M₁ and M₂ is a 32-bit float. Here they are intermediates: the matrices in use were computed from them.
M₁ maps XYZ (0.95047, 1, 1.0883) to l = m = s = 1. That white has Z = 1.0883, not the usual D65 value of 1.08883, and it is not the sRGB white (0.9504559, 1, 1.0890578). So the Linear RGB matrices carry a white-point fit. The current ones (2021, "derived using a higher precision sRGB matrix and with exactly matching D65 values") reproduce, all eighteen digits, as:
- the sRGB Linear RGB → XYZ matrix derived from the chromaticities, rounded to 8 decimals (0.41239080, 0.35758434, 0.18048079, …),
- multiplied by M₁,
- each row divided by its sum, so that the sRGB white lands exactly on l = m = s = 1: a von Kries step from M₁'s white to the sRGB white,
- and inverted exactly for LMS → Linear RGB; both rounded to ten decimals.
Going through XYZ with M₁ instead puts the sRGB white at a = −0.000022, b = −0.000123, and differs from the published matrices by up to 1.25 × 10⁻⁴ in OKLab: the fit is part of the space. The matrices published before 2021 were M₁ times the inverse of IEC 61966-2-1's 4-decimal XYZ → sRGB matrix, in single precision (still linked from the post).
M₂ and its published inverse are single precision too, and both round from one exact pair that meets the white constraint. Correct each row of M₂ to sum to exactly 1, 0, 0 (the printed rows are off by 6.5 × 10⁻⁹ and 3.7 × 10⁻⁸), spreading the difference in proportion to the entries' sizes: the result rounds to the printed M₂ in all nine single precision values, and its exact inverse, whose first column is exactly 1, to the published OKLab → LMS′ matrix in all nine. Inverting either printed matrix as it stands does not reproduce the other, and the numbers cannot tell which was computed first.
CSS Color 4 takes the published OKLab → LMS′ matrix as exact, with LMS′ → Lab its exact inverse, and re-derives its XYZ → LMS matrix for its own D65 white. This page takes the same published matrix, and the published LMS → Linear RGB matrix, as its definition.
10OKLab Lr (Reference Lightness)
A later refinement by Björn Ottosson (2021). Plain OKLab's L does not track CIELAB's L*: for a gray, L is the cube root of the luminance, which sits above L*/100 everywhere below white (0.565 against 0.495 at 18% gray) and rises steeply near black, where L* has a linear segment. Lr replaces L with a toe-adjusted lightness that follows CIELAB's L* (normalized to [0, 1]) closely, 0.495 at 18% gray, while leaving a and b untouched.
This affects only the lightness scale. The a/b opponent channels, the matrices, and the cube root are identical to plain OKLab. Lr is purely a remapping of L, applied as a final step (and undone as a first step on the inverse).
Constants:
k₁ = 0.206
k₂ = 0.03
k₃ = (1 + k₁) / (1 + k₂)
Toe (L → Lr):
Lr = 0.5 × (k₃·L − k₁ + √((k₃·L − k₁)² + 4·k₂·k₃·L))
Inverse toe (Lr → L):
L = (Lr² + k₁·Lr) / (k₃·(Lr + k₂))
The toe function is monotonic on [0, 1], fixes both endpoints (toe(0) = 0, toe(1) = 1), and inverts in closed form as shown above, so no iteration is required. Use Lr when perceptual lightness should agree with CIELAB L*; use plain OKLab L when matching reference implementations that omit the toe.
Which lightness scale?Details
11Dependency Graph
One color through the graphDetails
12Summary
| Space/Formula | Authoritative Definition | Derived |
|---|---|---|
| CIE XYZ | Color matching functions (empirical) | — |
| Linear RGB / sRGB | Chromaticity coordinates (primaries + D65 white point) | XYZ matrices |
| sRGB | Linear RGB + transfer function (γ=2.4, a=0.055) | K, T (slope and threshold) |
| Gamma 2.2 RGB | Linear RGB + γ=2.2 | — |
| Y'CbCr (Rec.709) | BT.709 R′G′B′ + forward transform (applied to sRGB-encoded values in practice) | Matrix form, inverse transform |
| sYCC | sRGB + BT.601 matrix (legacy) | — |
| Apple RGB | Chromaticity coordinates + γ=1.8 | XYZ matrices |
| CIELAB | XYZ + transform equations + reference white | — |
| CIE76 | CIELAB + Euclidean distance | — |
| CIE94 | CIELAB + kL, kC, kH, K₁=0.045, K₂=0.015 | SC, SH |
| CIEDE2000 | CIELAB + kL, kC, kH, 25⁷, K₁, K₂, etc. | G, T, SL, SC, SH, RT |
| OKLab | Linear RGB + published OKLab → LMS′ and LMS → Linear RGB matrices + cube root (as CSS Color 4) | RGB → OKLab matrices (their exact inverses) |
| OKLab Lr | OKLab + toe function (k₁=0.206, k₂=0.03) | k₃ |
AAppendix A: Bradford Chromatic Adaptation
When converting between color spaces with different white points (e.g., ProPhoto RGB at D50 to sRGB at D65), a chromatic adaptation transform is required. The Bradford transform is the industry standard, used by ICC color profiles.
Bradford matrix (XYZ → LMS):
From Lam (1985) and Hunt (1994). This transforms XYZ to a "sharpened" cone-like response space optimized for chromatic adaptation.
Adaptation formula:
To adapt from source white point (Ws) to destination white point (Wd):
M_adapt = BRADFORD⁻¹ × diag(LMS_Wd / LMS_Ws) × BRADFORD
Where LMS_W = BRADFORD × XYZ_W for each white point.
The resulting 3×3 matrix transforms XYZ coordinates directly from one illuminant to another. This can be precomposed with RGB↔XYZ matrices to create direct RGB-to-RGB conversions between spaces with different white points.
Adapt between white pointsDetails
| White in LMS, source to destination | |
| sRGB red, XYZ before and after |
BAppendix B: sRGB Transfer Function History
The sRGB transfer function has a complicated standardization history that explains why different constants appear in different sources.
The Mathematical Derivation
The sRGB transfer function combines a linear segment near black with a power curve for the rest of the range. Given the power curve parameters γ=2.4 and offset a=0.055, the linear segment parameters can be derived by requiring C¹ continuity (continuous value AND continuous slope) at the junction point.
The power curve portion is: f(x) = (1+a)·x^(1/γ) − a
For the linear portion f(x) = K·x to match both value and slope at threshold T:
Value match: K·T = (1+a)·T^(1/γ) − a
Slope match: K = (1+a)/γ · T^(1/γ − 1)
Solving these simultaneously:
K = (1+a)^γ · (γ−1)^(γ−1) / (a^(γ−1) · γ^γ) ≈ 12.9232102...
y₀ = a / (γ − 1) = 0.055 / 1.4 = 11/280 ≈ 0.0392857... (encoded threshold, exact rational)
T = y₀ / K ≈ 0.00303993... (linear threshold)
1996: HP/Microsoft Proposal
The original sRGB proposal used γ=2.4 and a=0.055 and gave three constants:
- K = 12.92 (the derived 12.9232... rounded)
- Linear threshold = 0.00304 (the derived 0.0030399... rounded)
- Encoded threshold = 0.03928, which is 12.92 × 0.00304 rounded (the derived 11/280 = 0.0392857... would round to 0.03929)
The proposal does not say how it obtained them; the numbers fit rounding the derived slope and linear threshold and taking the encoded threshold from those two. Much software followed these constants in practice. At that junction the slopes nearly agree (12.92 against the curve's 12.923), but the segments no longer meet: at the threshold the line reaches 0.039277 and the curve 0.039287. The line with slope 12.92 crosses the power curve at encoded 0.038155 and 0.040448, not near 0.03928.
1999: IEC 61966-2-1
The IEC standard changed the thresholds:
- Kept K = 12.92
- Changed thresholds to 0.0031308 (linear) and 0.04045 (encoded)
0.04045 is the upper crossing of the line with slope 12.92 and the power curve (0.040448, rounded), and 0.0031308 is 0.04045 / 12.92. The thresholds come from value continuity with the rounded slope, rather than from γ=2.4 and a=0.055 directly:
- The segments meet to within 3 × 10⁻⁸ (the 1996 constants miss by 1 × 10⁻⁵)
- The slope breaks: 12.92 for the line against 12.70 for the curve at the junction (1996: 12.92 against 12.923)
Neither set satisfies both the value-match and slope-match equations: the 1996 constants nearly keep the slope and give up the value, the IEC constants the reverse. A note W3C later added to the 1996 proposal says that during standardization "a small numerical error caused by rounding error was corrected." The precise fix was at hand: the derived values carried to more digits (K = 12.9232…, T = 0.0030399…, y₀ = 11/280) satisfy both equations at once. The IEC instead kept the rounded slope as if it were a defining constant and recomputed the thresholds from it, moving the junction from 0.00304 to 0.00313 and giving a curve that no longer follows from γ and a alone. The IEC spec was designed for 8-bit encoding, where either set produces correct results.
2021: W3C WCAG
The W3C accessibility guidelines had used 0.03928 (from the 1996 proposal) in their relative luminance formula. In May 2021 they changed it to the IEC's 0.04045, noting that it has no practical effect on the calculations in the context of the guidelines.
ICC Profiles
ICC profiles encode transfer function parameters in s15Fixed16 fixed-point format, which cannot represent 12.92 exactly. The actual stored value corresponds to a slope of approximately 12.921. So "exact compatibility" with ICC profiles is impossible regardless of which constants are chosen.
The Choice on This Page
For floating-point implementations, this page uses the mathematically derived constants (K≈12.9232, T≈0.00304). These values are within specification: the spec only defines derived constants to the precision shown (12.92 for the slope), and 12.9232 rounds to 12.92. Compare a spec that describes a square and lists its sides as 1.51 m and 1.5 m. The 1.5 m cannot mean exactly 1.50 m, because the declared shape tells you the numbers are rounded.
The full-precision values provide:
- Perfect C¹ continuity
- Exact invertibility
- Clean behavior for extended-range values
- Consistency with the original design intent of the transfer function
The IEC standard's rounded constants were pragmatic compromises for 8-bit systems that don't apply to floating-point implementations.