ΔEOK2 — ΔEOK with the Oklab a and b axes scaled by 2 — agrees with ΔE2000 noticeably better than ΔEOK does (r² 0.722 vs 0.642 on 5000 BT.2020 color pairs). Scaling the a/b axes by 2 is a measurable improvement, supporting Ottosson's recommendation. Repeating the same experiment in Oklrab — Oklab with a revised lightness estimate — gives the same story: ΔEOKr2 (r² 0.732) beats unscaled ΔEOKr (r² 0.613), and edges out ΔEOK2 slightly. The two lightness definitions perform about the same here; this dataset doesn't test viewing conditions (adaptive white), which is what Oklrab's lightness revision specifically targets.
This repo compares the existing ΔEOK with ΔEOK2, a slightly modified version which scales the a and b axes by a factor of 2 as recommended by Björn Ottosson, inventor of Oklab:
Adjust the scaling of a&b to more accurately predict color distances
He also said
I unfortunately didn't spend that much time calculating and validating that scaling factor when I first derived Oklab since I was mostly focused on the orthogonality between L, C and h (and I didn't expect it to become so widespread so quickly), and it seems like it is off by quite a bit. I've recently done some tests with color distance datasets as implemented in Colorio and on both the Combvd dataset and the OSA-UCS dataset a scale factor of slightly more than 2 for a and b would give the best results (2.016 works best for Combvd and 2.045 for the OSA-UCS dataset).
Instead of changing the definition of Oklab, which is now widely adopted, this produces another distance metric ΔEOK2.
This repo examines the improvement this creates; both ΔEOK and ΔEOK2 are evaluated against ΔE2000 on a dataset of color pairs generated in CAM16 (to avoid dependence on Oklab uniformity), and restricted to colors inside the BT.2020 gamut (to avoid any wierd behavior for unrealistic or imaginary colors).
The same comparison is then repeated in Oklrab,
Ottosson's revised version of Oklab which replaces the L axis with a "toe"-adjusted
lightness estimate — intended to predict perceived lightness better under a strong
adaptive white — while keeping the same a/b chroma axes. Applying the same unscaled
and ×2-scaled distance formulas to Oklrab instead of Oklab gives two more metrics,
named here ΔEOKr and ΔEOKr2 by analogy with ΔEOK / ΔEOK2. Oklrab is implemented in
Color.js (as "oklrab"); ΔEOKr and ΔEOKr2 are computed in
this repo (deltaEOKr / deltaEOKr2 in generate-colors.js)
as Color.js has no built-in method for them.
The full pipeline lives in run.js and is seeded, so the dataset
(pairs.csv), regressions and plots reproduce exactly from node run.js.
generateColors() draws N = 1000 base colors in CAM16-JMh, using
Color.js for the color handling and gamut testing:
- J (lightness) ~ Normal(μ = 60, σ = 20)
- M (colorfulness) ~ Normal(μ = 25, σ = 20)
- h (hue) ~ uniform over [0°, 360°)
A color is kept only if it is strictly inside the BT.2020 gamut; the generator resamples until exactly N are collected. Below, the kept colors are shown in the Oklab a–b plane, with the sRGB, P3 and BT.2020 gamut outlines (max-chroma silhouettes across all lightnesses) overlaid:
Because the reachable colorfulness depends on lightness and hue, gamut rejection skews the kept sample slightly: the kept means (J ≈ 57, M ≈ 23) sit a little below the input means. The dashed line marks the input mean.
generatePairs() turns each base color into S = 5 similar colors by
perturbing its coordinates independently, each ~ Normal(μ = base value, σ = 4),
with hue wrapped to [0°, 360°). Again only similar colors inside BT.2020 are
kept, resampling until each base has exactly S — giving N × S = 5000
[base, similar] pairs. This σ = 4 perturbation yields ΔE2000 differences
spanning ~0–16 (mean ≈ 3.9), covering both the small differences of
acceptability testing and the larger ones relevant to gamut mapping.
For every pair the five distances — ΔE2000, ΔEOK, ΔEOK2, ΔEOKr and ΔEOKr2 — are
then computed, the first three with Color.js directly and
the Oklrab-based ΔEOKr / ΔEOKr2 using Color.js's "oklrab" space.
ΔE2000 is generally accepted as the best color-difference metric for SDR colors, but is computationally complex; ΔEOK, by contrast, is computationally simple. The question is how well the simpler metric (and its ΔEOK2 / ΔEOKr / ΔEOKr2 variants) can stand in for ΔE2000.
Taking ΔE2000 as the reference, each candidate metric is fit against it by ordinary least-squares linear regression. The higher the coefficient of determination (r²), the more of the ΔE2000 variation the metric explains — i.e. the better it agrees with ΔE2000.
| Metric | Best-fit line | r² |
|---|---|---|
| ΔEOK | ΔE2000 = 95.10·ΔEOK + 1.03 | 0.642 |
| ΔEOK2 | ΔE2000 = 85.37·ΔEOK2 + 0.47 | 0.722 |
| ΔEOKr | ΔE2000 = 81.79·ΔEOKr + 1.17 | 0.613 |
| ΔEOKr2 | ΔE2000 = 80.22·ΔEOKr2 + 0.45 | 0.732 |
ΔEOK2 tracks ΔE2000 more closely than ΔEOK (r² 0.722 vs 0.642): scaling the a
and b axes by 2 measurably improves agreement with ΔE2000 on this dataset,
supporting Ottosson's recommendation. The same pattern holds in Oklrab:
ΔEOKr2 beats unscaled ΔEOKr (r² 0.732 vs 0.613), by a similar margin to the
Oklab pair. Swapping in Oklrab's lightness doesn't change the picture much —
ΔEOKr is slightly worse than ΔEOK, and ΔEOKr2 is slightly better than ΔEOK2 —
consistent with Oklrab's lightness revision addressing adaptive-white viewing
conditions that this dataset doesn't vary. Full per-pair figures are in
regressions.json.