Nodes/radiance/SDR to HDR Expand
ComfyUI Node

SDR to HDR Expand

Fake the headroom, don't expect magic

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
SDR to HDR Expand
  • image
  • image
◄inverse_oetfsRGB►
◄threshold0.80►
◄expansion_gain5.0►
◄expansion_gamma1.20►
◄smoothness0.10►

SDR to HDR Expand takes a normal 8-bit-ish SDR image, removes the display curve to get linear light, and pushes the highlights above SDR white. It's maths, not a model - no checkpoint, no download, no waiting. And the node's own description ends with the sentence that matters most: "Does not reconstruct clipped detail."

Nobody sells you that in a product page. What it actually does is expand the headroom above the brightest thing that survived in your image, so a white sky or a specular highlight can sit at something like 1.6 in scene-linear terms and behave like a real HDR source downstream. What it can never do is invent the detail that was clipped off at 8-bit, because that information left the building at capture. If you want learned recovery - actual invented highlight content from an AE-trained model - that's what SDR → HDR Universal or Recover are for, and those run the RUDRA pixel model (non-commercial weights, separate from the code's GPL-3.0).

So where does this node fit? The corpus has a steady trickle of "sdr to hdr" posts, and the KB's flux-2.md ecosystem work reflects how much of 2026's HDR chat is really about pipelines rather than checkpoints. Expand is the honest deterministic option in that stack: instant, no bits hallucinated, useful when your source is clean and you just want the highlights to breathe.

The knobs, and what they actually do

inverse_oetf is first because everything depends on it: sRGB (the default), Rec.709, or None if your input is already linear. Getting this wrong is the classic colour mistake - decode an sRGB image with the Rec.709 camera OETF and your midtones land in the wrong place before any expansion happens. The output stays linear, which is what the rest of Radiance's HDR chain expects.

threshold (default 0.8, 0–1) is linear luminance above which expansion begins. expansion_gain (default 5) is a multiplier on the added highlight energy, and here's the part people misread: luma gains gain × (luma − threshold)^gamma, so with the defaults SDR white rises to roughly 1.6, not 5×. The tooltip says it explicitly, which is unusually kind of it. expansion_gamma (default 1.2) shapes the curve: above 1.0 the expansion starts more gently and adds less, below 1.0 it ramps up fast just past the threshold. smoothness (default 0.1, 0–0.5) is the width of a soft onset at the threshold - a sigmoid on luma, not a spatial blur - so at 0 you get a hard knee and a visible line where expansion starts.

One input, image, and alpha passes through untouched, which matters if you're handing off a matte. One output, image.

The limitation that will annoy you

Straight from the pack's known-issues list, and worth repeating because it's the thing people report as a bug: Expand keeps SDR midtones at their SDR display level. Below the knee the signal stays at 1.0 = 100 nits, with 18% grey at 18 nits. Only the knee-to-white span opens up toward reference white. BT.2446 Method B would also lift the midtones by roughly 2×, so if your HDR deliverable looks flat and dark compared to what a broadcast conversion would give you, that's why. Grade after it, or put a proper tone-mapping node downstream - or just accept that you're expanding headroom, not re-mapping the whole tone scale.

The other honest note: on a 1080p frame, Expand runs in about 0.16 s on CPU. It's basically free. The expensive one in this family is the learned SDR → HDR path, which takes around 19 s per 1080p frame on CPU and wants a GPU for anything longer than a still.

Install

Ships with Radiance. ComfyUI Manager → search Radiance → install → restart → refresh. Or:

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

Expand itself needs nothing beyond numpy and torch - it's the one node in this family with no model attached. The pack's model story is what's heavy: the RUDRA weights (sdr2hdr_shadow_v1.safetensors, and alternatives) download on first use for the learned SDR → HDR nodes and live in ComfyUI/models/radiance/, or you can fetch them by hand from Hugging Face and drop them there. Set RADIANCE_ALLOW_DOWNLOADS=0 before launching ComfyUI to block automatic downloads across the whole pack.

Where to put it in a graph

The README's quick-start chain is a good template: Read → SDR → HDR Universal → Viewer, then Write to EXR. Swap Universal for Expand when you want the fast, deterministic version - same slot, much lower cost, no invented detail. And if you're feeding a video through it, remember the pixel model processes frames independently; Expand is memoryless by construction, so neither path removes flicker. Deflicker downstream if it shows.

CategoryFXTD STUDIOS/Radiance/HDR

Inputs (6)

NameTypeDefaultDescription
imageIMAGESDR image, display-encoded as chosen in inverse_oetf. Alpha, if present, passes through untouched.
inverse_oetfCOMBOsRGBTransfer curve removed before expansion, giving linear light. None = the image is already linear. The output stays linear.
thresholdFLOAT0.800–1Linear luminance (after the inverse OETF) above which expansion begins. 0.8 = 80% of SDR white in linear light.
expansion_gainFLOAT5.01–100Multiplier on the added highlight energy: luma gains gain x (luma - threshold)^gamma. With the defaults SDR white (1.0) rises to about 1.6, not 5x.
expansion_gammaFLOAT1.200.1–5Exponent on the amount luma exceeds the threshold. Above 1.0 the expansion starts more gently and adds less; below 1.0 it rises faster just above the threshold.
smoothnessFLOAT0.100–0.5Width, in linear luma units, of the soft onset at the threshold (a sigmoid on luma, not a spatial blur). 0 = hard onset.

Outputs (1)

NameTypeDescription
imageIMAGE—