ComfyUI Node

Grade

Radiance Grade — a real Lift/Gamma/Gain node so you stop faking color work with img2img

By FXTD-Studios·Created 9 months ago·Updated 3 days ago· 249
Grade
  • image
  • reference_image
  • image
  • grade_info
◄presetNone (Custom)►
◄preset_strength1.00►
◄match_strength1.00►
◄preset_file►
◄lift_r0.000►
◄lift_g0.000►
◄lift_b0.000►
◄gamma_r1.000►
◄gamma_g1.000►
◄gamma_b1.000►
◄gain_r1.000►
◄gain_g1.000►
◄gain_b1.000►
◄offset_r0.000►
◄offset_g0.000►
◄offset_b0.000►
◄contrast1.00►
◄pivot0.50►
◄saturation1.00►

Here's the thing most ComfyUI users do when their image "needs a grade": they re-roll it with a prompt tweak, or worse, run it back through the sampler with a low denoise. That's the expensive way to do work a colorist does in a second. Radiance Grade is the LGG node - Lift, Gamma, Gain, plus Offset, Contrast, and Saturation - with per-channel control, running in 32-bit float. It's the piece of a proper color pipeline that was missing from most ComfyUI installs.

The KB's post-processing doc makes the argument plainly: almost none of this is AI, and you should reach for the cheap deterministic primitive before the expensive generative one. This is that primitive. A gamma curve is a gamma curve - but here it's a per-channel gamma with a pivot and a JSON export, which is where it stops being a toy.

How it works

The grade math is standard film-print style: lift is a shadow offset, gamma is a midtone power, gain multiplies highlights, offset adds a global shift. Contrast is applied around a pivot (output = (img - pivot) * contrast + pivot), and saturation is luminance-preserving so cranking it doesn't blow out brightness. The whole thing runs in 32-bit float, which is the pack's whole thesis - standard ComfyUI nodes clamp to 8-bit-ish range, and this one doesn't. The pivot tooltip flags that v2.3.3 pins it to 0.18 for scene-linear correctness, which is the difference between grading an sRGB image and grading actual HDR/linear footage.

Two features put it ahead of a bare LGG math node:

  • Presets. Thirteen built-in looks (Cinematic Teal & Orange, Bleach Bypass, Film Noir, Golden Hour, Matrix Green, Horror Desaturated, and more) with a preset_strength blend so you can dip into a look instead of committing. You can also load your own from a JSON file in GRADE_PRESETS format via preset_file.
  • Grade matching. Connect a reference_image and the node computes the grade automatically to match that image's color statistics in CIE L*a*b* (mean/std), with match_strength blending between your manual grade and the matched one.

Inputs and outputs that matter

The manual controls are nine per-channel values plus the four globals: lift_r/g/b, gamma_r/g/b, gain_r/g/b, offset_r/g/b, then contrast, pivot, saturation. For a beginner: set preset to a look at low preset_strength, then nudge gain for highlights and lift for shadows. The gamma values sit at 1.0 (no change), gain at 1.0, lift/offset at 0.

Outputs are the real payoff: image (the graded 32-bit result) and grade_info - a JSON string of every applied parameter. Wire that into the pack's ApplyGradeInfo node and you can replay the exact grade onto other shots with a strength control. That's the difference between "I got a nice look" and "I have a reproducible grade."

Installing

cd ComfyUI/custom_nodes
git clone https://github.com/fxtdstudios/radiance.git
cd radiance
pip install -r requirements.txt   # or requirements_windows.txt / requirements_linux.txt / requirements_mac_silicon.txt

Or search Radiance in ComfyUI Manager. Linux needs libopenexr-dev before pip. Restart after.

Common issues

The recurring one, straight from the community and the README: this node cannot recover data that isn't there. If your source is an 8-bit sRGB PNG, grading it in float doesn't create headroom - the pack "cannot recover highlight or shadow detail that was already clipped in an 8-bit source." People expect the HDR magic to fix their blown-out sky and it doesn't; that's a capture problem, not a grade problem. Feed it EXR or float output if you want the full dynamic range. Second gotcha: match_strength defaults to 1, so plugging in a reference image will fully override your manual grade until you dial it back. That's intended, but it surprises people the first time.

