Latent Channel Match
Latent-space color matching, before the VAE ever sees it
- target
- reference
- mask
- latent
Latent Channel Match is the "match color grade" of the latent world. It takes the per-channel mean and standard deviation from one latent (the reference) and transfers them onto another (the target), so the target's channel distribution - and therefore its overall brightness, contrast and color balance - lands where the reference sits. Think of it as palette transfer done in the compressed space the diffusion model actually works in, instead of as a post-process on a decoded image.
Why bother doing this in the latent? Because the VAE is lossy, and every decode-reencode round trip costs you detail. If you want two generations to share a common look - say, you generated a reference image you love and a second image that's close but "feels different" - matching channels while both are still latents keeps you in the model's native space. The KB's VAE notes spell out the same principle: a shared latent space is exactly what lets you skip the decode-encode cycle entirely. This node is that idea, packaged as a one-stop.
How it works
The mechanism is deliberately simple. For each channel of the target latent, the node z-scores it (subtract the target channel's mean, divide by its std), then rescales to the reference channel's mean and std:
matched = (target - target_mean) / target_std * reference_std + reference_mean
That's the whole trick. It's per-channel and independent, so no cross-channel bleed. mix blends between the original target (0) and the fully matched result (1), which is how you get a hint of the reference's grade rather than a wholesale takeover. The optional mask limits the matched result to masked regions, so you can match only the sky or only a character's lighting.
Two useful details the node handles for you: the reference latent can be a single sample that broadcasts across the whole target batch, and the two latents don't even need matching spatial sizes - only the channel statistics matter. Wire the latent output into a sampler, another channel op, or the VAE.
The three inputs that matter
- target - the latent you're changing.
- reference - the latent providing the look.
- mix - how much of the match to apply. Start at 0.3–0.5 and nudge.
The rest is the optional mask.
Installing it
Part of the Skoogeer-Noise pack. ComfyUI Manager: search "Skoogeer-Noise", install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/ttulttul/Skoogeer-Noise
# restart ComfyUI
No model downloads - just the pack's standard deps (torch, numpy, einops, pyyaml).
Gotchas
- "It did nothing." If target and reference already have nearly identical channel stats, the match is a no-op by definition. Generate the reference with a different seed or a different model first.
- Reference batch shape. A single reference sample broadcasts; a multi-sample reference must match the target's sample count or the node errors. Keep the reference as one latent unless you know what you're doing.
- It only matches moments, not semantics. This transfers distribution, not content - the target keeps its own shapes and subjects, it just adopts the reference's brightness/contrast/color envelope. If you want actual content from the reference, look at channel merge nodes instead.
- Flux.2 latents are patchified (2x2, 128-channel) - if you're matching those, consider wrapping with the pack's unpatchify/patchify nodes so the channel stats are measured on true spatial neighbors.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| target | LATENT | Latent whose channels will be normalized to match the reference stats. | |
| reference | LATENT | Reference latent providing the per-channel mean/std to transfer. | |
| mix | FLOAT | 1.000–1 | Blend factor between the original target and the fully matched result. |
| maskopt | MASK | Optional mask to limit the matched result to masked areas. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |