Nodes/radiance/ACES 2.0 Tonescale
ComfyUI Node

ACES 2.0 Tonescale

The curve that decides what 'correct' looks like

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
ACES 2.0 Tonescale
  • image
  • image
  • curve_info
◄peak_nits100►
◄modeluminance_preserving►
◄contrast_g1.15►
◄grey_target0.100►
◄toe_scene0.040►

What it is

The ACES 2.0 tonescale operator, on its own, with the knobs exposed. Not a full output transform - no gamut mapping, no primaries conversion, no encoding. Just the curve that turns scene-linear luminance into display-referred luminance using the ACES 2.0 reference form.

Why would you want a piece of an output transform as a standalone node? Because it's the part that does all the visible work. If you're building a custom display chain, or you've got an HDR image and you want the ACES 2.0 rolloff without the rest of the pipeline's opinions, this is the only thing you need. It's also the fastest way to see what the ACES 2.0 curve does before you commit a whole graph to it.

It's worth being clear about what it isn't: this is not a "make my image look nicer" tone mapper. It's a specification. Reaching for it to fix a washed-out generation is like using a torque wrench as a hammer.

The mechanism

The curve is a Hill / Michaelis-Menten rational function, which is the analytical form the Academy's reference implementation uses - a smooth S with no hard clip anywhere except the final normalisation. It's parameterised to put 18% scene grey at 10% of peak display output, which is the anchor that makes the whole thing land where a colourist expects it. The curve_info string it outputs includes the computed K constant so you can see it actually solved for your settings.

Two modes, and the difference is real:

  • luminance_preserving (default) tone-maps luma and then scales RGB by the same factor. Hue and saturation survive. This is the one to use unless you have a reason.
  • per_channel runs the curve independently on R, G and B. That can dodge a colour cast on difficult footage but it will shift hues, because three separately-compressed channels no longer sum to the same colour.

Inputs you'll actually touch

image - scene-linear ACEScg (AP1). Same rule as every ACES node in this pack: if you hand it a display-encoded image, you'll get mud.

peak_nits - the display peak in cd/m². 100 is SDR, 1000/2000/4000 are the HDR tiers. This is the single most important setting, because it defines what "white" means for everything the curve does.

mode - as above.

Then the three optional parameters, which are the interesting ones:

  • contrast_g (default 1.15) is the contrast exponent. The ACES 2.0 reference value is 1.15; lower flattens, higher snaps. Note the Full output transform node ties this to the surround choice - Dark 1.10, Dim 1.15, Average 1.20 - so if you're matching a look, this is the dial that decides it.
  • grey_target (default 0.10) is where 18% grey lands as a fraction of peak. Standard is 10 nits out of 100 for SDR. Raise it and mid-tones lift.
  • toe_scene (default 0.04) is the scene luminance below which a linear toe kicks in, which keeps deep shadows from getting lifted by the curve's gamma. If your blacks look foggy, this is why.

Outputs

image and curve_info. Note that the output is normalised and clamped to 0–1 - this is a display-referred result, not scene-linear you can keep grading. It belongs at the end of a chain. If you need to keep working, you're looking for a different node.

curve_info is a one-line summary (peak, g, grey target, toe, K). Useful for logging; not an image.

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: use the bundled python_embeded\python.exe for pip. Dependencies are light for this node - torch and numpy do the work; the heavy libraries in requirements.txt (OpenEXR, OpenImageIO, opencolorio, diffusers) are for the file I/O, colour management and model-driven nodes elsewhere in the pack. No model download is involved here.

Gotchas

  • Feeding it display-encoded input. It'll run, and it'll look wrong. Linearise first.
  • Expecting scene-linear out. It clamps to 0–1. Anything downstream that treats values above 1.0 as valid will be quietly misled.
  • Stacking it with a full output transform. You've tone-mapped twice; highlights will look beige.
  • Reading per_channel as "more accurate". It's not - it's a different trade, and on colourful footage it shifts hue. The tooltip says as much.
  • Forgetting toe_scene exists. Deep-shadow lift is the most common complaint about any tone curve, and this parameter is the fix.
CategoryFXTD STUDIOS/Radiance/HDR

Inputs (6)

NameTypeDefaultDescription
imageIMAGEScene-linear ACEScg (AP1) image batch.
peak_nitsFLOAT10048–10000Display peak luminance in cd/m². 100 = SDR, 1000/2000/4000 = HDR.
modeCOMBOluminance_preservingluminance_preserving: tone-map luma then scale RGB — preserves hue/saturation. per_channel: apply curve independently to R, G, B — may introduce hue shifts but avoids colour casts.
contrast_goptFLOAT1.150.8–1.6Contrast exponent g. ACES 2.0 reference = 1.15.
grey_targetoptFLOAT0.1000.05–0.3Display grey target as fraction of peak. Standard = 0.10 (10 nits of 100 SDR).
toe_sceneoptFLOAT0.0400.005–0.2Scene luminance below which a linear toe is applied. Prevents gamma lift in deep shadows.

Outputs (2)

NameTypeDescription
imageIMAGE—
curve_infoSTRING—