Film Grain
The film grain that actually looks like film, not noise
- camera_roll
- camera_roll
- preview
The fastest way to tell a generated image from a photo is texture. Too-smooth skin, airbrushed gradients - real film has grain, and grain is a large part of why a shot reads as "shot" instead of "rendered." FilmGrainLab is the step in the comfyui-jbnodes pipeline that puts that texture in, and it's the node people mean when they say the pack's grain engine got good.
One thing to get out of the way immediately: this node takes a camera_roll (the pack's CAMERA type), not a plain image. You feed it the output of CameraLab, not your Load Image node. Wire a raw IMAGE in and it won't connect. That's by design - grain is part of the camera simulation, and the camera_roll carries the film metadata (format, stock, grain settings) the engine needs to size the grain correctly for the "frame."
How it works
The grain engine is pure numpy/torch/scipy, which matters more than it sounds: earlier versions of the pack rendered grain with moderngl/GLSL shaders, which meant OpenGL, X11, and EGL dependencies on your system. That's gone - the README is explicit that no system-level graphics libraries are required anymore. If you have an old install throwing GL errors, update the pack and they'll disappear.
The actual mechanism is layered noise. algorithmic_octaves controls how many noise octaves are stacked (more octaves = finer detail in the grain structure), and morphological_variance sets whether each octave decays (<1, smoother) or grows (>1, more texture). The engine scales grain size to the physical film frame via film_size, so 8x10 sheet film gets much finer relative grain than 135. RMS granularity is the headline number - the tooltip gives you the anchors: 8 = T-MAX 100, 17 = Tri-X 400.
The inputs that matter
You'll touch maybe four of these and leave the rest alone:
- film_profile - the cheat code. Pick a WOF preset like "Tri-X 400 Push 1 (EI 800)" and it auto-fills the grain sliders with sensible values you can then override.
- rms_granularity (1–50, default 8) - how much grain. Lower = fine T-Max look, higher = pushed Tri-X look.
- film_size - 135 / 120 / 4x5 / 8x10. Smaller formats render grain coarser relative to the frame.
- emulsion_type - Cubic (Tri-X, HP5+, clumpy classic grain) vs Tabular (T-Max, Delta, the fine even stuff) vs Mixed.
The rest is genuinely optional seasoning: halation adds that warm glow around highlights (light piping in the film base), emulsion_softness simulates light scatter in the emulsion, blend_mode changes how grain composites (Soft Light is the safe default), and luminance_peak_bias moves where grain is most visible between shadows and highlights. temporal_entropy is the one people trip on: it's the Z-axis offset of the 3D noise, i.e. it changes the grain seed pattern. Bump it and the grain changes character without touching size or amount.
Outputs are the updated camera_roll (wire this downstream into DeveloperLab) and a preview IMAGE you can actually look at.
Install
It ships inside the pack, so one install covers everything:
cd ComfyUI/custom_nodes
git clone https://github.com/AlbertJBurton/comfyui-jbnodes.git
cd comfyui-jbnodes
pip install -r requirements.txt
Then restart ComfyUI. Or use ComfyUI Manager and search "comfyui-jbnodes" - it's listed there. Dependencies are numpy, torch, colour-science, scipy, and Pillow; the unusual ones (colour-science, scipy) are what Manager needs to fetch for you.
Gotchas
- It's a
CAMERAnode, not anIMAGEnode. CameraLab first, always. - The pack is beta and the author warns of breaking changes between versions. If an old workflow stops loading after an update, that's probably it.
- The grain runs on CPU-side numpy/scipy and can be slow at high resolution or high
precision-style settings - dropalgorithmic_octavesif your queue crawls.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| camera_roll | CAMERA | — | |
| film_profileopt | COMBO | None | WOF film profile preset. Select a profile to auto-fill grain parameters, then override individual values if needed. |
| rms_granularityopt | FLOAT | 8.01–50 | RMS Granularity (8 = T-MAX 100, 17 = Tri-X 400) |
| film_sizeopt | COMBO | 135 | Physical film format. Smaller formats = coarser relative grain |
| emulsion_typeopt | COMBO | Cubic | Cubic = traditional grain (Tri-X, HP5+). Tabular = T-grain (T-Max, Delta) |
| film_gritopt | FLOAT | 0.200–1 | Grain edge sharpness. 0 = soft, 1 = harsh/contrasty |
| halationopt | FLOAT | 0.100–1 | Light piping in film base. Warm glow around highlights |
| emulsion_softnessopt | FLOAT | 0.750–1.5 | Micro-blur simulating light scatter in the emulsion |
| blend_modeopt | COMBO | Soft Light | How grain is composited onto the image |
| luminance_peak_biasopt | FLOAT | 0.500–1 | Brightness zone where grain is most visible (0 = shadows, 1 = highlights) |
| algorithmic_octavesopt | INT | 21–10 | Number of noise octaves. More = finer detail in grain structure |
| morphological_varianceopt | FLOAT | 1.00–2 | Amplitude multiplier per octave. <1 = decay (smoother), >1 = growth (more texture) |
| temporal_entropyopt | FLOAT | 1.000–2 | 3D noise Z-axis offset. Changes the grain seed pattern |
| shadow_ditheropt | FLOAT | 0.000–2 | Additive noise in deep shadows to prevent posterization |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| camera_roll | CAMERA | — |
| preview | IMAGE | — |