Nodes/GPU LayerStyles/GPU LayerStyles ColorBalance
ComfyUI Node

GPU LayerStyles ColorBalance

Shadows, midtones, highlights without wrecking your luminance

By lany42·Created 22 days ago·Updated 15 days ago· 0
GPU LayerStyles ColorBalance
  • image
  • image
◄cyan_red0.000►
◄magenta_green0.000►
◄yellow_blue0.000►
◄output_devicecpu►
◄batch_size0►

If you've used Photoshop's Color Balance, you already know this node. Three sliders, each pulling a colour pair apart, each acting mostly on one tonal zone: shadows, midtones, highlights. It's the "the whole clip is too cyan in the shadows" fix, and it's the grade that keeps the brightness of the frame where it was.

Why you'd reach for it

This is a fixed-function pixel operation - no model, no sampler, deterministic to the last bit. The post-processing layer of ComfyUI is full of these, and the failure mode it exists to prevent is throwing a diffusion pass at a problem that a colour curve fixes perfectly and instantly. Colour balance is the tool for "the grade is off" and, importantly, not the tool for "it doesn't match that image" - that's ColorMatch.

How it works

It's not a per-zone mask, which is the part people get wrong when they read the label. The implementation applies three three-point spline curves to the whole RGB tensor at once, centred at 0.15, 0.5 and 0.8 of the value range - so each curve is weighted toward a tonal region (shadows, mids, highlights) but none of them is a hard mask. The middle curve is the strongest; the shadow and highlight curves are deliberately gentler.

Then, and this is the actual selling point, it measures luminance before and after with the Rec.709 weights (0.2126 / 0.7152 / 0.0722) and rescales the result so the original luminance is restored. Move the sliders and the hue shifts; the frame does not get brighter or darker. It's clamped to [0, 1] at the end, so pushing a channel to the wall still clips.

The node short-circuits: leave all three sliders at zero and it returns the input unchanged, no maths at all.

The inputs and output that matter

image in, image out - one batch in, one batch out, same length, RGB shifted, alpha untouched.

Three floats, all default 0, all ranging -1 to 1 with a step of 0.001:

  • cyan_red - negative pushes cyan, positive pushes red.
  • magenta_green - negative green, positive magenta.
  • yellow_blue - negative yellow, positive blue.

That 0.001 step is not decoration. A good grade on this node lives between about -0.1 and +0.1; you're meant to nudge, not sweep. If you're dragging to ±0.5 you're probably looking for a different node.

Then the two execution controls, output_device and batch_size, which every colour node here shares. output_device: cpu (the default) computes in float32 on ComfyUI's compute device and hands back a CPU tensor; switch it to gpu when the next node in the chain is also running on the device, and you cut a pointless round trip. batch_size sets frames per chunk - 0 starts at 64 - and halves itself automatically if an allocation fails mid-run, printing a warning in the log. It's the knob for "this 500-frame batch nearly fits", not a fix for "this batch doesn't fit".

Install

ComfyUI Manager → GPU LayerStyles, or manually:

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/lany42/gpu-layerstyles
# restart ComfyUI

No pip install, no models, no API key - the runtime dependency list in pyproject.toml is empty, which is the entire thesis of this pack. Needs Python 3.13+ and a ComfyUI recent enough to ship comfy_api. AGPL-3.0-only, so if you're wrapping it in a product, read the licence.

The pack's README benchmarks it as part of a three-node chain that runs 42× faster than ComfyUI_LayerStyle on a 525-frame clip. It's an informal, user-reported measurement with no stated hardware, so take the direction and ignore the decimal. The direction makes sense: the upstream LayerStyle toolkit is a sprawling Photoshop-in-a-pack whose dependency list is a recurring source of "my ComfyUI won't import this". This one installs by git clone and nothing else.

Getting burned

The luminance preservation is the feature and the trap. If you want a genuinely brighter, warmer image, this node will fight you - it keeps the brightness where it was, so a big warm push reads as a colour cast rather than as more light. Use ColorTemperature for that, or run BrightnessContrastV2 after.

Second: with only three curves there's no keying, no local correction. This is a global grade on the whole batch. Per-region work needs masks upstream, not this.

CategoryGPU LayerStyles/Color

Inputs (6)

NameTypeDefaultDescription
imageIMAGE—
cyan_redFLOAT0.000-1–1—
magenta_greenFLOAT0.000-1–1—
yellow_blueFLOAT0.000-1–1—
output_deviceCOMBOcpuReturn output on CPU (default) after float32 processing on ComfyUI's selected compute device. GPU keeps output on that device to avoid transfers between compatible nodes. ComfyUI CPU mode returns CPU output.
batch_sizeINT00–2147483647Frames per processing chunk. 0 starts at up to 64 frames. Positive values can exceed 64, capped by the output length. Allocation failures halve the chunk size for this run; each run starts fresh. Complete inputs and outputs still need memory. Pass smaller batches through the entire workflow to reduce complete-batch memory.

Outputs (1)

NameTypeDescription
imageIMAGE—