H3 Clip Refiner /noEmbryo
Your H3 clips get grainier every join — here's the fix that stays in latent space
- latent
- reference_latent
- model
- positive
- negative
- latent
- report
If you've chained more than three H3 Motion Context clips together, you've seen the texture ratchet. Clip one is clean. Clip four looks like it was shot on pushed film stock, and by clip seven the whole thing is alive with high-frequency grain that nobody asked for. It isn't your sampler, and it isn't random - it's cumulative, and it gets a little worse at every join.
This node is a one-scalar correction for exactly that.
What it's doing
The degradation has a statistic, and the node names it: band_ratio, the standard deviation of the high-frequency band divided by the total standard deviation. Split a latent's spatial content into a blurred low band and everything left over, and that leftover's share of the total sigma creeps up monotonically down a chain - the author's own measurements go 0.3643 → 0.3673 → 0.3702 across three joins. Roughly +4–5% of mid-band energy per clip. Small enough to ignore once, obvious by the sixth clip.
So the fix is: measure that ratio, and rescale the high band until it matches the clip you liked.
The band split is a difference of Gaussians - a separable blur over the latent's H/W gives you lo, with hi = t - lo. The gain is not naively target / current, because scaling the high band changes the total sigma it is a fraction of, so the target moves as you apply it; that undershoots by a few percent, the sort of correction that silently does most of its job and leaves you wondering. Instead the node solves the fixed point, then refines against the measured statistic over a few passes, with the gain clamped between 0.5 and 2.0. Two consequences worth knowing: the low band is untouched, bit-identical, so nothing gets blurred or smeared, and the correction can't invent detail because it's one scalar on one band. The worst it can do is get the gain wrong, which the report tells you.
strength then blends: exactly 0 is a no-op (measure only, even with a target set), below 1.0 gives partial correction, 1.0 is the full move, above 1.0 extrapolates past it. The audio stream is passed through untouched; only the video latent is rescaled.
The two-run workflow people fumble
- Measure. Leave
target_ratioat 0.0 on your first clip. The latent passes straight through unchanged - measure mode is completely safe to leave in the graph - and thereportoutput prints something likeband_ratio=0.3643. - Correct. For every later clip, type that number into
target_ratio.
If you'd rather not copy numbers around, wire clip one's latent into reference_latent and the node measures the target for you. Cleanest setup there is, with one trap: when reference_latent is connected, target_ratio is ignored entirely. People leave both set, the widget looks authoritative, and the node quietly does something else.
To make a chain less grainy than the reference, aim lower than measured - the report's own example is measuring 0.3917 and targeting 0.3800. And read the report after a correction, not just before: it prints the gain it actually applied, and since that gain is clamped, an ambitious target can land short of where you pointed it. Also, the number in the report is averaged over three band scales while the correction targets the mid scale, so don't expect the next clip to read back precisely the value you typed. It's a guide, not an identity.
Inputs and outputs
latent takes the sampler's output - the AV latent from SamplerCustomAdvanced. If you're saving this clip into the archive, grab the sampler's denoised output rather than the raw one. target_ratio and strength are the two widgets above. reference_latent is optional.
Out comes latent (refined) and report (a string - wire it to a text display node, or just read the console, because the node also logs the measurement). Place the whole thing between the sampler and H3 Motion Context Save Latent. That's the only position that helps: fix the clip before it's written into the archive, because once clip four is saved with its grain baked in, later clips inherit it as context.
Install
ComfyUI Manager → search noEmbryo, or:
cd ComfyUI/custom_nodes
git clone https://github.com/noembryo/ComfyUI-noEmbryo
Restart afterwards. No pip dependencies - the pack declares none - and the nodes appear under Add Node → noEmbryo → MiniMax H3. ComfyUI's own H3 support and the H3 VAEs are assumed either way.
What goes wrong
H3ClipRefiner: latent has no audio stream. This node needs a full AV latent - video and audio, as a pair. A bare video latent from a plain VAE Encode won't do. In a normal H3 Motion Context graph you already have the right thing; it only bites when you slot the refiner into a different pipeline.could not measure band_ratio (latent too small?). The Gaussian split needs at least 8 pixels of spatial extent in the latent. Tiny latents have nothing to measure.- Over-correction looks like plastic. Push
target_ratiowell below the measured value, orstrengthabove ~1.5, and you're scrubbing the high band that carried skin texture and fine detail along with the grain. Turn it down until the joins stop jumping; the goal is continuity, not cleanliness.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | The generated AV latent from your H3 sampler (SamplerCustomAdvanced output). | |
| strength | FLOAT | 1.000–3 | Correction strength. 0.0 = measure only (pass through). 1.0 = full match to reference statistics. >1.0 = overshoot (pushes past the reference). |
| context_length | COMBO | 22 | Number of frames at head/tail that are the Motion Context overlap regions. Must match the context_length in H3 Motion Context and Clip Stitcher. |
| reference_latentopt | LATENT | Reference latent (e.g., first clip). When connected, all target statistics are measured from this latent and the input latent is corrected to match it. | |
| modelopt | MODEL | Optional. Connect your H3 model to enable the refine resample: a short low-denoise second sample that regenerates the micro-detail the chain loses. Leave disconnected for the pure statistical corrector. | |
| positiveopt | CONDITIONING | Positive conditioning for the refine resample (same prompt you sampled the clip with). | |
| negativeopt | CONDITIONING | Negative conditioning for the refine resample. | |
| refine_denoiseopt | FLOAT | 0.400–1 | Denoise for the refine resample. ~0.3-0.5 restores detail while keeping the structure (only the high band is transferred, so the background cannot shift). Higher changes content more. |
| refine_stepsopt | INT | 31–50 | Sampler steps for the refine resample. 2-4 is plenty. |
| refine_cfgopt | FLOAT | 1.00–100 | CFG for the refine resample. Match your main sampler. |
| sampler_nameopt | COMBO | euler | Sampler for the refine resample. |
| scheduleropt | COMBO | normal | Scheduler for the refine resample. |
| seedopt | INT | 00–18446744073709550000 | Seed for the refine resample noise. |
| detail_strengthopt | FLOAT | 1.000–1 | How much of the refined high band (detail) to transfer over the original. 0 = statistical only, 1 = full refined detail. The low band (structure) is always kept from the original, so the background never shifts. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |
| report | STRING | — |