Grade
Radiance Grade — a real Lift/Gamma/Gain node so you stop faking color work with img2img
- image
- reference_image
- image
- grade_info
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_strengthblend so you can dip into a look instead of committing. You can also load your own from a JSON file inGRADE_PRESETSformat viapreset_file. - Grade matching. Connect a
reference_imageand the node computes the grade automatically to match that image's color statistics in CIE L*a*b* (mean/std), withmatch_strengthblending 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.
Inputs (21)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Input image to grade. | |
| preset | COMBO | None (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_strength | FLOAT | 1.000–1 | Mix 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_imageopt | IMAGE | Optional reference image for automatic grade matching. | |
| match_strengthopt | FLOAT | 1.000–1 | How 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_fileopt | STRING | Optional 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_ropt | FLOAT | 0.000-1–1 | Value added to the red channel before gain (so it is scaled by gain). A flat additive offset, not a shadow-weighted lift. |
| lift_gopt | FLOAT | 0.000-1–1 | Value added to the green channel before gain (so it is scaled by gain). A flat additive offset, not a shadow-weighted lift. |
| lift_bopt | FLOAT | 0.000-1–1 | Value added to the blue channel before gain (so it is scaled by gain). A flat additive offset, not a shadow-weighted lift. |
| gamma_ropt | FLOAT | 1.0000.01–5 | Power 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_gopt | FLOAT | 1.0000.01–5 | Power 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_bopt | FLOAT | 1.0000.01–5 | Power 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_ropt | FLOAT | 1.0000–5 | Multiplier on the red channel, applied after lift. 1.0 is neutral, 0 is black; values above 1.0 can push HDR highlights higher. |
| gain_gopt | FLOAT | 1.0000–5 | Multiplier on the green channel, applied after lift. 1.0 is neutral, 0 is black; values above 1.0 can push HDR highlights higher. |
| gain_bopt | FLOAT | 1.0000–5 | Multiplier on the blue channel, applied after lift. 1.0 is neutral, 0 is black; values above 1.0 can push HDR highlights higher. |
| offset_ropt | FLOAT | 0.000-1–1 | Value 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_gopt | FLOAT | 0.000-1–1 | Value 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_bopt | FLOAT | 0.000-1–1 | Value added to the blue channel after gain (not scaled by gain), shifting the whole channel up or down in the image's own encoding. |
| contrastopt | FLOAT | 1.000–3 | Linear contrast around pivot: out = (in - pivot) * contrast + pivot, applied after gamma. 1.0 is neutral, 0 flattens everything to the pivot value. |
| pivotopt | FLOAT | 0.500–1 | Grey 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). |
| saturationopt | FLOAT | 1.000–3 | Saturation relative to Rec.709 luma, applied last. 1.0 is neutral, 0 is monochrome, above 1.0 increases colourfulness. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| grade_info | STRING | — |