CategoryFXTD STUDIOS/Radiance/Color

Inputs (21)

NameTypeDefaultDescription
imageIMAGEInput image to grade.
presetCOMBONone (Custom)Built-in look. Its lift/gamma/gain/offset/contrast/saturation values are blended with the manual sliders by preset_strength; pivot is not part of a preset. None (Custom) uses the sliders only.
preset_strengthFLOAT1.000–1Mix between the manual slider values (0) and the preset values (1). At 1.0 a selected preset fully replaces the manual lift/gamma/gain/offset/contrast/saturation sliders.
reference_imageoptIMAGEOptional reference image for automatic grade matching.
match_strengthoptFLOAT1.000–1How far gain and offset are pulled toward the values derived from reference_image (0 = off, 1 = full). Only used when a reference image is connected.
preset_fileoptSTRINGOptional path to a JSON file of presets, keyed by name, using the same fields as the built-ins. It is merged over the built-ins, so it can only redefine a preset already listed in the preset menu.
lift_roptFLOAT0.000-1–1Value added to the red channel before gain (so it is scaled by gain). A flat additive offset, not a shadow-weighted lift.
lift_goptFLOAT0.000-1–1Value added to the green channel before gain (so it is scaled by gain). A flat additive offset, not a shadow-weighted lift.
lift_boptFLOAT0.000-1–1Value added to the blue channel before gain (so it is scaled by gain). A flat additive offset, not a shadow-weighted lift.
gamma_roptFLOAT1.0000.01–5Power applied to the red channel after lift, gain and offset: out = in^(1/gamma), sign preserved. Above 1.0 brightens mid-tones, below 1.0 darkens them; 1.0 is neutral.
gamma_goptFLOAT1.0000.01–5Power applied to the green channel after lift, gain and offset: out = in^(1/gamma), sign preserved. Above 1.0 brightens mid-tones, below 1.0 darkens them; 1.0 is neutral.
gamma_boptFLOAT1.0000.01–5Power applied to the blue channel after lift, gain and offset: out = in^(1/gamma), sign preserved. Above 1.0 brightens mid-tones, below 1.0 darkens them; 1.0 is neutral.
gain_roptFLOAT1.0000–5Multiplier on the red channel, applied after lift. 1.0 is neutral, 0 is black; values above 1.0 can push HDR highlights higher.
gain_goptFLOAT1.0000–5Multiplier on the green channel, applied after lift. 1.0 is neutral, 0 is black; values above 1.0 can push HDR highlights higher.
gain_boptFLOAT1.0000–5Multiplier on the blue channel, applied after lift. 1.0 is neutral, 0 is black; values above 1.0 can push HDR highlights higher.
offset_roptFLOAT0.000-1–1Value added to the red channel after gain (not scaled by gain), shifting the whole channel up or down in the image's own encoding.
offset_goptFLOAT0.000-1–1Value added to the green channel after gain (not scaled by gain), shifting the whole channel up or down in the image's own encoding.
offset_boptFLOAT0.000-1–1Value added to the blue channel after gain (not scaled by gain), shifting the whole channel up or down in the image's own encoding.
contrastoptFLOAT1.000–3Linear contrast around pivot: out = (in - pivot) * contrast + pivot, applied after gamma. 1.0 is neutral, 0 flattens everything to the pivot value.
pivotoptFLOAT0.500–1Grey level that contrast rotates around, in the image's own encoding (0.5 suits display-referred sRGB; scene-linear mid-grey is about 0.18).
saturationoptFLOAT1.000–3Saturation relative to Rec.709 luma, applied last. 1.0 is neutral, 0 is monochrome, above 1.0 increases colourfulness.

Outputs (2)

NameTypeDescription
imageIMAGE—
grade_infoSTRING—