Nodes/Image Processing Suite for ComfyUI/CIE LCH Noise (GEGL-like)
ComfyUI Node

CIE LCH Noise (GEGL-like)

Perceptual film grain, straight from GIMP's math

By marcoc2·Created 2 years ago·Updated 5 months ago· 1
CIE LCH Noise (GEGL-like)
  • image
  • image
lightness_distance40.0
chroma_distance40.0
hue_distance3.0
holdness2
seed0

Most noise filters just add random pixels and call it a day. CIE LCH Noise (GEGL-like) is the thoughtful version: it adds noise in the CIE LCh color space, which is designed to match how human perception actually works. That means the grain it produces looks like film grain rather than digital static - the kind of organic texture that makes a clean AI render feel like it was shot on stock instead of generated. It's a port of GIMP's gegl:noise-cie-lch filter, and it's one of the more quietly useful nodes in the AnotherUtils pack for the final-finish crowd.

The key idea: LCh splits color into Lightness (L), Chroma (C, how saturated), and Hue (h, the actual color angle). Noise in RGB lands wherever it lands. Noise in LCh lands where your eye is least annoyed by it - you can jitter lightness for grain, chroma for "chunky" color, or hue for subtle shimmer, each independently.

How it works

The node does a full round trip: sRGB → linear RGB → XYZ (D65) → Lab → LCh, all in vectorized torch, then perturbs the three channels, then converts back. Two details make it feel authentic:

  • Hue wraps. Hue is a circle, so hue noise wraps around 0–360° instead of clamping - no hard color cutoffs. It also only jitters hue where chroma is above zero (random hue on a gray pixel is meaningless).
  • Holdness (default 2) replicates GEGL's "dulling" trick: instead of one uniform draw, it takes the minimum of holdness uniforms, which skews the noise toward small values - most pixels move a little, few move a lot. Bump it higher for subtler, clumpier grain.

Everything is seeded via the seed input, so a fixed seed gives reproducible grain - important when you're iterating on a grade and want to compare settings pixel-for-pixel.

The inputs that matter

  • lightness_distance (0–100, default 40) - how far L can jump. This is your grain amount.
  • chroma_distance (0–100, default 40) - color saturation jitter. Higher = noisier, more "electronic" color.
  • hue_distance (0–180, default 3) - keep this small. Hue noise past ~10 gets ugly fast, and the default 3 is already visible.
  • holdness (1–8, default 2) - the dulling factor above.
  • seed - determinism.

One output, image. Alpha is preserved unchanged.

Where it fits

Use it in the finishing chain: after upscaling or right before a final light sharpen, on photographic or cinematic renders. It's also a cheap way to add texture that survives video - a consistent seeded grain across frames is exactly what "film grain" needs. Pair it with the pack's RGB Noise for heavier, channel-split grain, or Mean Curvature Blur to smooth first and grain after.

Install

ComfyUI Manager → search AnotherUtils, or:

cd ComfyUI/custom_nodes
git clone https://github.com/marcoc2/ComfyUI-AnotherUtils.git

Restart ComfyUI. Pure torch - no weights, no pip extras.

Common issues

If grain is invisible, you're likely on small lightness_distance values or the image is very flat - LCh noise is most obvious in midtones. If it looks like rainbow static, your hue_distance is way too high; keep it under ~10. On very large batches the vectorized conversion churns GPU memory, so process big batches in chunks. And remember the seed: two runs with the same seed produce identical grain, which is either a feature or a trap depending on whether you remembered to vary it.

Categoryimage/noise

Inputs (6)

NameTypeDefaultDescription
imageIMAGE
lightness_distanceFLOAT40.00–100
chroma_distanceFLOAT40.00–100
hue_distanceFLOAT3.00–180
holdnessINT21–8
seedINT0-2147483648–2147483647

Outputs (1)

NameTypeDescription
imageIMAGE