Nodes/Deno Custom Nodes/(Deno) Film Grain
ComfyUI Node

(Deno) Film Grain

(Deno) Film Grain — the cheapest way to stop your output looking AI-generated

By Deno2026·Created 7 months ago·Updated 3 days ago· 198
(Deno) Film Grain
  • images
  • images
◄enabledtrue►
◄amount6.00►
◄grain_size1.00►
◄roughness0.25►
◄tone_weightedtrue►
◄temporal_modechanging►
◄seed2026100701►
◄frame_offset0►
◄processing_batch_size1►
◄grain_scale_moderesolution►

Your generation isn't failing on detail, it's failing on cleanliness. Sensors and film stock are noisy; diffusion output isn't. Adding noise back at the end is the standard anti-AI-look move, and the cheapest - a pixel pass, not another diffusion run.

(Deno) Film Grain is that pass. It takes an IMAGE batch - a photo, or decoded video frames - and hands back the same batch with monochrome grain mixed in. Same dimensions, same dtype, alpha untouched. No model, no download, and it runs on CPU.

How the grain is actually made

It's one noise field added to all three channels rather than per-channel color noise, which is why it reads as luminance grain instead of RGB confetti. The field is two Gaussian-blurred white-noise passes - fine (sigma 0.45px) and coarse (1.15px) - normalized, mixed 75:25 by default, then soft-clipped with a tanh so extreme particles round off instead of punching holes in your highlights.

Tone protection multiplies that noise by a per-pixel weight curve from the frame's own luminance: near zero at pure black and white, peaking in the midtones, leaning darker. The add is in 8-bit brightness units - noise times amount / 255 - then clipped to 0–1.

The scale mode is the recent part. In Match resolution (default for new nodes) grain is generated on an aspect-matched grid with a 1536px short edge, then only the grain is Lanczos-resampled to your frame. No renormalization after that, deliberately: it would amplify subpixel grain on small frames instead of matching the texture at display size. Fixed pixels generates at input resolution - the original behavior, kept for old workflows and API prompts.

What you actually touch

Native widgets are swapped for a compact green panel, in English regardless of your ComfyUI locale. An On / Off toggle sits in the header; off passes the input through without even copying it. Four controls matter:

  • Strength, 0–1 (default 0.5). Stored as amount in 8-bit brightness units; the panel maps 0–1 onto 0–12, so 0.5 is amount 6. The backend still accepts up to 100 so older workflows keep working.
  • Grain size, 0.25–4. Scales the grain blur only, never your image detail.
  • Roughness - the coarse proportion. 0.25 is the 75:25 fine/coarse default.
  • Tone protection, on by default.

The rest is under Advanced: grain_scale_mode, temporal_mode (Per frame / Fixed), seed, frame_offset, and Frames at once (processing_batch_size, 1–4). Per frame varies the pattern each frame - what you want for video; Fixed repeats one pattern, what you want for A/B tests with the seed control set to fixed. Long clip in chunks? Put the chunk's first global frame index into frame_offset and the pattern continues as one take.

One output: images (IMAGE). Into Save Image for a still, or Create Video / VHS Video Combine for video - keep audio and frame rate on the muxing node, since this one never touches audio. A native VIDEO socket connects after Create Video, so it can't sit inline on a VIDEO wire.

Install

ComfyUI Manager: search Deno Custom Nodes, install, restart ComfyUI, hard-refresh the browser page. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/Deno2026/comfyui-deno-custom-nodes
# restart ComfyUI afterwards

Nothing to download for this node and no packages it needs - numpy and Pillow, plus scipy if it's around (a pure-numpy blur fallback gives the same result). Before you click install, though: the pack's requirements.txt also pulls openai-whisper, nvidia-ml-py, psutil and urllib3 for its other nodes, and Whisper is not small. Every Deno node has a green i button top-right for node help, which turns yellow with a ! when a newer pack version exists.

Where it bites

RAM, not VRAM. Grain is CPU work and won't fight your generation for VRAM, but an enabled run still allocates the complete output batch in system memory. For float32 RGB that's frames × width × height × 12 bytes - about 23.7 MiB per 1080p frame, 2.78 GiB for 120 of them - on top of your input batch and ComfyUI's cache. Leave Processing at Low RAM and chunk long clips.

Old workflow, new-node default. v0.7.114 made Match resolution the default for newly created nodes; existing workflows keep Fixed pixels and look exactly as before. If a hand-rebuilt workflow shows differently sized grain, check grain_scale_mode first.

Extreme aspect ratios fail in Match resolution. The reference grid is capped at 16,777,216 pixels, so a wildly panoramic input errors out instead of eating your RAM. Switch to Fixed pixels.

Smaller ones. A saved amount above 12 stays intact and the Strength slider sits out of range until you move it. NaN or infinity upstream makes the node raise instead of writing garbage downstream. Wire a value into a panel socket and that control greys out, socket in charge. And fine grain "disappears" in encoded video because codecs throw it away first.

Honest framing: this is a thirty-year-old image operation, and that's the feature. Its historical home was WAS Node Suite's film-grain node, dormant since 2023; this is a maintained equivalent that handles video properly. Put it after the final resize, before saving.

CategoryDeno/Image

Inputs (11)

NameTypeDefaultDescription
imagesIMAGEPhoto or decoded video frame batch, after the final resize.
enabledBOOLEANtrueOff passes the original input through without copying.
amountFLOAT6.000–100Legacy/API grain strength in 8-bit brightness units. The panel maps strength 0..1 to amount 0..12; its default is amount 6. The larger backend range preserves older workflows. 0 passes through.
grain_sizeFLOAT1.000.25–4Scales grain blur only. 1 uses fine/coarse sigma 0.45/1.15px at the 1536px short-edge reference, or at every size in Pixels mode. Image detail is not blurred.
roughnessFLOAT0.250–1Coarse grain proportion. 0.25 uses the original 75:25 fine/coarse mix.
tone_weightedBOOLEANtrueReduce grain near pure black and white, with emphasis on darker midtones.
temporal_modeCOMBOchangingChanging varies the pattern per frame for video. Fixed keeps the same pattern for comparisons.
seedINT20261007010–18446744073709550000Fixed seed reproduces the same grain. Changing uses seed + frame_offset + frame index.
frame_offsetINT00–18446744073709550000For separately processed video chunks, enter the first frame's global index. Otherwise keep 0.
processing_batch_sizeoptINT11–4Frames processed together on CPU. 1 minimizes temporary RAM; larger groups can improve speed. The complete output IMAGE batch still requires RAM. This does not change upstream generation VRAM.
grain_scale_modeoptCOMBOresolutionResolution samples only the grain from a 1536px short-edge reference for similar screen-relative texture. Pixels preserves the original fixed-pixel grain. Strength is unchanged; subpixel grain is naturally filtered. The reference grid is limited to 16,777,216 pixels for memory safety.

Outputs (1)

NameTypeDescription
imagesIMAGEGrain-applied frames on CPU, with original dimensions, dtype and alpha.