Nodes/ComfyUI-NL_Nodes/🎨 NL Grade
ComfyUI Node

🎨 NL Grade

The Nuke-style color grade ComfyUI was missing

By NOLABEL-VFXΒ·Created 9 months agoΒ·Updated 2 months agoΒ· 0
🎨 NL Grade
  • images
  • mask
  • mask_image
  • image
  • image_info_json
β—„lift_master0.000β–Ί
β—„expand_liftfalseβ–Ί
β—„lift_r0.000β–Ί
β—„lift_g0.000β–Ί
β—„lift_b0.000β–Ί
β—„gain_master1.000β–Ί
β—„expand_gainfalseβ–Ί
β—„gain_r1.000β–Ί
β—„gain_g1.000β–Ί
β—„gain_b1.000β–Ί
β—„gamma_master1.000β–Ί
β—„expand_gammafalseβ–Ί
β—„gamma_r1.000β–Ί
β—„gamma_g1.000β–Ί
β—„gamma_b1.000β–Ί
β—„show_advancedfalseβ–Ί
β—„blackpoint0.000β–Ί
β—„whitepoint1.000β–Ί
β—„multiply1.000β–Ί
β—„offset0.000β–Ί
β—„reversefalseβ–Ί
β—„black_clamptrueβ–Ί
β—„white_clampfalseβ–Ί
β—„mix1.00β–Ί
β—„image_info_jsonβ€”β–Ί

NL Grade is the node you reach for when the usual "exposure" slider stops being enough. Most of the color tools floating around ComfyUI are simple multiply-by-a-number or "add some saturation" affairs. This one is a real primary grade, lifted almost directly from Nuke's Grade node: lift, gain, and gamma, per channel, with blackpoint/whitepoint remapping, multiply/offset, clamps, and mask support. If you've ever graded plates in Nuke or Resolve, this will feel like coming home. If you haven't, it's the first color node that lets you actually fix a shot instead of just dimming it.

It belongs to the NOLABEL-Nodes color-management story. The pack normalizes everything into a working space (ACEScg by default) as it reads, and NL Grade operates in that same working space. That matters: your lift and gain sliders are doing their math on linear scene-referred values, not on whatever gamma your monitor is lying to you about.

How it works

The controls are grouped into master + per-channel pairs. lift_master, gain_master, and gamma_master handle the overall image; flip expand_lift, expand_gain, or expand_gamma and you get the individual red/green/blue sliders. blackpoint and whitepoint remap where black and white land in your range, multiply and offset scale and shift the whole thing, and reverse inverts the entire grade - handy as a round-trip sanity check. black_clamp and white_clamp keep values from running off the ends, and mix dissolves between the input and the graded result, which is how you dial in "yes but a third of that."

The inputs that actually matter for a beginner:

  • images - what you're grading.
  • mask - restricts the effect to non-zero areas. Leave it unplugged and it behaves as a full white mask.
  • mask_image - a Nuke-style image mask: RGBA uses its alpha, RGB uses Rec.709 luminance. Ignored if mask is connected.
  • image_info_json - the serialized color metadata from an upstream NL node, passed through untouched so the pipeline keeps tracking its colorspace.

Outputs are image and image_info_json. The second one is why you'd chain this into NL Write or another NL color node - the metadata survives the grade.

Installing it

NL Grade ships in the NOLABEL-Nodes pack, so the install is the same as any of these nodes. Easiest is ComfyUI Manager - search for "ComfyUI-NL_Nodes" and install. Or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/NOLABEL-VFX/ComfyUI-NL_Nodes

Restart ComfyUI and the NOLABEL nodes appear. One thing the README buries: the color pipeline wants PyOpenColorIO and OpenEXR from requirements-color.txt. Without them the pack still runs, but the color-managed path (and the working-space context that makes this grade meaningful) is degraded. If grade results look wrong, that's the first thing to check.

Gotchas

The per-channel controls are hidden behind expand_* toggles - people miss that and wonder where the hue fixes went. Also, the working space comes from the NL Workflow context panel, so if you haven't set up project/shot context, the node falls back to defaults that may not match your pipeline. When in doubt, start with everything at defaults (lift 0, gain 1, gamma 1) and move one control at a time. A grade is a series of small moves, not one big one.

CategoryNOLABEL/Color

Inputs (28)

NameTypeDefaultDescription
imagesIMAGEInput image.
lift_masterFLOAT0.000-1–1β€”
expand_liftBOOLEANfalseβ€”
lift_rFLOAT0.000-1–1β€”
lift_gFLOAT0.000-1–1β€”
lift_bFLOAT0.000-1–1β€”
gain_masterFLOAT1.0000–10β€”
expand_gainBOOLEANfalseβ€”
gain_rFLOAT1.0000–10β€”
gain_gFLOAT1.0000–10β€”
gain_bFLOAT1.0000–10β€”
gamma_masterFLOAT1.0000.01–10β€”
expand_gammaBOOLEANfalseβ€”
gamma_rFLOAT1.0000.01–10β€”
gamma_gFLOAT1.0000.01–10β€”
gamma_bFLOAT1.0000.01–10β€”
show_advancedBOOLEANfalseβ€”
blackpointFLOAT0.000-1–1β€”
whitepointFLOAT1.000-1–10β€”
multiplyFLOAT1.0000–10β€”
offsetFLOAT0.000-1–1β€”
reverseBOOLEANfalseβ€”
black_clampBOOLEANtrueβ€”
white_clampBOOLEANfalseβ€”
mixFLOAT1.000–1β€”
maskoptMASKOptional MASK. Restricts effect to non-zero areas. If unconnected, defaults to a white mask.
mask_imageoptIMAGEOptional IMAGE used as a Nuke-style mask. RGBA uses alpha; RGB uses Rec.709 luminance. Ignored when mask is connected.
image_info_jsonoptSTRINGOptional NL color metadata, passed through unchanged.

Outputs (2)

NameTypeDescription
imageIMAGEβ€”
image_info_jsonSTRINGβ€”