Nodes/radiance/◎ Radiance Expand Dynamic Range
ComfyUI Node

◎ Radiance Expand Dynamic Range

The honest version

By fxtdstudios·Created 7 months ago·Updated 9 days ago· 242
◎ Radiance Expand Dynamic Range
  • image
  • image
source_gamma2.2
highlight_recovery1.0
black_point0.000
target_stops14.0
highlight_rolloff1.5

Here's the pitch that gets everyone excited: take a regular 8-bit image, run it through a node, and get back an HDR image with 14 stops of dynamic range. HDRExpandDynamicRange is that node, and the truth it ships with is more interesting than the pitch. It expands - it rebuilds highlight headroom above white and stretches the tonal range into a proper float image - but it cannot create detail that was never there. What it does instead is give you back the latitude an HDR file is supposed to have, so the image you thought was "blown" actually has somewhere to go in the grade.

Read the README's warning once and you'll never be surprised again: it "cannot recover highlight or shadow detail that was already clipped in an 8-bit source image." That's physics, not a bug. This node is for taking an image that is soft-clipped - highlights gently rolling off at 1.0 instead of being sheared off - and giving those values room to breathe above white.

How it works

The mechanism is a luminance-driven quadratic expansion (from the source): it linearizes (using source_gamma), isolates luma with Rec.709 weights, then applies a curve above a threshold - values below the threshold keep their character, values above it get pushed up a quadratic ramp toward a target peak. target_stops sets that peak: 14 stops maps to 2^(14−8) = 64× above white. highlight_rolloff controls where the expansion starts (higher rolloff = softer, starts earlier). The new luminance is then re-applied to the RGB channels by ratio, so color relationships survive, and highlight_recovery blends between the original and expanded curves.

The inputs

  • source_gamma (default 2.2) - the gamma of your source, used to linearize. Feed it linear data and set this to 1.0.
  • highlight_recovery (0–2) - how hard the expansion pushes. 0 = no expansion, 1 = the stated curve, 2 = aggressive.
  • black_point (±0.1) - a tiny floor adjustment before the curve.
  • target_stops (8–20, default 14) - the headline number. Be honest with yourself: 20 stops out of an 8-bit JPEG is theater, not data.
  • highlight_rolloff (1–3) - softness of the expansion threshold.

Single output: image, float32, ready to flow into HDRToneMap or the ACES 2.0 output transform.

Where it fits - and where it doesn't

The honest pipeline: expand your SDR image here, then tone-map it back down with HDRToneMap. The round trip gives you a shoulder - highlights that roll off gracefully instead of clipping - which is most of what makes HDR footage feel like film. If your source is genuinely clipped (hard white, zero texture in the sky), no expander fixes it; that's the case for regenerating or a detail-restoring model, not a curve.

Install

Part of Radiance - ComfyUI Manager → search Radiance, or:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtdstudios/radiance.git
cd radiance
pip install -r requirements.txt

Restart. Under FXTD Studios/Radiance/Color.

CategoryFXTD Studios/Radiance/Color

Inputs (6)

NameTypeDefaultDescription
imageIMAGE
source_gammaFLOAT2.21–3
highlight_recoveryFLOAT1.00–2
black_pointFLOAT0.000-0.1–0.1
target_stopsFLOAT14.08–20
highlight_rolloffFLOAT1.51–3

Outputs (1)

NameTypeDescription
imageIMAGE