Film Simulation V4.1 (HDR Capable)
33 film stocks, zero model downloads — this is the heart of the pack
- image
- hdr_image
- preview_sdr
This is the node the pack is named after, and it's not what ComfyUI usually is. There's no diffusion model, no checkpoint, no prompt - it's parametric color science running on numpy and OpenCV, tuned by a films.json that ships in the repo and describes 33 film stocks. Kodak Portra 160/400/800, Ektar, Gold, Ektachrome, Kodachrome, Fuji Pro 400H, Velvia, Provia, Astia, Superia, Cinestill 400D and 800T, the whole LomoChrome family, and a proper row of black-and-whites (Tri-X, T-Max, Acros, HP5, Pan F, XP2, Adox Silvermax, Rollei Ortho). No weights to download, nothing to train, and it runs on CPU in well under a second. The "V4.1" and "(HDR Capable)" in the name aren't marketing fluff - this is the current iteration of a real photographic pipeline.
How it works. Everything happens in linear light. If is_linear_input is off it linearizes a gamma-encoded image first; then it applies physical white balance from Kelvin plus tint, auto or manual exposure (auto targets 18% grey using a log-average), a per-stock 3×3 color matrix, optional local contrast in the log domain, halation/bloom driven by highlight luminance, per-channel log-pivot contrast curves, luminance-masked film grain with channel crosstalk, and split toning for shadows/highlights. Finally it blends the graded result with the original by effect_strength and fixes the black point. The grain is seeded from the CPU tick count, so it varies run to run - that's film for you.
The inputs that matter. Ten of them, but you'll live in maybe five:
film_type- the 33-stock menu above. Start here.is_linear_input(default true) - this one decides if the result looks right. True when fed by the DNG reader (which outputs linear). False if you're feeding a normal sRGB image straight from another ComfyUI chain. Get it backwards and everything turns muddy.auto_exposure(default on) andexposure(EV offset, −5 to +5) - the exposure stack.effect_strength(0–1) - the "how much film vs. how much digital" dial. At 0 it's the original image with WB applied; at 1 it's the full stock.grain_power(0–5) andhalation_power(0–5) - how loud the film grain and the glow around bright areas are. Cinestill 800T fans live at the top of the halation range.wb_temperature_K(2000–12000) andwb_tint(−50 to +50) - your white balance override, which is why the DNG reader's own WB mode matters less than you'd think.local_contrast(0–2) - punchy midtone clarity, mostly for color stocks.
What comes out. Two images, and they're not the same thing. hdr_image is the graded result still in linear, scene-referred space - values past 1.0 exist, which is the entire point of an HDR pipeline. That wires into HDR Merge (Gainmap) or straight into Save AVIF/HEIC HDR. preview_sdr is the same grade run through a filmic tone curve and gamma 2.2, clamped to 0–1 so it looks right on a normal monitor - that one goes to Save SDR Preview. Mixing them up is the most common beginner fumble in the whole pack: save the linear one as an 8-bit PNG and you get a dark, clipped mess.
Install. Manager → search "Comfy-FilmSimulator", or git clone https://github.com/zhuyanan/Comfy-FilmSimulator into ComfyUI/custom_nodes, restart. The pack's real requirements are numpy, opencv-python, rawpy, and pillow-heif; the film node itself just needs numpy and OpenCV, both of which ComfyUI almost certainly already has. If a preset looks wrong, it's not a broken install - it's a color decision in films.json you can edit, and the repo ships a "Film Preset Adjust" cheat sheet for exactly that.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| film_type | COMBO | Neutral Reference (Standard) | 33 options: Neutral Reference (Standard), Kodak Portra 160, Kodak Portra 400, Kodak Portra 800, Kodak Ektar 100, Kodak Gold 200, +27 |
| wb_temperature_K | INT | 56002000–12000 | — |
| wb_tint | FLOAT | 0.0-50–50 | — |
| auto_exposure | BOOLEAN | true | — |
| exposure | FLOAT | 0.0-5–5 | — |
| effect_strength | FLOAT | 1.000–1 | — |
| grain_power | FLOAT | 1.00–5 | — |
| halation_power | FLOAT | 1.00–5 | — |
| local_contrast | FLOAT | 0.00–2 | — |
| is_linear_input | BOOLEAN | true | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| hdr_image | IMAGE | — |
| preview_sdr | IMAGE | — |