OkLab Color
OkLab Color — grading that doesn't wreck hue when you touch lightness
- image
- image
OkLab Color is the node you reach for when a normal saturation or contrast slider starts pulling your colors somewhere you didn't ask. Most color math in the wild runs in RGB or HSV, where "make it brighter" quietly shifts hue and makes skin look sickly. This node grades in OkLab, a color space designed (by Björn Ottosson, in 2020) so that lightness, chroma, and hue are genuinely separate axes. Adjust one, the other two hold still. That's not a tuning trick - it's the geometry of the space.
For photorealism work this is quietly important. The standard AI-image advice is "pull saturation down 5–10% and add a subtle cast" without wrecking tones; in OkLab you can do exactly that and keep skin, sky, and foliage where they were. It's also a fine companion to the pack's other grading nodes: use 3-Way Color Balance for creative zone tinting, and OkLab when you want clean perceptual adjustments - especially chroma that stays even across every hue, which RGB saturation is famously bad at.
How it works
Every pixel goes sRGB → linear → OkLab. Lightness is adjusted by a multiplier (L *= lightness), contrast is applied around mid-grey with a slope of 2**contrast, chroma is scaled, hue is rotated as a pure angle, and the a/b tint offsets are added last as a global cast. Then it's converted back. Because OkLab's three axes are roughly orthogonal to human perception, the lightness and contrast passes don't touch chroma or hue - "by construction," as the repo's own derivation doc puts it.
The inputs that matter
- lightness (0–2, default 1) - multiplier on perceived brightness; holds hue and chroma.
- contrast (−1 to 1, default 0) - perceptual contrast around mid-grey.
2**contrastis the slope: 0 is identity, −1 flattens, +1 punches. - chroma (0–2, default 1) - colorfulness, even across all hues. 0 is greyscale, 2 is an aggressive candy push.
- hue (−180 to 180) - global rotation in degrees, holding lightness and chroma. The one that instantly gives you teal-shadows-orange-skin territory without touching anything else.
- tint_a / tint_b (±0.1, default 0) - the color-cast controls: a is green↔red, b is blue↔yellow. Note the range: ±0.1 is already a strong cast, so nudge, don't crank.
- strength (0–1) - blends between the original and the graded result, like every Darkroom node.
Output is image, ready to chain into the next grading node or into a LUT bake (it's color-only, so it's allowed in the bake chain).
Install & notes
It's part of ComfyUI-Darkroom: Manager → search "Darkroom", or git clone https://github.com/jeremieLouvaert/ComfyUI-Darkroom into custom_nodes/, pip install -r requirements.txt, restart. Pure numpy, no GPU, no downloads.
One thing to watch: because the tint range is so small, it's easy to overshoot and then not find your way back. Start at 0, move in 0.01 steps, and trust the strength slider for fine blending rather than tiny cast values. The lightness slider past ~1.2 will push highlights toward clipping in a way that flattens the image - pair it with a Tone Curve if you want to hold detail.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| lightnessopt | FLOAT | 1.000–2 | Perceptual lightness multiplier (L *= lightness). Holds hue and chroma. |
| contrastopt | FLOAT | 0.00-1–1 | Perceptual contrast around mid-grey. slope = 2**contrast; 0 = identity, -1 flatten, +1 punch. |
| chromaopt | FLOAT | 1.000–2 | Chroma (colorfulness) multiplier, even across all hues. 1 = unchanged, 0 = greyscale. |
| hueopt | FLOAT | 0-180–180 | Global hue rotation in degrees. Holds lightness and chroma. |
| tint_aopt | FLOAT | 0.000-0.1–0.1 | Tint along the green↔red axis (a offset). Small range — ±0.1 is a strong cast. |
| tint_bopt | FLOAT | 0.000-0.1–0.1 | Tint along the blue↔yellow axis (b offset). Small range — ±0.1 is a strong cast. |
| strengthopt | FLOAT | 1.000–1 | Blend between original (0) and graded (1) |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |