TS Film Emulation
Cinematic film looks without leaving the graph
- image
- IMAGE
AI images have a tell, and it's usually not the hands - it's the color. Perfect, sterile, no grain, everything evenly lit and evenly graded. TS Film Emulation is the one-stop fix: pick a Kodak Portra, Fuji, or Cineon-style preset, and your render suddenly has a memory of having been shot on film.
It's the kind of node you bolt onto the end of a pipeline and forget about. Wire IMAGE in, get a graded IMAGE out, no model download, no API, works on every frame of a video batch. If you've ever gone down the rabbit hole of hunting .cube LUTs on random forums, this also gives you a clean home for them.
How it works
The node does three deterministic things, all pure pixel math:
- A film preset or a LUT. The
film_presetdropdown has built-in analog looks (Kodak, Fuji, Cineon-style), or you can flip to "External LUT" and pick any.cubefile from themodels/luts/folder - drop the file in there, and it appears inlut_choiceafter a refresh. That folder is where your own downloaded LUTs go, and the node reads them at runtime. - Gamma + contrast.
gamma_correctionapplies the LUT in sRGB space (keep it on - that's the recommended path for most.cubefiles), andcontrast_curveis a mid-gray pivot where 1.0 is unchanged, above 1.0 is punchier. - Grain.
grain_intensityandgrain_sizeadd film grain, which is the single cheapest move for killing the AI look - the post-processing doc makes the point that film grain is where the community's strongest "how to break the plastic look" arguments live.
There's also warmth (positive warms, negative cools) if the preset is close but the white balance isn't, and film_strength / lut_strength to blend any of it back toward the original.
The inputs that matter
Honestly, for most people: film_preset, and then film_strength if you want the look dialed down to 50%. Everything else is refinement. grain_intensity at its 0.02 default is subtle; crank it if you want it visible, or set it to 0 for a clean-but-graded image. contrast_curve around 1.1–1.2 is the usual "punch it up" range - don't chase 3.0, you'll just crush your shadows.
One output, IMAGE, which drops straight into a preview or saver.
Installing it
Part of comfyui-timesaver - install via ComfyUI Manager (search "Timesaver") or:
cd ComfyUI/custom_nodes
git clone https://github.com/AlexYez/comfyui-timesaver
cd comfyui-timesaver
python -m pip install -r requirements.txt
Restart ComfyUI and it's there. No weights to download. If you want external LUTs, create models/luts/ and drop .cube files in.
Gotchas
The biggest surprise is that LUTs only show up in the dropdown if they're in the right folder - a .cube sitting in input/ is invisible to this node. And remember the LUT folder resolution: the pack looks in its own bundled luts/ directory first, then models/luts/, so when you drop your own files in, put them in ComfyUI/models/luts/ where extra_model_paths.yaml and the rest of ComfyUI expect them. Other than that, it's a fire-and-forget node - the kind you install once, use in every workflow, and eventually stop noticing, which is exactly the compliment a color node wants.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Image to apply the film look to. | |
| enable | BOOLEAN | true | Kept for older workflows; the node hides it — use Bypass instead. If disabled, the image passes through unchanged. |
| film_preset | COMBO | Built-in analog film look. Choose 'External LUT' to use a .cube LUT from lut_choice instead. | |
| lut_choice | COMBO | External .cube LUT file (from the luts folder) applied when film_preset is 'External LUT'. 'None' = no LUT. | |
| lut_strength | FLOAT | 1.000–1 | Blend amount of the external LUT. 0 = original, 1 = full LUT. |
| gamma_correction | BOOLEAN | true | Kept for older workflows and ignored: since 12.13 a .cube LUT is always applied to the sRGB values it was made for. |
| film_strength | FLOAT | 1.000–1 | Blend amount of the selected film preset. 0 = original, 1 = full preset. |
| contrast_curve | FLOAT | 1.000–3 | S-curve contrast around mid-gray; black and white are never clipped. 1 = unchanged, >1 = more contrast. |
| warmth | FLOAT | 0.00-1–1 | White balance: positive warms, negative cools. Black stays black. |
| grain_intensity | FLOAT | 0.020–0.5 | Strength of added film grain. 0 = no grain. |
| grain_size | FLOAT | 0.50.5–5 | Scale of the grain particles. Higher values give coarser grain. |
| fade | FLOAT | 0.000–0.5 | Matte film look: lifts the blacks, whites stay white. 0 = off. |
| shadow_saturation | FLOAT | 1.000–2 | Color saturation in the shadows. 1 = unchanged, <1 = desaturated. |
| highlight_saturation | FLOAT | 1.000–2 | Color saturation in the highlights. 1 = unchanged, <1 = desaturated. |
| grain_speedopt | FLOAT | 1.000.05–1 | For clips: how often the grain pattern is redrawn. 1.0 = a new pattern every frame (lively, digital). 0.5 = one pattern held for two frames, 0.25 for four — closer to scanned film, where the grain does not race the action. No effect on a single image. |
| grain_seedopt | INT | 00–4294967295 | Seed for the grain. The same seed gives the same grain on the same frames, so a re-render matches the take you graded. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |