Nodes/GPU LayerStyles/GPU LayerStyles Brightness Contrast V2
ComfyUI Node

GPU LayerStyles Brightness Contrast V2

Three sliders, applied in an order you should know

By lany42·Created 23 days ago·Updated 15 days ago· 0
GPU LayerStyles Brightness Contrast V2
  • image
  • image
◄brightness1.00►
◄contrast1.00►
◄saturation1.00►
◄output_devicecpu►
◄batch_size0►

Brightness, contrast, saturation. Every image editor has these three sliders and most ComfyUI packs expose them somewhere; the reason to care about this particular node is that it tells you exactly what it does to your tensor, and it does it to a whole video batch in float32 without a Python loop in sight.

What it is

GPULayerStyles_BrightnessContrastV2 is the basic tone node in the GPU LayerStyles pack, the one you drop after a decode when a clip is muddy or slightly washed out. It's deterministic - no model, no seed lottery - which is the whole argument of the post-processing layer: a gamma-style tweak takes a millisecond and always does the same thing, while re-rolling a generation to fix "it looks flat" takes minutes and changes the face.

How it actually works

The three operations run in a fixed order, and the order is not cosmetic:

  1. Brightness multiplies RGB and clamps to [0, 1]. It's multiplicative, not additive, so midtones move more than whites and you don't get that flat additive-wash look as fast.
  2. Contrast subtracts the frame's luma mean, scales, and adds the mean back. Read that closely: the mean is computed per frame (mean(dim=(1,2)) in the source), not across the batch. That's a deliberate choice for video - batch-wide contrast would make each frame's correction depend on its neighbours, which is how you get brightness pumping between frames. Per-frame keeps each frame internally consistent.
  3. Saturation converts to luma and pushes RGB away from or toward grey.

Both luma calculations use the 0.299 / 0.587 / 0.114 Rec.601 weights. All three steps clamp, so stacking extremes clips rather than wraps.

Inputs and outputs you actually set

image is the only connection. Then:

  • brightness - default 1, range 0–3. 1 is a no-op.
  • contrast - default 1, range 0–3. Below 1 flattens, above 1 punches. Past about 1.4 on a real photo you'll see the clipped shadows.
  • saturation - default 1, range 0–3. 0 is greyscale, which is a nice trick for checking whether a grade is holding up.
  • output_device - cpu (default) or gpu, and batch_size - frames per processing chunk, 0 meaning "start at 64".

Those last two appear on most nodes in this pack and they're worth understanding once. Everything is computed in float32 on ComfyUI's selected compute device; output_device decides where the finished tensor sits. Leave it on cpu for a normal graph. Set it to gpu only when the next node also runs on the GPU - then you skip the device round trip entirely. batch_size is the OOM valve: chunks are processed one at a time, and on an allocation failure the node logs a warning and halves the chunk size for that run. It doesn't reduce the memory your complete input and output need, so it won't save you from a 500-frame 1080p batch if that batch doesn't fit.

The output is a single image batch - same length, same resolution, RGB channels adjusted. Alpha, if present, is passed through untouched.

Install

ComfyUI Manager → search GPU LayerStyles, or:

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

No pip dependencies (pyproject.toml declares an empty runtime dependency list), no models, no API key. It does want Python 3.13+ and a recent ComfyUI - the pack is written against the newer comfy_api node schema. AGPL-3.0-only licensed.

The README's headline is a 42× speedup over ComfyUI_LayerStyle on a 525-frame 720×1280 chain - treat that as directional, not gospel. It's explicitly an informal, user-reported number with no hardware or repeat count attached. The directional part is plausible though: the upstream LayerStyle pack is a huge toolkit with a long requirements.txt, and this pack exists mostly because people wanted those few operations without that dependency surface.

Where it goes wrong

Honestly, not much - the failure modes are your own sliders. The two that matter: brightness above roughly 1.5 on already-bright frames clips highlights to flat white, and contrast that moves the mean of each frame independently means a sequence with a lighting change between shots gets graded inconsistently shot to shot. If a clip goes too contrasty, you're past the range where the clamp can save you - back off and add the punch later.

CategoryGPU LayerStyles/Color

Inputs (6)

NameTypeDefaultDescription
imageIMAGE—
brightnessFLOAT1.000–3—
contrastFLOAT1.000–3—
saturationFLOAT1.000–3—
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—