Nodes/ComfyUI-GeN13/Color Match Temporal (GeN13)
ComfyUI Node

Color Match Temporal (GeN13)

Your image-to-video clip goes blue halfway through. This is the node for that

By heigen13·Created 23 days ago·Updated 11 days ago· 1
Color Match Temporal (GeN13)
  • images
  • reference
  • images
◄enabledtrue►
◄methodwhite balance►
◄modetemporal►
◄smooth_frames8►
◄strength1.00►
◄cube_name►
◄tone_curvegamma►
◄after_cutmatch new shot start►
◄skin_anchortrue►

Image-to-video models drift. You give Wan or LTX a nice warm still, and by second eight the skin is magenta-adjacent and the sky has gone teal. Nothing in the pipeline is "wrong" - the model is just re-deciding the white balance frame by frame, and each small decision stacks. You can chase it with a colour-correction node and a lot of swearing, but that's one fixed correction for a moving target.

Color Match Temporal (GeN13) is the moving-target version. It takes your decoded frames, takes a reference image (your first frame, almost always), and pulls every frame back toward that reference - per frame, but smoothed over time so the correction doesn't itself flicker.

Where it sits

After VAE Decode, before whatever writes the file. In the pack's own order, color match comes first and Degrease second - Degrease's input tooltip literally says "connect after Color Match Temporal", which tells you the author built both for the same portrait-video workflow.

Wire images from the decode, wire reference from your source still (the same image you fed the image-to-video node, or frame 0 of the batch), and send the output to Video Combine, Save Image, or onward to retouching.

What it actually does

The interesting part is the default method, white balance, and how it decides what "drift" means. Rather than trusting the whole frame, it finds pixels that didn't move - it works on a blurred, small, scene-linear thumbnail, computes per-pixel log-luminance difference against the reference, subtracts the global median shift, and keeps only pixels within about 8% of it. Those are your static background. If at least ~15% of the frame qualifies it trusts the estimate fully; between 5% and 15% it fades the correction down; under 50 good pixels it measures nothing.

That's why a big white shirt or a close-up can't fool it: a moving subject isn't static, so it never enters the average. And the gains are clamped to about ±8%, so even when it has to fall back to a cruder whole-frame estimate, it under-corrects rather than wrecking the shot. Under-correcting is the right failure.

Then it smooths. In temporal mode the per-frame correction is filtered over a window of smooth_frames on each side (default 8), guided by a step detector so the smoothing doesn't bleed across a hard cut. global mode applies one correction to the whole clip - useful when the drift is a single uniform wash.

Scene cuts are detected separately: each frame gets a coarse 6×4 Lab layout vector, and a cut is flagged when its similarity to the previous frame drops hard relative to its neighbours. What happens after a cut is your call via after_cut - match each new shot to its own start (the default, and the sane one), keep matching everything to frame 0, or stop correcting.

mkl and reinhard are the older, heavier methods: mean/covariance transfers that move brightness as well as colour cast. If you pick either, tone_curve (gamma keeps your black and white points, linear is the legacy curve) decides how the new luminance is redistributed. For a clip you're trying to stabilise, white balance + temporal is the combination you want; the other two are for matching a render to a plate.

The fields that matter

  • method - leave it on white balance. It's the one that fixes the cast without dimming or brightening your clip.
  • smooth_frames - 8 is a good default. Raise it to 16–24 on long slow clips; set it to 0 only if you genuinely want each frame independently matched, which is how you get shimmer.
  • strength - the honest dial. 1.0 matches fully, 0.4–0.6 keeps some of the model's own warmth, which sometimes looks better than a technically perfect match.
  • cube_name - optional. Type a name and it exports the correction as a .cube LUT into ComfyUI_LayerStyle/lut if you have LayerStyle installed, otherwise into output/luts. Handy if you want to grade the raw clip in Resolve instead. There's also a skin-tone anchoring stage the README advertises - a Lab skin mask that nudges each frame's skin chroma toward the reference's, which is what stops a face going redder after a cut to a close-up. In the current source it runs as a finishing step after the match; if your reference has no skin in it, it logs a note and skips rather than guessing.

Output is a single images batch. That's it - same frames in, corrected frames out.

Installing

ComfyUI Manager: search GeN13, install, restart. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/heigen13/ComfyUI-GeN13
pip install -r ComfyUI-GeN13/requirements.txt

That requirements file is one line - nvidia-ml-py, for the pack's little monitor strip beside the Run button - and nothing in this node needs it. Portable builds: python_embeded\python.exe -m pip.

When it doesn't do much

If your clip barely has a static background - a fast pan, a full-frame close-up the whole way through - the white-balance estimator has nothing to anchor on and the ±8% cap means the correction comes out mild. Push strength to 1 and consider mkl instead.

And check your reference. The node assumes it's the look you want. Feed it a frame from the middle of the clip and you'll get a clip that matches its own drift.

CategoryGeN13/color

Inputs (11)

NameTypeDefaultDescription
imagesIMAGEFrames: the video frames to correct, usually from VAE Decode.
referenceIMAGEReference: the target color, usually the first-frame image.
enabledBOOLEANtrueEnabled: BYPASS outputs the frames unchanged.
methodCOMBOwhite balanceMethod: white balance corrects color cast only and keeps the original brightness (recommended); mkl and reinhard also match brightness.
modeCOMBOtemporalMode: temporal corrects each frame and smooths over time (recommended); global applies one correction to the whole clip.
smooth_framesINT80–120Smoothing radius: frames averaged on each side; higher values are steadier. 0 corrects each frame on its own.
strengthFLOAT1.000–1Strength: 0 keeps the original frames, 1 matches the reference fully.
cube_nameSTRINGLUT name: leave empty to skip. Saves a .cube to ComfyUI_LayerStyle\lut, or to output\luts if LayerStyle is not installed.
tone_curveoptCOMBOgammaTone curve: for mkl and reinhard only. gamma preserves the black and white points (recommended); linear is the legacy curve.
after_cutoptCOMBOmatch new shot startAfter a cut: match each new shot to its own start (recommended), match every shot to the first frame, or stop correcting.
skin_anchoroptBOOLEANtrueSkin anchor: after matching, shifts each frame so its skin tone equals the reference skin; fixes redder skin after a cut to a close-up.

Outputs (1)

NameTypeDescription
imagesIMAGE—