π Lightness Shadows/Mid/High
Three Sliders That Beat Messing With Levels
- image
- IMAGE
What it's actually for
Splitting an image's brightness into shadows, midtones and highlights and pushing each one independently - the black-magic half of any photo editor's tone panel. In a ComfyUI graph you usually get one brightness slider and a gamma curve and that's it, which is why "the shadows are crushed but the highlights are fine" is such a common dead end.
It's a fast tweak node. The obvious uses: lift the shadows on a dim render before it goes into a texture, pull the highlights back on a blown-out HDR-ish pass, or balance three view passes so they read consistently against each other. Small, no dependencies, and it costs nothing to keep in a graph.
How it works
The maths is refreshingly simple, and worth knowing because it explains the failure modes.
First, per-pixel luminance using Rec.709 weights (0.2126/0.7152/0.0722) - the perceptual brightness of each pixel.
Then three masks, built with smoothstep so there are no hard bands:
- shadow mask =
1 - smoothstep(0, 0.4, lum)- full strength below 0.4, fading out - highlight mask =
smoothstep(0.6, 1.0, lum)- kicks in above 0.6 - midtone mask =
1 - (shadow + highlight)
And the sliders are offsets, not curves. Each one is remapped from its 0-1 range to -1 to +1 ((value - 0.5) * 2), multiplied by its mask, summed, and added to the pixel value, then clamped to 0-1.
That's the whole node. Two things follow: at the defaults of 0.5, 0.5, 0.5 the offset is zero and the image passes through bit-identical, which makes it a safe no-op node while you're building. And because it's an additive offset rather than a curve, pushing shadows up raises black to grey rather than lifting detail out of them - it's a brightness shift, not a shadow recovery tool.
The inputs that matter
- image - the IMAGE to adjust. Standard ComfyUI batch tensor.
- shadows (default 0.5) - below 0.5 darkens shadows, above brightens.
- midtones (default 0.5) - same idea for the middle of the range. This is the one that acts like "exposure".
- highlights (default 0.5) - below 0.5 pulls highlights down, above pushes them up toward clipping.
One output: IMAGE.
Everything runs on the tensor in float32 with a clamp at the end, so it's cheap even on big batches - you can leave it in a batch pipeline without worrying about throughput.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Antonioilev/ComfyUI_Antonioilev_Lightpack.git
# restart ComfyUI
Manager search: "Antonioilev Light Pack". It's pure torch, so nothing to pip install. The pack ships no requirements.txt, mind - if this node is missing from the Antonioilev/2D menu, it's the __init__.py import guard hiding an error, and the console's loaded/failed count is where you find out.
Where people get burned
- Additive, so it clips. Nudge highlights up on an image that's already near white and you lose the top end to the clamp. Pull down instead of pushing up when you want to reclaim detail.
- The masks overlap slightly by design. The midtone mask can dip a little negative in the crossover regions, which means a big midtones push can fight the shadow and highlight sliders. Small moves behave predictably; large ones get fiddly.
- It's per-pixel on luminance, not per-channel. Your colours shift in brightness rather than in hue, which is the sane behaviour, but a strong shadows lift will still visibly flatten saturation in dark areas.
- Not the node for a grade. One offset per band, no gamma, no contrast, no colour. If you need more than three sliders, that's a different node - this one is designed to be one drag away from neutral.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | β | |
| shadows | FLOAT | 0.500β1 | β |
| midtones | FLOAT | 0.500β1 | β |
| highlights | FLOAT | 0.500β1 | β |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | β |