Nodes/radiance/ColorCorrect
ComfyUI Node

ColorCorrect

Exposure, contrast and lift/gain that don't clip your HDR

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
ColorCorrect
  • image
  • image
◄exposure0.0►
◄contrast1.00►
◄brightness0.00►
◄saturation1.00►
◄gamma1.00►
◄lift_r0.00►
◄lift_g0.00►
◄lift_b0.00►
◄gain_r1.00►
◄gain_g1.00►
◄gain_b1.00►
◄luma_spaceRec.709 / sRGB►
◄clamp_outputfalse►

Most "color correct" nodes in this ecosystem are a brightness slider with ambitions. Brightness is additive - it adds a constant to every pixel, which flattens contrast and blows highlights before you've done anything useful. This one does the operations in an order that a person who's sat at a grading console would recognise, in 32-bit float, without clamping.

The order matters, and it's documented

The node applies, in this sequence:

  1. Lift (lift_r/g/b) - a flat per-channel offset added at every level, applied first so exposure and gain scale it afterward. The tooltip is honest that this is a flat offset, not a shadow-weighted lift, which is not what a colourist means by the word. Know that going in.
  2. Exposure in stops. +1 doubles brightness, -1 halves it. Linear-domain maths, which is why it behaves properly on float.
  3. Gain per channel (gain_r/g/b, default 1) - a multiplier, so this is the one that does the "tint the highlights" job.
  4. Contrast, pivoting around 0.18 in linear rather than 0.5. That's the fix that matters: pivoting contrast at 0.5 on linear values moves your mid-grey and makes everything look muddy. 0.18 is linear mid-grey, so contrast expands and contracts around the tone your eye reads as neutral.
  5. Gamma, computed as sign-preserving x^(1/gamma), so negative values survive instead of being clamped to zero. On >1 gamma you brighten midtones without moving white.
  6. Saturation, weighted by luma_space - pick Rec.709 / sRGB, ACEScg / AP1 or Rec.2020 to match your working space. Getting this wrong shifts hue as you saturate, which is the classic "why did the skin go orange" moment.
  7. Brightness, an additive offset, applied last so it acts in the output space.

That ordering is the node's actual value. Same controls, wrong order, and lift/gain behaves nothing like the grading tool you learned on.

clamp_output is off by default and should stay off in an HDR chain - it preserves super-whites and negatives, which is the entire reason to be in float. Turn it on only at the very end if something downstream can't cope.

Where it fits

This is the grade node you put at the end of an HDR pipeline, or anywhere you need an exposure trim without a colour-space conversion. If you're doing a creative look - curves, split-toning, per-channel shaping - RadianceCurves is the better tool. If you're doing a technical pass - this is 1.5 stops hot, lift the blacks 0.02, add a touch of contrast - this is it, and it's also the node you'd use to make a linear image viewable before a preview.

Install

Ships in Radiance. Manager → search Radiance → install, restart, refresh the browser. Manual:

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: python_embeded\python.exe for the pip line. Pure tensor maths - no models, no downloads, nothing to configure.

Where people get burned

Grading in the wrong space. These operations assume linear values. Feed it sRGB-encoded input and exposure-by-stops is no longer physically meaningful, the 0.18 contrast pivot lands somewhere unhelpful, and everything feels like it's fighting you. Decode first.

Expecting the lift to be a lift. Per the tooltip: flat offset, applied first, so a gain of 2 doubles it. If you want a shadow-weighted lift you're looking at the wrong control, and no amount of fiddling will make it behave like one.

Leaving clamp_output on out of habit. It's off for a reason. An aggressive grade on float data can push highlights above 1.0 temporarily - that's fine, it's data, and you may well bring it back down later. Clamping at every step is how you get a chain of nodes that each quietly destroy a little range.

CategoryFXTD STUDIOS/Radiance/Color

Inputs (14)

NameTypeDefaultDescription
imageIMAGEInput image to color correct. Processed in 32-bit float precision.
exposureFLOAT0.0-10–10Exposure adjustment in stops. +1 = double brightness, -1 = half brightness.
contrastFLOAT1.000–4Contrast multiplier around mid-gray (0.18 in linear). >1 = more contrast, <1 = less.
brightnessFLOAT0.00-1–1Additive brightness offset. Applied last so it works in the final output space.
saturationFLOAT1.000–3Color saturation. 0 = grayscale, 1 = original, >1 = boosted.
gammaoptFLOAT1.000.1–4Gamma correction: values are raised to 1/gamma. >1 = brighten midtones, <1 = darken. Sign-preserving for HDR.
lift_roptFLOAT0.00-0.5–0.5Value added to the red channel at every level, applied first, so exposure and gain then scale it. A flat offset, not a shadow-weighted lift.
lift_goptFLOAT0.00-0.5–0.5Value added to the green channel at every level, applied first, so exposure and gain then scale it. A flat offset, not a shadow-weighted lift.
lift_boptFLOAT0.00-0.5–0.5Value added to the blue channel at every level, applied first, so exposure and gain then scale it. A flat offset, not a shadow-weighted lift.
gain_roptFLOAT1.000–2Red channel gain (multiplier). Applied after lift, before contrast.
gain_goptFLOAT1.000–2Green channel gain (multiplier). Applied after lift and exposure, before contrast.
gain_boptFLOAT1.000–2Blue channel gain (multiplier). Applied after lift and exposure, before contrast.
luma_spaceoptCOMBORec.709 / sRGBLuminance weights for saturation. Match your working colorspace.
clamp_outputoptBOOLEANfalseClamp output to [0,1]. OFF = HDR pass-through (preserves super-whites/negatives).

Outputs (1)

NameTypeDescription
imageIMAGEColor corrected image in 32-bit float.