xLUT
Give Your Renders a Film Grade Without Re-Rolling the Seed
- image
- lut_image
- image
- lut_info
- lut
The fastest way to ruin a good render is to re-roll the seed because the colors feel off. A LUT is the deterministic answer that's been hiding in plain sight: a lookup table that maps every input color to an output color, applied in milliseconds, no sampler involved. That's what xLUT does - and it goes one step further, because it can also build a LUT from a reference image you feed it. It's part of MKRShift Nodes, a big solo-maintained pack from Cris K B that bundles look development, finishing, and output utilities.
How it works
xLUT has two personalities, and which one you get depends on how you feed it.
First: apply an existing grade. You pick a .cube LUT from the node's dropdown, and xLUT loads it and runs your image through a trilinear lookup. No model, no VRAM, just a table lookup with a strength blend. If the image has an alpha channel, it passes it through untouched - only RGB gets graded.
Second, the cleverer mode: generate a LUT from a reference image. Wire a lut_image in and xLUT builds a 3D cube (33³ by default) by computing per-channel quantile curves from the reference's histogram, then applies a saturation and midtone-tint adjustment scaled by generated_style_strength. This is statistical look-matching, not a literal grade clone - it moves your palette toward the reference's feel the way a color-match node does, but baked into a reusable LUT.
The inputs that matter
Only two required inputs, and one of them is unusual:
image- the frame you're grading. Feed it anything: a raw sampler output, an upscaled image, a single frame from a video batch.settings_json- a multiline JSON string holdinglut_name,strength,generated_lut_size,generated_style_strength, andapply_generated_lut. You rarely touch it by hand:xLUTships a custom frontend widget, so you get a real dropdown and sliders that serialize into this field.lut_image(optional) - the reference that becomes the generated LUT. Without it,xLUTjust applies whateverlut_namepoints at.
For the generated mode, generated_lut_size (4–64) is your quality knob - 33 is the standard, 17 is fine for a quick preview - and generated_style_strength pushes the saturation/tint more aggressively. apply_generated_lut: false is a genuinely useful trapdoor: it makes the node emit the LUT without applying it, which is exactly what you want when you're building a look to save, not to grade this pass.
What comes out
image- the graded result.lut_info- a status string. Trust it; it tells you whether the LUT applied, whether it fell back to passthrough, and why.lut- theMKR_LUTpayload. This is the pack's LUT currency: wire it intoxLUTOutputto save it as a.cubefile, or into other LUT-aware nodes.
Installing it
ComfyUI Manager → search "MKRShift Nodes", install, restart. Or the manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes
No pip requirements, no model downloads. Restart ComfyUI and the node shows up under MKRShift Nodes → Color → LUT.
Where people get burned
The dropdown starts empty - the pack ships no LUTs. The node scans its own luts/ folder plus ComfyUI/input/luts/, so drop your .cube files there and refresh. If you get "No LUT selected", the node is passing your image through untouched; that's by design, not a bug. And if a lut_info message says a LUT "was not found," the file isn't where the scanner looks. One more expectation to set: a generated-from-image LUT gets you a similar feel, not frame-perfect color matching - use it to unify a batch, not to copy a grade exactly.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| settings_json | STRING | {"lut_name":"None","strength":1.0,"generated_lut_size":33,"generated_style_strength":1.0,"apply_generated_lut":true} | — |
| lut_imageopt | IMAGE | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| lut_info | STRING | — |
| lut | MKR_LUT | — |