H3 Chain Normalize (texture + colour)
Your H3 chain sharpens shot by shot. This node levels it after the fact.
- images
- images
The problem: chaining MiniMax-H3 is a drift machine
MiniMax-H3 only generates clips in blocks of roughly 10-15 seconds. To get a scene longer than that you chain blocks together, and that's where things quietly go wrong. Every shot after the first is generated from the previous shot's output, and that feedback loop accretes: fine texture compounds roughly 1.3x per join, the colour grade wanders, and a long take that opens looking like film ends up looking like an over-sharpened demo reel.
The ComfyUI-H3-Multishot pack (jlucasmcrell's chaining toolkit, the most thorough one around for H3) attacks this from two sides. In-loop controls like chain_gain_control and master_normalize damp the drift while the chain renders. H3ChainNormalize is the second side: it fixes what survives, on the decoded frames, right before they become a video. The author's release notes call it the fix for the "known limit" - the slow sharpening ratchet across long chains.
Where it drops in
One wire: between the sampler's IMAGE output and CreateVideo. The pack's samplers emit the finished chain as a frame batch; this node processes the whole batch as a tensor and hands the levelled frames on. No model, no network, no extra dependency - pure torch math on frames you already have.
How it works
Three ideas, all right there in the source:
- A baseline from shot 1, not the average. It measures contrast-normalised Laplacian energy (a texture metric) across roughly the first 10 seconds of shot 1, after skipping the first ~2 seconds, and takes the median. Why skip the opening? H3 starts with an exposure fade-in (luma climbs for ~1.7s), and folding that in drags the baseline dark and softens the whole piece.
- Structure-band softening, not a blur. When a later frame's texture exceeds the baseline by more than the deadband, the correction is applied only to the 5-17px "structure band" (the difference of two box blurs). Grain and sensor noise pass through untouched. The author measured why: a whole-frame blur flattens drift just as well but costs ~20% of the fine band - the "camcorder texture" you want to keep. The structure-only version took an 84-second lamp-lit gauge from 1.49x drift down to 1.22x with grain intact.
- Histogram-matching for colour. With
colour_matchon (it is by default), every frame is histogram-matched per RGB channel to a shot-1 reference frame. That's the half of the drift no sharpening control ever touched - the grade quietly wandering warm or cool across a long chain.
The correction amount is EMA-smoothed so it eases in instead of stepping at a shot boundary.
The inputs you'll actually touch
baseline_seconds (10), skip_seconds (2), deadband (1.06) and strength (1.0) ship at values the author eye-approved - leave them alone for a first pass. fps (24) just converts seconds to frames for the baseline window; keep it matching your render. The two you might actually move: strength, for more or less pull-back, and colour_match off, for texture-only levelling on a stylized grade.
The single output is images, wired straight into CreateVideo or SaveVideo.
Install
Shared with the whole pack, so you do this once:
cd ComfyUI/custom_nodes
git clone https://github.com/jlucasmcrell/ComfyUI-H3-Multishot
Or search H3 Multishot in ComfyUI Manager - the pack is on the Registry as comfyui-h3-multishot. You need ComfyUI v0.30.0+ (native MiniMax-H3 support) plus the H3 checkpoint, text encoder and VAEs; the README links all of them. Notably there are no pip dependencies here - the pack deliberately uses only libraries ComfyUI already ships, so this node won't fight your CUDA build.
Gotchas
- It only levels texture, colour and exposure. Framing or motion drift is a different failure - re-run the chain instead.
- Feed it a whole chain, not a random batch. It anchors everything to shot 1's opening seconds, so a batch that isn't a shot-1-then-more sequence gets re-graded toward the wrong reference.
- If your opening shot has an exposure ramp longer than
skip_seconds, raiseskip_secondsor the baseline goes dark and the whole take softens. - Under 8 frames it passes the batch straight through with a console note - nothing to level in a still.
One honest caveat, straight from the README: texture drift is not fully fixable after the fact. The only lever is blur, and blur removes real detail along with the invented kind. H3ChainNormalize is the careful version of that trade - it targets the structure band so the grain you want survives. It won't make an eight-shot chain look like a three-shot one. But past about four shots, it's the difference between a take that reads as sharpened plastic and one that holds. One node, zero config - just drop it in.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| baseline_seconds | FLOAT | 10.02–30 | How much of shot 1 sets the house level. |
| skip_seconds | FLOAT | 2.00–10 | Skipped at the very start. H3 opens with an exposure fade-in (luma climbs for ~1.7s); including it drags the baseline dark and makes the whole piece get softened. |
| fps | FLOAT | 24.001–120 | — |
| strength | FLOAT | 1.000–2 | How hard excess structure is pulled back. 1.0 matched the eye-approved result. |
| deadband | FLOAT | 1.061–1.5 | Frames within this ratio of the baseline are left alone, so shot 1 stays untouched. |
| ema | FLOAT | 0.100.01–1 | Smoothing on the correction. Low values ease it in so nothing pops at a window join. |
| colour_match | BOOLEAN | true | Histogram-match every frame to a shot-1 reference. This is what fixes the grade wandering across a long chain. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |