Film Grain Effect V2 (video)
Film grain that breathes — and lets you write the math
- images
- IMAGE
Static grain is easy - plenty of nodes add a fixed noise layer and call it a day. Film grain that moves like real celluloid, pulsing in intensity over time, is harder. FilmGrainEffect_v2 is the version of DJZ-Nodes' grain node that gives you an actual expression editor, so you can control exactly how the grain swells and recedes across the frames. If you're finishing a video and it feels a touch too clean, this is the polish node.
How it works
For each frame the node computes a time-based intensity from a mathematical expression where t is the frame number times time_scale, then multiplies it by base_intensity, and adds gaussian noise scaled by noise_scale. The clever part is that the expression can call a small safe function set - sin, cos, exp, normal(mu, sigma), uniform(a, b) - so the grain isn't just "on" but breathing according to your formula. It's evaluated with a restricted sandbox (__builtins__ emptied), so you can't blow up your machine with it, and a bad expression just returns 0.0 with a console message rather than crashing.
Six presets ship baked in: subtle, vintage, unstable_signal, dip, ebb, and flow. unstable_signal is the one that looks like a transmission problem - it combines a fast oscillation with a decaying burst. dip does a gaussian "dip" every ~60 frames, great for that brief darkening between reel changes.
The inputs
- preset - pick one, or
customto use your own expression. - expression_input - the fun part. Default is
0.08 * normal(0.5, 0.15) * (1 + 0.2 * sin(t/25)). Swap inexp(-t/40)for grain that fades in over a clip, oruniform(0.2, 0.8)for erratic popping.tstarts at frame 0. - base_intensity - overall strength, 0-1. Start at 0.05-0.1; above 0.2 you're past "grain" and into "snowstorm."
- time_scale - speeds up or slows down the temporal modulation. Lower = lazier breathing.
- noise_scale - the size/graininess of the noise itself.
- seed - the one thing v1 got wrong that v2 fixes: reproducible grain. Same seed, same pattern, every time.
Output is the same-resolution IMAGE batch. Feed it straight into a save node or an encoder.
Installing
It lives in the DJZ-Nodes pack (MushroomFleet / Drift Johnson). Grab the pack once:
cd ComfyUI/custom_nodes
git clone https://github.com/MushroomFleet/DJZ-Nodes
cd DJZ-Nodes
pip install -r requirements.txt
Or "DJZ-Nodes" via ComfyUI Manager. The grain nodes are pure numpy/PIL, so no special deps beyond the pack's usuals.
Where people get burned
If expression_input errors, you get no intensity (a flat 0.0) - silent-ish, only visible in the console. And remember the expression is evaluated per frame with fresh randomness from normal/uniform, so the pattern is reproducible only with a fixed seed. Want a quick taste? Leave preset on subtle, set base_intensity to 0.08, and it's a ten-second upgrade to any video.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| preset | COMBO | subtle | 7 options: custom, subtle, vintage, unstable_signal, dip, ebb, +1 |
| expression_input | STRING | 0.08 * normal(0.5, 0.15) * (1 + 0.2 * sin(t/25)) | — |
| base_intensity | FLOAT | 0.100–1 | — |
| time_scale | FLOAT | 1.00.1–10 | — |
| noise_scale | FLOAT | 0.200–1 | — |
| seed | INT | 00–2147483647 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |