Abstract color conversion methodology workspace with connected color-space panels, channel bars, gamut diagrams, coordinate grids, swatches, and rounding controls.

Short answer

Hue Codex distinguishes the authored input from the resolved display value. If a user enters Lab, LCH, OKLab, or OKLCH, the converter preserves that authored value for source reporting and gamut notes, then resolves a displayable sRGB HEX fallback for contrast checks, palettes, exports, swatches, and other shared downstream logic. Display precision changes what users see, not the internal math used for decisions.

  • Accepted colors are resolved to an opaque sRGB HEX fallback before shared downstream tool logic runs.
  • Authored Lab/LCH/OKLab/OKLCH values are preserved as source metadata in conversion and gamut messaging, not as the canonical value for every downstream calculation.
  • HSL, HSV, and HWB are RGB-derived models; Lab/LCH and OKLab/OKLCH are perceptual or device-independent views of the same resolved color.
  • Lab and LCH use D50 Lab after adapting from sRGB D65 XYZ. OKLab and OKLCH use D65 XYZ.
  • Out-of-range RGB results are clamped when converted back to displayable sRGB.

Standards status

These badges identify which parts of this methodology are standards-backed, draft-track, Hue Codex-specific, approximate, or dependent on browser behavior.

Standard Draft standard Browser-dependent
Standard Stable standard or standards-backed behavior used as authority, such as WCAG 2.2 and broadly implemented CSS Color 4 behavior.
Draft standard Draft, candidate, or preview specification material, such as CSS Color 5 features or the Design Tokens draft; not treated as final authority.
Browser-dependent Output depends on browser APIs, rendering, color management, canvas pixels, clipboard, download, or CSS support.

Formulas, choices, heuristics, and limits

This separates standards-based formulas from Hue Codex implementation decisions, product heuristics, and known limitations for this methodology.

Standards-based formulas

Formula, threshold, syntax, or data behavior taken from a cited standard or standards-backed source.

  • CSS Color 4 color-space definitions, sRGB linearization, XYZ conversion context, and Lab/LCH or OKLab/OKLCH coordinate behavior.
  • Stable CSS color syntax for HEX, rgb(), hsl(), hwb(), lab(), lch(), oklab(), and oklch() where supported by the tool.

Implementation choices

How Hue Codex chooses to parse, normalize, round, export, or sequence calculations.

  • Hue Codex distinguishes authored input from the resolved fallback: the authored Lab/LCH/OKLab/OKLCH string is preserved for source reporting in conversion tools, while shared downstream calculations use a displayable six-digit uppercase sRGB HEX fallback.
  • Lab and LCH are calculated through D65 to D50 adaptation; OKLab and OKLCH use D65 XYZ.
  • Precision modes affect output strings, while internal checks keep raw calculation values when thresholds matter.

Hue Codex heuristics

Product rankings, bands, labels, suggestions, or role hints that are useful guidance but not external standards.

  • Compact, readable, token, and detailed precision presets are Hue Codex output choices.
  • Fallback and summary wording are chosen to make handoff safer, not because a standard requires that exact wording.

Known limitations

Caveats, edge cases, browser dependencies, approximations, or contexts the method does not prove.

  • Converting an out-of-gamut perceptual color back to sRGB can clamp channels and hide lost chroma.
  • Browser rendering, color management, and unsupported notation can change what users actually see.
  • CSS Color 5 references are draft context and should not be treated as final standards authority.

Inputs and parsing

Standard Browser-dependent

Hue Codex accepts common CSS-style color notation, including short and long HEX, rgb(), hsl(), hwb(), lab(), lch(), oklab(), and oklch() forms where the tool exposes typed input. Unsupported or incomplete input is rejected instead of guessed.

Alpha values can be displayed or exported by tools that ask for alpha, but most color-model comparison and conversion outputs resolve the base color as an opaque sRGB value.

Authored source versus resolved fallback

Standard Browser-dependent

Canonical sRGB HEX means the resolved display fallback, not necessarily the full authored color. For sRGB-compatible input, the authored value and fallback describe the same color and only notation or rounding changes.

For Lab, LCH, OKLab, or OKLCH input outside sRGB, Hue Codex preserves the authored functional value for the converter source panel, detected-input reporting, gamut warning, and change note. The shared color object then uses the clipped sRGB HEX fallback for swatches, contrast, palette generation, color difference, copied HEX/RGB values, and most exports.

Hue Codex is therefore not claiming that an out-of-gamut authored Lab or OKLCH value survives unchanged through every tool. The preserved authored value explains the source and the loss; the resolved fallback is what most downstream calculations operate on.

How Hue Codex treats authored values and resolved fallbacks.
Situation Preserved as authored input Used for downstream logic
#4169E1 or rgb(65 105 225) Equivalent sRGB value; notation may be normalized #4169E1
In-gamut Lab/LCH/OKLab/OKLCH Original functional value shown in source reporting Equivalent resolved sRGB HEX fallback
Out-of-gamut Lab/LCH/OKLab/OKLCH Original functional value shown with clipping and gamut notes Clipped displayable sRGB HEX fallback
Input alpha Detected and reported where the tool exposes alpha Base color resolves to opaque sRGB unless that tool explicitly composites or exports alpha

Conversion path

Standard
  • HEX is normalized to six uppercase sRGB digits for the canonical display color.
  • sRGB channels are linearized before XYZ, Lab, OKLab, luminance, or contrast math.
  • XYZ D65 is derived from linear sRGB with the exact linear sRGB-to-XYZ D65 matrix listed below.
  • Lab and LCH are calculated from D50 XYZ after linear Bradford chromatic adaptation from D65; reverse conversion uses the D50-to-D65 Bradford matrix.
  • OKLab and OKLCH are calculated from D65 XYZ with the listed XYZ-to-LMS and LMS-to-OKLab matrices, then converted between Cartesian and polar forms for chroma and hue.

Precision and rounding

Hue Codex heuristic

Hue Codex rounds display strings according to the output mode. Compact modes use fewer places for readability; token and detailed export modes preserve more decimal places for repeatable handoff.

Rounding is applied at output boundaries. Tool checks such as contrast pass/fail use the raw values from the calculation path whenever the threshold matters.

Common display precision used across conversion tools.
Model Typical display
RGB, HSL, HSV, HWB Whole or short decimal component values depending on mode
XYZ Up to six or seven decimal places in detailed modes
Lab and LCH Up to five decimal places in detailed modes
OKLab and OKLCH Up to seven decimal places for OK axes or chroma in detailed modes

Gamut and clamping

Standard Browser-dependent

When a perceptual or device-independent value converts back to sRGB, output channels are clamped to the 0 to 255 display range. This makes the color displayable in the browser but can hide the fact that a theoretical color was outside sRGB.

The gamut methodology page explains why this is different from profile-aware color management or print proofing.

Validation checks

Standard Browser-dependent
Representative conversion validation checks.
Input Expected normalized behavior Reason
#fff #FFFFFF Three-digit HEX expands channel pairs
#4169E1 rgb(65 105 225) Royal-blue sRGB channel values
hsl(0 0% 100%) #FFFFFF Neutral white in HSL resolves to white
oklch values outside sRGB Clamped sRGB display color Browser display output must fit sRGB channels

Conversion equations

Standard Browser-dependent

Hue Codex normalizes to sRGB HEX first, then derives the requested color-space views. These equations list the transfer functions, matrices, adaptation steps, and polar conversions used for sRGB, XYZ, D50 Lab/LCH, and D65 OKLab/OKLCH.

sRGB transfer functions and XYZ D65 matrices
# Inputs r8, g8, and b8 are encoded sRGB channels in [0, 255].
srgb_to_linear(c8):
  v = c8 / 255
  if v <= 0.04045: return v / 12.92
  return ((v + 0.055) / 1.055) ^ 2.4

linear_to_srgb_channel(c):
  if c <= 0.0031308:
    encoded = 12.92 * c
  else:
    encoded = 1.055 * (c ^ (1 / 2.4)) - 0.055
  return round(clamp(encoded, 0, 1) * 255)

M_srgb_to_xyz_d65 = [
  [506752/1228815, 87881/245763, 12673/70218],
  [87098/409605, 175762/245763, 12673/175545],
  [7918/409605, 87881/737289, 1001167/1053270],
]

M_xyz_d65_to_srgb_linear = [
  [12831/3959, -329/214, -1974/3959],
  [-851781/878810, 1648619/878810, 36519/878810],
  [705/12673, -2585/12673, 705/667],
]

rgb_to_xyz_d65(r8, g8, b8):
  rgb_linear = [srgb_to_linear(r8), srgb_to_linear(g8), srgb_to_linear(b8)]
  return M_srgb_to_xyz_d65 * rgb_linear

xyz_d65_to_rgb(X, Y, Z):
  rgb_linear = M_xyz_d65_to_srgb_linear * [X, Y, Z]
  return [
    linear_to_srgb_channel(rgb_linear[0]),
    linear_to_srgb_channel(rgb_linear[1]),
    linear_to_srgb_channel(rgb_linear[2]),
  ]
Bradford D65/D50 adaptation, Lab, and LCH
D50 = [0.3457/0.3585, 1, (1 - 0.3457 - 0.3585)/0.3585]
D65 = [0.3127/0.3290, 1, (1 - 0.3127 - 0.3290)/0.3290]

M_d65_to_d50 = [
  [1.0479297925449969, 0.022946870601609652, -0.05019226628920524],
  [0.02962780877005599, 0.9904344267538799, -0.017073799063418826],
  [-0.009243040646204504, 0.015055191490298152, 0.7518742814281371],
]

M_d50_to_d65 = [
  [0.955473421488075, -0.02309845494876471, 0.06325924320057072],
  [-0.0283697093338637, 1.0099953980813041, 0.021041441191917323],
  [0.012314014864481998, -0.020507649298898964, 1.330365926242124],
]

xyz_d65_to_d50(X, Y, Z):
  return M_d65_to_d50 * [X, Y, Z]

xyz_d50_to_d65(X, Y, Z):
  return M_d50_to_d65 * [X, Y, Z]

xyz_d50_to_lab(X, Y, Z):
  Xn, Yn, Zn = D50
  epsilon = 216/24389
  kappa = 24389/27
  f(t) = t > epsilon ? cbrt(t) : (kappa*t + 16)/116
  fx = f(X / Xn)
  fy = f(Y / Yn)
  fz = f(Z / Zn)
  L = 116*fy - 16
  a = 500*(fx - fy)
  b = 200*(fy - fz)
  return L, a, b

lab_to_xyz_d50(L, a, b):
  Xn, Yn, Zn = D50
  epsilon = 216/24389
  kappa = 24389/27
  fy = (L + 16)/116
  fx = a/500 + fy
  fz = fy - b/200
  finv(f) = (f^3 > epsilon) ? f^3 : (116*f - 16)/kappa
  return [Xn*finv(fx), Yn*finv(fy), Zn*finv(fz)]

lab_to_lch(L, a, b):
  C = sqrt(a*a + b*b)
  H = normalize_degrees(atan2(b, a) * 180 / pi)
  if C <= 0.0015: H = undefined
  return L, C, H

lch_to_lab(L, C, H):
  return [L, C*cos(H*pi/180), C*sin(H*pi/180)]
OKLab matrices and OKLCH polar conversion
M_xyz_d65_to_lms = [
  [0.8190224379967030, 0.3619062600528904, -0.1288737815209879],
  [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],
  [0.0481771893596242, 0.2642395317527308, 0.6335478284694309],
]

M_lms_to_oklab = [
  [0.2104542683093140, 0.7936177747023054, -0.0040720430116193],
  [1.9779985324311684, -2.4285922420485799, 0.4505937096174110],
  [0.0259040424655478, 0.7827717124575296, -0.8086757549230774],
]

M_oklab_to_lms_prime = [
  [1.0000000000000000, 0.3963377773761749, 0.2158037573099136],
  [1.0000000000000000, -0.1055613458156586, -0.0638541728258133],
  [1.0000000000000000, -0.0894841775298119, -1.2914855480194092],
]

M_lms_to_xyz_d65 = [
  [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],
  [-0.0405757452148008, 1.1122868032803170, -0.0717110580655164],
  [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816],
]

xyz_d65_to_oklab(X, Y, Z):
  lms = M_xyz_d65_to_lms * [X, Y, Z]
  lms_prime = [cbrt(lms[0]), cbrt(lms[1]), cbrt(lms[2])]
  return M_lms_to_oklab * lms_prime

oklab_to_xyz_d65(L, a, b):
  lms_prime = M_oklab_to_lms_prime * [L, a, b]
  lms = [lms_prime[0]^3, lms_prime[1]^3, lms_prime[2]^3]
  return M_lms_to_xyz_d65 * lms

oklab_to_oklch(L, a, b):
  C = sqrt(a*a + b*b)
  H = normalize_degrees(atan2(b, a) * 180 / pi)
  if C <= 0.000004: H = undefined
  return L, C, H

oklch_to_oklab(L, C, H):
  return [L, C*cos(H*pi/180), C*sin(H*pi/180)]

Reproducible test vectors

Standard Browser-dependent

These vectors validate normalization, RGB-family conversion, Lab conversion, OKLab conversion, and a round trip back to the canonical sRGB HEX value.

Color conversion vectors.
Input Expected output Notes
#fff #FFFFFF Short HEX expands to six uppercase sRGB digits
#4169E1 to RGB rgb(65 105 225) Canonical sRGB channel values
#4169E1 to HSL hsl(225 72.727273% 56.862745%) RGB-derived hue, saturation, and lightness
hsl(225 72.727273% 56.862745%) to HEX #4169E1 HSL round trip returns the source HEX after channel rounding
#4169E1 to HWB hwb(225 25.490196% 11.764706%) RGB-derived whiteness and blackness
#4169E1 to Lab lab(46.800061 17.782586 -66.657521) D65 sRGB converted through D50 Lab
#4169E1 to OKLab oklab(0.559848187 -0.011820607 -0.187862028) D65 XYZ to OKLab

Sources and standards

These references anchor the public standards and formats used by Hue Codex. Status badges distinguish stable standards, drafts, Hue Codex heuristics, approximations, and browser-dependent behavior.

Tools using this methodology

These Hue Codex tools link to this methodology because they depend on the formulas, assumptions, limits, or data policy described here.