Emulsion Grade
Your entire color pass behind one dropdown
- image
- IMAGE
Everything after the sampler is deterministic pixel work, and most people do it badly - a gamma node here, no grain at all, then they wonder why the render still reads as generated. Emulsion Grade covers the whole layer in one node: pick a preset and get levels, tone curve, contrast, white balance, HSL, vignette, bloom, halation and grain, in a single pass, no model involved. No diffusion pass, no face rewriting. The pitch holds up.
Why you'd reach for it
The cheapest single move for making an image read as captured rather than generated is grain - the community has been saying so for years, and it's an additive-noise pass, not something to prompt for. Add the lens-stuff AI never produces (vignetting, halation, a touch of chromatic aberration) plus a small saturation pull, and you've fixed the most common "AI look" complaint without generating anything new. That is exactly the pass this node is.
The alternative historically was a chain: WAS's film grain node, a gamma node, something for the vignette, a colour-match at the end. WAS Node Suite is still the broadest of those packs and dormant since 2023, and the small clean packs don't cover the whole set. The other selling point is consistency: one preset is one set of numbers, so a 40-image batch and a video sequence get the same look frame after frame - re-grading with img2img gives you neither repeatability nor numbers.
What it actually does to the pixels
A preset is JSON, and every field is optional - absent means neutral, so the file only says what it does. grading.py applies them in one fixed order: levels → tone curve → brightness → contrast → temperature/tint → hue → saturation → colour balance → HSL mixer → .cube LUT → clarity → sharpness → bloom → halation → chromatic aberration → vignette → grain. Pure torch, on ComfyUI IMAGE tensors, device and dtype preserved. The tone curve is a Catmull-Rom spline through your points; grain is luminance-modulated noise, loudest in the midtones and quieter at the extremes, like real film.
Then the whole result is blended back toward the original by strength. That detail matters: strength scales the entire grade at once, grain and vignette included, so 0.7 is the natural way to say "that preset, softer" instead of fighting individual fields.
The widgets that matter
- image - feed it the final-resolution image. Grain is applied at whatever size arrives, so grading a 512px preview and then upscaling turns film grain into soft blobs.
- preset - built from the JSON files in the pack's
presets/folder. Fourteen ship: two B&W,cold,warm,intense,spectacular,vintage,moody_garden,retro_01–04, andkodak_portra_160/kodak_ektachrome_400x. - strength - 0 is the untouched original, 1 the full grade. Above 1 extrapolates the grade and clamps it back into range at the end; 2.0 is a special-occasion setting, since the extrapolation clips.
- seed - reproducibility for the grain only. If the preset has no grain block, seed does nothing at all.
Output is a single IMAGE, so it feeds Preview Image, Save Image, a video combine, or anything else that eats an image.
Installing it
ComfyUI Manager → search Emulsion, or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/24latents/ComfyUI-Emulsion.git
Restart ComfyUI. There is genuinely nothing to install - requirements.txt is a single comment in French explaining that torch already ships with ComfyUI. No model downloads, no CUDA extensions, no pip roulette, which puts it well outside the usual custom-node dependency pain. Two example workflows also show up under Workflow → Browse Templates → ComfyUI-Emulsion; the grade one puts four presets on one image side by side.
Where people get burned
A broken preset is only as loud as your console. If the preset file is missing, isn't valid JSON, or points at a .cube LUT that isn't where it expects, the error is logged and the image passes straight through unchanged. Your batch won't crash, but the picture will look ungraded - check the log before assuming the node is broken. The LUT folder ships empty (just a .gitkeep), so any preset with a "lut" field needs you to drop the .cube into custom_nodes/ComfyUI-Emulsion/presets/luts/.
Your presets live inside the pack folder, which is also where Emulsion Extract writes. A git pull, a Manager update or a clean reinstall can take them with it - keep copies elsewhere. And a preset you drop in shows up in the dropdown on browser refresh, not instantly; no restart needed, an F5 is.
Grade last. Grain and vignette are resolution-dependent. Upscale first, grade second, Save Image third.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| preset | COMBO | 14 options: black_and_white_01, black_and_white_02, cold, intense, kodak_ektachrome_400x, kodak_portra_160, +8 | |
| strength | FLOAT | 1.000–2 | — |
| seed | INT | 00–4294967295 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |