Nodes/radiance/ACES Transform
ComfyUI Node

ACES Transform

The cheap ACES look for previews (and why it isn't a deliverable)

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
ACES Transform
  • image
  • image
  • aces_info
◄odtsRGB D65►
◄exposure_offset0.0►
◄peak_nits1000►
◄saturation1.00►
◄grade_info_in—►

What it is

A filmic tone curve on scene-linear ACEScg, followed by conversion and encoding for a normal display. The author's own description is unusually honest: it's a fitted curve - the Narkowicz ACES approximation, not the Academy RRT - and it's for "quick previews, not deliverables."

That framing is the useful part. If you want to see approximately what an ACES view does to your image without running a full colour-managed output transform, this gets you 90% of the look in a fraction of the fuss, and it will never be accepted as a technically correct transform by anyone who checks. Know which one you're buying.

The mechanism, plainly

Three steps. Input is scaled by 2 ** exposure_offset if you moved the exposure. Then the fitted curve is applied: (x * (2.51x + 0.03)) / (x * (2.43x + 0.59) + 0.14), clamped to 0–1. That's the whole tone map - one rational expression, which is why it's fast and why it isn't reference-accurate. Then the result is converted from ACEScg through XYZ with a D60→D65 adaptation, matriced into the output primaries, and encoded.

Because of that clamp, everything the curve produces is display-referred and in range. You're not protected against anything downstream - you're at the end of the chain.

Inputs

image - scene-linear ACEScg. Display-encoded input gives you a wrong result, as always with this pack.

odt - the target display, and this is where the node's limits show:

  • sRGB D65 applies the sRGB OETF.
  • DCI-P3 D65 uses P3 primaries with a 2.6 gamma.
  • Rec.2020 PQ (HDR10) writes the ST 2084 signal.
  • Rec.2020 HLG writes HLG, with tone-mapped white at signal 1.0.

exposure_offset - stops, applied before the curve. The intuitive one.

peak_nits - the nits that tone-mapped white is encoded at. Only the PQ output uses it; the other three ignore it entirely. The default is 1000.

saturation - a post-curve saturation adjustment around Rec.709-weighted luma. 0 is greyscale, 1 is unchanged, and the result is clamped. Handy for pulling back the over-saturated look models often leave in highlight regions.

grade_info_in is optional and is only recorded as an "upstream" field in the info output. It does not change the image. It exists so a chain of grade nodes can carry a provenance trail.

Outputs

image and aces_info. The string is JSON - node name, the ODT chosen, exposure, peak nits, saturation, plus whatever upstream grade metadata you wired in. If you're building a graph that documents itself, feed it forward; if you only want pixels, ignore it.

When it's the right call

Grading an HDR batch and wanting a fast view. Previewing what a PQ or HLG deliverable will approximately look like before committing to the Full output transform. Making a "filmic" version of something for a client note where nobody will measure it. Rough exposure checks - flip exposure_offset and watch.

When it's the wrong call: any delivery where the transform is specified. Use ACES 2.0 Output Transform there. The Narkowicz fit diverges from the ACES 2.0 reference curve, most visibly in the toe and in how it handles very bright values, and the whole point of a specified transform is that it's specified.

Install

ComfyUI Manager → search Radiance → install → restart → refresh the browser. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt

Windows portable users should run that pip command with ComfyUI's bundled python_embeded\python.exe. This node is pure torch, so if it errors you're looking at an install-level problem, which is the usual ComfyUI custom-node injury: every pack installs into the same Python environment, there is no isolation, and one pack's transformers pin can break another's. If Radiance loads but other packs don't afterwards, that's why.

Gotchas

  • Reading the tooltip as marketing rather than a warning. "Use for quick previews, not deliverables" means exactly that.
  • Tuning peak_nits on the sRGB output. It does nothing there.
  • Expecting scene-linear output. It's clamped; the curve is the last thing that happens.
  • Wiring grade_info_in and expecting a visible change. Metadata only.
  • Confusing it with the Full node. Similar menus (sRGB / P3 / HDR options), completely different maths underneath.
CategoryFXTD STUDIOS/Radiance/HDR

Inputs (6)

NameTypeDefaultDescription
imageIMAGEScene-linear ACEScg image.
odtCOMBOsRGB D65Target display. sRGB: sRGB curve. DCI-P3 D65: P3 primaries, 2.6 gamma. Rec.2020 PQ: ST 2084 signal. Rec.2020 HLG: HLG signal, tone-mapped white at signal 1.0.
exposure_offsetFLOAT0.0-4–4Exposure change in stops applied to the scene-linear input before the tone curve.
peak_nitsFLOAT1000100–10000Luminance in nits that tone-mapped white (1.0) is encoded at. Only used by Rec.2020 PQ (HDR10); ignored by the other outputs.
saturationFLOAT1.000–1.5Saturation after the tone curve, around Rec.709-weighted luma: 0 = greyscale, 1 = unchanged. Result is clamped to 0..1.
grade_info_inoptSTRINGOptional grade_info JSON from an upstream node. Only recorded as "upstream" in this node's info output; it does not change the image.

Outputs (2)

NameTypeDescription
imageIMAGE—
aces_infoSTRING—