Authoritativedefined by the standard Derivedcomputed from the authoritative values at full precision 8-bit referencethe same derivation, rounded by the standard for 8-bit workflows Intermediatefitted or computed on the way to a published value, shown as history

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.

Black → White
Red → Green
Green → Blue
Blue → Red
Top of each bar: even steps of the sRGB values. Bottom: even steps of linear light, then encoded. Halfway from black to white, the top is code 128, which is 21.6% of the light; the bottom is 50% of the light, which is code 188.

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:

Linear RGB → XYZ (4 decimal, per specification)
XYZ
=
0.41240.35760.18050.21260.71520.07220.01930.11920.9505
×
RGB

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:

Linear RGB → XYZ
XYZ
=
0.41239080.35758430.18048080.21263900.71516870.07219230.01933080.11919480.9505322
×
RGB
XYZ → Linear RGB
RGB
=
3.2409699−1.5373832−0.4986108−0.96924361.87596750.04155510.0556301−0.20397701.0569715
×
XYZ

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
Edit the chromaticities and the matrices follow, at full precision. Load a preset, then try the CIE D65 white point as a counterexample: it does not give sRGB, it gives a different color space.
Linear RGB → XYZ
XYZ → Linear RGB
White, XYZ with Y = 1
derived from the white chromaticity
Derived Linear RGB → XYZ matrix maps (1, 1, 1) to
8-bit reference Linear RGB → XYZ matrix (4 decimals) maps (1, 1, 1) to
sRGB exchanged with the 8-bit reference values, both ways
measuring…
Linear RGB → XYZ, largest difference from the matrix of the authoritative chromaticities

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
The curve derived per the standard at full precision and the two 8-bit references rounded from it overlay almost perfectly; switch to the darks to see the junction, and note how far gamma 2.2 sits from all of them. The table measures what the plot cannot resolve.
Value gap and slope gap compare the linear segment with the power curve at the threshold; both vanish only for the curve derived from γ and a at full precision. The last column decodes all 256 codes with one curve and re-encodes them with the other, both ways: the 8-bit reference constants and the derived ones exchange every 8-bit code unchanged, while their largest difference is a fraction of a u16 code. Gamma 2.2 is a different curve.
The probe starts from a u8 code, the direction an 8-bit workflow takes: decode it with each curve, re-encode with the derived one, and see whether the same code comes back.
Curveu8 in, decoded to linearLinear, Δ vs derivedRe-encoded with the derived curveu8 out
VariantKT, linearEncoded thresholdValue gapSlope gapMax Δ vs derivedu8 codes exchanged with derived
Repeated at full precision, same constants both ways
CurveValues that no longer returnLargest changeSettles after
Full precision encoded values swept across the junction, each decoded and re-encoded repeatedly with one set of constants until it stops changing. With the derived constants every value returns. With the 8-bit reference constants the two segments do not meet, so a narrow band of values lands somewhere else; rounding to 8 bits hides that, a full precision workflow keeps it.

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
One round trip is decode, forward matrix, inverse matrix, encode, run again and again on a dark gray ramp of u8 codes 0 to 63. Linear light is always full precision; you choose the constants, and whether the sRGB intermediate between round trips is kept in full precision or stored as 8-bit. The 8-bit reference values are rounded to a precision that has no effect when every result is rounded to a whole 8-bit code. Error diffusion changes that: it raises the effective bit depth, so that precision stops being enough.
Round trips
0
Largest drift of any step
0
u16 codes
Steps that would now land on another u8 code
0%
when finally converted to u8
At this rate, a whole u8 code of drift after
n/a
round trips
Original rampu8 codes 0 to 63, left to right
After the round trips, converted to u8
Drift, amplified around mid graya tint means the channels drifted apart
Drift of each step, per channel, in u16 codesagainst its starting code; 257 u16 codes = 1 u8 code
Try it

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'CbCr
=
0.21260.71520.0722−0.1146−0.38540.50000.5000−0.4542−0.0458
×
R'G'B'

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
The square is the Cb Cr plane at the chosen Y′, the strip is Y′ at the chosen Cb and Cr, both rendered through the derived inverse transform. Hatched areas are Y′CbCr values outside R′G′B′. Type a hex value to go the other way.
Cb across, Cr up
Y′
sRGB hex
Y′, Cb, Cr
R′G′B′
Share of this Cb Cr square inside R′G′B′

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):

[linearized] Apple RGB → XYZ
XYZ
=
0.44966160.31625610.18453820.24461590.67204430.08333980.02518110.14118580.9226909
×
RGB
XYZ → [linearized] Apple RGB
RGB
=
2.9519785−1.2896043−0.4739153−1.08508361.99080930.03720170.0854722−0.26942971.0910277
×
XYZ

Full conversion (Apple RGB → XYZ):

  1. Decode: linear = encoded^1.8
  2. Matrix multiply

Converting Apple RGB to sRGB:

Because the primaries differ, gamma adjustment alone is incorrect. The correct path:

  1. Decode Apple RGB (γ=1.8)
  2. Matrix: linearized Apple RGB → XYZ
  3. Matrix: XYZ → Linear RGB
  4. Encode sRGB (piecewise function)
Apple RGB to sRGBDetails
Enter an Apple RGB color. The left swatch and the top half of each slider go through the four steps above; the right swatch and the bottom halves only swap the gamma. All are shown as sRGB.
Apple RGB channelstop half correct, bottom half gamma only
R′
G′
B′
StepValues
CIEDE2000, correct against gamma only
Gamut
the swatches clamp out-of-range 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
Give a reference color and two candidates: every measure on this page lands on the candidate it calls closer, and the table says how many times as far the other one is, and they do not always agree. Rows marked × 100 are Euclidean distances in that space. Among 400,000 random references with two candidates within 60 codes per channel, the eight measures disagreed on the closer one 49% of the time. Or compare two colors directly, or load the published CIEDE2000 test pairs and check the implementation against reference data.

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:

lms
=
0.41222147080.53633253630.05144599290.21190349830.68069954510.10739695660.08830246190.28171883760.6299787005
×
RGB

Step 2 — Cube root:

l' = ∛l
m' = ∛m
s' = ∛s

Step 3 — Opponent channels:

Lab
=
0.21045426830.7936177747−0.00407204301.9779985324−2.42859224190.45059370950.02590404250.7827717124−0.8086757549
×
l'm's'

Definition (OKLab → Linear RGB):

Step 1:

l'm's'
=
1.00000000000.39633777740.21580375731.0000000000−0.1055613458−0.06385417281.0000000000−0.0894841775−1.2914855480
×
Lab

Step 2:

l = l'³
m = m'³
s = s'³

Step 3:

RGB
=
4.0767416621−3.30771159130.2309699292−1.26843800462.6097574011−0.3413193965−0.0041960863−0.70341861471.7076147010
×
lms
OKLab color pickerDetails
The square is the a b plane at the chosen lightness, from −0.32 to 0.32 on both axes, and the strip is lightness at the chosen a and b, both rendered through the inverse above and the sRGB encoding. Hatched areas are OKLab values outside sRGB. The strip can run in L or in the Lr of the next section. Type a hex value to go the other way.
a across, b up
L
sRGB hex
L, a, b
sRGB
Share of this a b square inside sRGB

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.

XYZ → LMS (M₁)
lms
=
0.81893301010.3618667424−0.12885971370.03298454360.92931187150.03614563870.04820030180.26436626910.6338517070
×
XYZ
LMS′ → Lab (M₂)
Lab
=
0.21045425530.7936177850−0.00407204681.9779984951−2.42859220500.45059370990.02590403710.7827717662−0.8086757660
×
l'm's'

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:

  1. the sRGB Linear RGB → XYZ matrix derived from the chromaticities, rounded to 8 decimals (0.41239080, 0.35758434, 0.18048079, …),
  2. multiplied by M₁,
  3. 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,
  4. 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
Gray ramps from black to white, plotted as a lightness value against linear luminance. Lr is meant to line up with CIELAB's L*; plain OKLab L does not. Under the plot, each scale is drawn as a gray ramp in even steps of its own value, and the marker shows where the probed gray falls on it. Drag a ramp to move the probe.
Largest gap, L against L*/100
plain OKLab L
Largest gap, Lr against L*/100
with the toe

11Dependency Graph

CIE XYZ (empirical root)CIELABCIE76 (ΔE*ab)CIE94 (ΔE*94)CIEDE2000 (ΔE₀₀)Linear RGBsRGBY'CbCr (Rec.709)Gamma 2.2 RGBOKLabOKLab Lr (toe remap of L)Apple RGB
Each box links to its section. Teal is the RGB family, blue is CIELAB, amber is the difference formulas.
One color through the graphDetails
Pick a color and follow it through every space on this page, at full precision.
Chromaticity diagram, CIE 1931 xyyour color is the dot
sRGB primariesApple RGB primariesD65 white pointoutline: CIE 1931 2° spectral locus

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):

LMS
=
0.89510.2664−0.1614−0.75021.71350.03670.0389−0.06851.0296
×
XYZ

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
The Bradford matrix above, applied to real white points. D50 is the ICC profile connection space white (0.9642, 1, 0.8249); D65 is the sRGB white derived from its chromaticities.
Adaptation matrix, XYZ to XYZ
Source white
Destination white
Check
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.