Color Grading Wheels
Color grading your ComfyUI renders without leaving the graph
- image
- image
Every SDXL or Flux render comes out oversaturated and a bit too punchy, and the standard advice is to fix it in post. This node is that post. Color Grading Wheels drops a Lumetri-style panel straight into your ComfyUI graph - three wheels for shadows, midtones, and highlights, each with a split slider and a global intensity control. No LUT files, no model downloads, no GPU cost beyond a few tensor ops. It's the deterministic, "just grade the damn image" tool that sits between your VAE decode and your Save Image.
There's real demand for this. The community has burned hours trying to get AI models to interpret a color look without nuking the photo - img2img entangles color with content, IP-Adapter style transfer is too subtle or too destructive. A node that does plain pixel math in the graph is the boring, reliable answer, and honestly the boring answer is usually the right one here.
How it actually works
The wheels aren't just sliders wearing a costume - each one genuinely maps a hue/saturation pick. The angle you drag to sets the hue, the distance from center sets how much of it gets applied, and the code converts that to an RGB offset vector in the background. That vector gets added to the image, scaled by intensity.
The clever part is the tonality split. The node computes luma with Rec. 709 weights (0.2126/0.7152/0.0722), then builds three masks: a smoothstep falloff for shadows and highlights around their split points, and a gaussian bump for midtones centered at midtones_split. The weights are normalized so every pixel is a blend of the three wheel colors - no hard banding, and the boundaries soften further as you widen the midtone range. Then it's just clamp(image + weighted_delta * intensity, 0, 1).
The front end does the rest. A small JS file hides the raw float widgets and draws the wheels, split sliders, and intensity bar you actually see. Because the values live in hidden widgets, they serialize into the workflow JSON - your grade travels with the workflow, exactly like any other node setting.
The inputs that matter
You feed it a standard IMAGE and get an IMAGE back, so it slots anywhere in a pipeline.
- shadows_x / shadows_y, midtones_x / midtones_y, highlights_x / highlights_y - each in -1..1, default 0 (neutral). Drag outward to push color, toward center to back off.
- shadows_split, midtones_split, highlights_split - where each band's boundary sits in the 0..1 luma range, defaults 0.35 / 0.5 / 0.65.
- intensity - master gain from 0 to 2, default 1. Turn this down if the grade is too heavy; it's your safety knob.
Installing it
No dependencies, no model files, no requirements.txt at all - it's pure torch plus the standard library. Either install through ComfyUI Manager (search "ComfyUI_ColorGrading") or the manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/bertoo87/ComfyUI_ColorGrading
Restart ComfyUI, then search the node picker for "Color Grading Wheels".
Gotchas and honest limits
Three things bite people. First, this is an additive offset, not a real lift/gamma/gain grade - it shifts colors toward the wheel hue in RGB space and never touches saturation down. There is no desaturation control here; if your image is oversaturated, this node won't fix that. Second, it silently skips non-RGB images - the code returns the input untouched if it doesn't have 3 channels, so an RGBA image will pass through looking like the node did nothing. Third, wheels look like they'd do dramatic work, but the max offset is capped well below full saturation on purpose; the wheels don't reach the rim in practice, so if a grade feels weak, it's doing what it's designed to. Keep the drags subtle and let intensity do the heavy lifting - nobody pulls their shadows wheel to the rim and keeps the job.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| shadows_x | FLOAT | 0.00-1–1 | — |
| shadows_y | FLOAT | 0.00-1–1 | — |
| shadows_split | FLOAT | 0.350–1 | — |
| midtones_x | FLOAT | 0.00-1–1 | — |
| midtones_y | FLOAT | 0.00-1–1 | — |
| midtones_split | FLOAT | 0.500–1 | — |
| highlights_x | FLOAT | 0.00-1–1 | — |
| highlights_y | FLOAT | 0.00-1–1 | — |
| highlights_split | FLOAT | 0.650–1 | — |
| intensity | FLOAT | 1.000–2 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |