Nodes/Runtime44 ComfyUI Nodes/Runtime44 Film Grain
ComfyUI Node

Runtime44 Film Grain

Film grain in pixel space — real noise, not a filter

By runtime44·Created 2 years ago·Updated 2 years ago· 42
Runtime44 Film Grain
  • image
  • Image
mode
sigmas0.00
amount1.0
seed44

Runtime44 Film Grain adds actual noise to an image in pixel space, using the same random_noise machinery from scikit-image that computer-vision folks have used for years. It's the difference between a "grain" filter that smears a texture over your render and actual per-pixel noise: real variance, proper modes, and a seed so you can reproduce it.

Why would you bother? Photorealistic renders - especially the clean, over-smoothed outputs of modern checkpoints - read as CGI precisely because they're too clean. Film grain breaks up that smoothness and gives the image something for the eye to chew on. It's a finishing step: you run it after VAE Decode, as the last thing before Save Image. If your work lives in the photoreal-or-nothing camp, this is a legitimately useful final pass.

How it works

The node runs random_noise from scikit-image (via cucim on CUDA, numpy fallback on CPU), then blends the noisy image with the original using PIL. Three noise modes:

  • gaussian - classic film-like luminance noise.
  • poisson - signal-dependent noise, which lands differently on bright vs dark areas.
  • speckle - multiplicative noise, mostly texture in highlights.

The sigmas input feeds the noise's mean for gaussian and speckle (the naming is borrowed from skimage's own parameter, so don't overthink it), and amount controls the blend between the original and the noisy version: 1.0 is pure noise, 0.0 is the untouched image. seed (default 44) makes it repeatable.

The gotcha that bites everyone

The defaults are a trap. sigmas starts at 0 and amount at 1.0, which together mean "maximum grain with a zero-mean noise" - i.e., a very noisy image straight out of the box. The dials also fight each other: raising amount increases everything, so the way to get subtle grain is usually to keep amount moderate (0.2–0.5) and tune sigmas up from 0 until you see texture you like. There's no "strength" slider that does what you expect; it's a two-axis control.

It takes a whole IMAGE and returns a whole Image, so graining only part of a frame (say, a sky but not a face) means masking first and compositing after. The node has no mask input of its own.

Installing

It ships in Runtime44 ComfyUI Nodes:

cd ComfyUI/custom_nodes
git clone https://github.com/runtime44/comfyui_r44_nodes
cd comfyui_r44_nodes
python -m pip install -r requirements.txt   # or: uv pip install -r requirements.txt

Restart ComfyUI. This is one of the two nodes in the pack (with Color Match) that actually uses the heavy CUDA dependencies - cupy-cuda12x and cucim are in the requirements file and are multi-GB downloads on a CUDA 12 machine. On CPU it falls back to numpy + scikit-image, so it still works, just slower on big frames.

Two last notes. The pack has been dormant since mid-2024, so what you see is what you get. And this node lands in the default "sd" category in the node menu rather than an image section - mildly annoying, but you'll find it in the same place every time. Keep the seed at a fixed value when you're iterating on a workflow; bump it when you want each frame's grain to differ (which you will, if you're doing video frames).

Categorysd

Inputs (5)

NameTypeDefaultDescription
imageIMAGE
modeCOMBO3 options: gaussian, poisson, speckle
sigmasFLOAT0.00
amountFLOAT1.0
seedINT440–9223372036854776000

Outputs (1)

NameTypeDescription
ImageIMAGE