Hello,
While working on the ACES 2.0 module, I discovered a bug in the SSTS curve.
The SSTS normalization used / n (peak luminance) instead of / n_r (reference luminance, 100 nits). This inverted the peak ↔ brightness relationship: increasing the peak made the image darker instead of giving more highlight headroom. The behavior is now aligned with ACES 2.0 (ssts_fwd(x) / n_r).
New sliders (replacing the old “perceptual brightness”)
| Slider | Role | Range |
|---|---|---|
| Input exposure | Pre-SSTS exposure in EV | −2 to +2 EV, default 0 |
| Peak luminance | SSTS peak luminance (curve character) | −100% to +100%, 0% = 200 nits (ACES 2.0 ref.) |
Peak is decoupled from exposure: Peak luminance controls the curve roll-off (higher peak = softer compression), Input exposure controls exposure independently.
Abney rotation — new formulation
The old hl_hue_shift used an arbitrary luminance threshold (st_desat_weight based on y_exposed). The rotation was often imperceptible.
New approach: the rotation angle is tied to the actual SSTS compression ratio:
compression = min(y_tm / (y_abs × EV), 1.0) // 0 = heavily compressed, 1 = linear
weight = pow(1.0 - compression, exponent)
exponent = max(1.0 − |hl_hue_shift|, 0.1)
angle = hl_hue_shift × 1.0 × weight
hl_hue_shift = 0: no rotationhl_hue_shift > 0: rotation increases with compression, smooth transitionhl_hue_shift = +1.0: progressive rotation from compressed highlights toward midtones
Out-of-gamut yellows are automatically chroma-clamped (rotation scaled to stay within [0,1]) with no hue artifacts.
I’ll take a look tonight or tomorrow at the bug you’re having with the AGX slider.
Have a nice day,
Christian,
P.S. I won’t be talking about soccer today, ![]()