Temporal Face Detailer (SDXL video)
Fix video faces without the flicker — a FaceDetailer that actually tracks
- image
- model
- clip
- vae
- positive
- negative
- lora_stack
- reference_image
- image
- face_masks
- debug_overlay
- face_tracks
If you've ever run Impact Pack's FaceDetailer on every frame of a video, you already know why this node exists. Each frame gets a fresh img2img pass with fresh noise, the face sharpens up nicely - and then morphs, shimmers, or straight-up changes identity a few frames later. Per-frame detailing fixes faces and creates flicker in the same gesture.
Temporal Face Detailer is the video-grade version of that loop. It detects and tracks faces across the whole frame batch, re-details each tracked face with a fixed seed and stabilized crops, and blends the results with optical flow, so you get the detail pass without the strobe light. One node does the entire pipeline: detect → track → detail → smooth → paste back. It's SDXL-only - you feed it a normal checkpoint's MODEL, CLIP, and VAE, and frames from a video loader like VideoHelperSuite's Load Video (Upload).
How it works
The anti-flicker magic is layered, and worth knowing - every lever is a widget on this node:
- Tracked, stabilized crops. Faces are linked into identity tracks frame-to-frame (greedy association on IoU + center distance, with a constant-velocity prediction). Each track's crop is centered on the facial-landmark centroid instead of the wobbly detection box (
crop_anchor: landmarks), and the window is smoothed over time - the sampled region barely moves, so neither does the result. - Fixed per-track seed and noise. Every frame in a track is sampled from the same noise - the source literally replicates one noise tensor across the track's frames. Single biggest identity-stability lever, and on by default (
noise_mode: fixed_per_track). - Moderate denoise. Default 0.35, and the README's 0.3–0.45 sweet spot is right.
denoise_maxadds adaptive denoise: steady frames stay locked at the base value while high-motion frames ramp up, where correction is most needed and flicker is masked by the motion anyway. - Flow-guided temporal blending in pixel space (
flow_strength) and optionally latent space (latent_blend). Optical flow from the originals warps the running result onto each new frame, occlusion-aware so motion doesn't smear. Latent blending smooths in the VAE's semantic space and lets you raise denoise before flicker shows. - Color match + feathered paste-back. Each crop's exposure is pinned to its source (no brightness pulsing), and only the face region is composited back, so backgrounds stay pixel-exact.
The inputs that matter
Wire image, model, clip, vae, type a positive/negative prompt (the widgets are fine - positive/negative conditioning inputs override them if you prefer encode nodes), and hit go. Defaults are genuinely good for a first pass. The ones you'll actually touch:
denoise- the main quality-vs-consistency knob.noise_mode- leave it onfixed_per_track.detector-insightfacefor realistic faces, ayolo:...anime model for stylized ones.reference_image- a clean face photo to anchor identity against (see below).chunk_size- lower it (default 4) if you OOM.detail_every- set 2–4 on long clips; it samples keyframes and flow-propagates the rest for a big speedup.
Outputs: image goes to Video Combine (the final video), face_masks to a preview/save, debug_overlay to a second Video Combine if you want to inspect tracking, and face_tracks to Face Track Preview or a second-pass detail.
Installing it
ComfyUI Manager - search "Temporal Face Detailer" - or the manual way:
cd ComfyUI/custom_nodes
git clone https://github.com/nikythebikky/ComfyUI-Temporal-Face-Detailer
pip install -r ComfyUI-Temporal-Face-Detailer/requirements.txt
Then restart. The heavy lift is insightface (RetinaFace) plus onnxruntime - swap for onnxruntime-gpu to run detection on CUDA. InsightFace's models auto-download into models/insightface, shared with ReActor and friends; if it isn't installed, the nodes fall back to OpenCV YuNet, then to a Haar cascade.
Where people get burned
Anime faces. Realistic-face detectors miss or jitter on stylized characters, and unstable detection is a root cause of flicker no downstream blending can fix. Install ultralytics and drop face_yolov8m_anime.pt (same weights Impact Pack uses) into models/ultralytics/bbox/, then pick yolo:bbox/face_yolov8m_anime.pt. The README's recipe for WAN Animate post-pass: keep denoise low (~0.15), add latent_blend 0.4, anchor with a reference_image, use raft_small flow on fast motion.
Identity drift over a long clip. Temporal blending keeps frames consistent with each other, but the whole sequence can drift away from who it should be. Connect a reference face to reference_image - it nudges each frame's init latent toward the reference (scaled by reference_strength × denoise, so it won't ghost at low denoise) and color-anchors the face region. For strong identity, patch the model with IPAdapter FaceID upstream and feed the patched model in.
Turing GPUs. Tested on a 22 GB 2080 Ti, the pack explicitly warns: don't enable SageAttention on Turing - it silently produces NaN/black frames. fp16/bf16 only, no fp8 needed.
Flicker you can't kill. Check debug_overlay first - if the boxes themselves flicker or drop out, no smoothing fixes the root cause. Switch detectors before you touch denoise.
Inputs (45)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | video frame batch | |
| model | MODEL | — | |
| clip | CLIP | — | |
| vae | VAE | — | |
| positive_text | STRING | detailed face, sharp eyes, high quality skin texture | — |
| negative_text | STRING | blurry, deformed, low quality | — |
| detector | COMBO | insightface | yolo:* entries are ultralytics models found in models/ultralytics — use an anime face model (e.g. bbox/face_yolov8m_anime.pt) for stylized characters |
| det_threshold | FLOAT | 0.500.05–1 | — |
| min_face_size | INT | 248–1024 | — |
| max_faces | INT | 41–16 | — |
| detector_device | COMBO | cuda | 2 options: cuda, cpu |
| iou_threshold | FLOAT | 0.250.05–0.95 | min overlap to link a detection to a track |
| max_track_gap | INT | 100–120 | frames a face may vanish (occlusion) before its track ends; gaps are interpolated |
| min_track_length | INT | 21–120 | — |
| crop_factor | FLOAT | 1.71–4 | — |
| crop_smoothing | FLOAT | 0.800–1 | temporal smoothing of the crop window (anti-jitter) |
| crop_anchor | COMBO | landmarks | landmarks centers the crop on the facial-landmark centroid (far more stable than the detection box); falls back to bbox when the detector yields no landmarks |
| align_rotation | BOOLEAN | false | rotation-register crops so the eye line is horizontal in every sampled crop (similarity transform, inverse-warped on paste-back); needs a landmark-capable detector |
| guide_size | INT | 768256–2048 | resolution faces are resampled at |
| max_size | INT | 1024256–2048 | — |
| seed | INT | 00–18446744073709550000 | — |
| steps | INT | 201–100 | — |
| cfg | FLOAT | 7.00–30 | — |
| sampler_name | COMBO | 44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38 | |
| scheduler | COMBO | 9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3 | |
| denoise | FLOAT | 0.350–1 | main quality/consistency lever: higher = more detail but more flicker (0.3-0.45 recommended) |
| denoise_max | FLOAT | 0.000–1 | adaptive denoise: when above 'denoise', steady frames keep the low base value while high-motion frames ramp toward this. 0 = off (single global denoise) |
| noise_mode | COMBO | fixed_per_track | fixed_per_track reuses the same seed and noise on every frame of a track — the biggest identity-stability lever |
| detail_mode | COMBO | img2img | inpaint restricts sampling to the face mask in latent space; img2img resamples the whole crop (mask still limits the paste-back) |
| mask_dilation | INT | 80–128 | — |
| feather | INT | 150–128 | — |
| temporal_strength | FLOAT | 1.000–1 | master anti-flicker strength; scales flow blend + color match |
| flow_strength | FLOAT | 0.500–1 | optical-flow-guided temporal blend of detailed crops (pixel space) |
| latent_blend | FLOAT | 0.000–1 | flow-guided temporal blend in LATENT space before decode — smooths in the VAE's semantic space, letting you raise denoise with less flicker. Try 0.3-0.5 with denoise 0.25+; 0 = off |
| flow_bidirectional | BOOLEAN | true | — |
| flow_backend | COMBO | farneback | raft_small/raft_large (torchvision) give much cleaner flow on fast motion at some VRAM/time cost; auto-falls back to farneback on any failure |
| color_match | FLOAT | 0.500–1 | match each detailed crop's color to its source (stops brightness pulsing) |
| chunk_size | INT | 41–64 | crops sampled per batch; lower if you hit OOM |
| detail_every | INT | 11–30 | keyframe mode: sample every Nth frame, flow-propagate the rest (speed on long clips) |
| positiveopt | CONDITIONING | overrides positive_text | |
| negativeopt | CONDITIONING | overrides negative_text | |
| lora_stackopt | LORA_STACK | — | |
| track_promptsopt | STRING | per-track positive prompt overrides, one per line: 'track_id: prompt' (see the debug overlay for IDs) | |
| reference_imageopt | IMAGE | identity anchor: a reference face image the detailed faces are biased toward (init-latent nudge + color anchoring). For strong identity conditioning also patch the MODEL with IPAdapter FaceID upstream — crops are sampled with whatever model you feed in, so it composes | |
| reference_strengthopt | FLOAT | 0.350–1 | how hard to pull toward reference_image (no effect unless it is connected) |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| face_masks | MASK | — |
| debug_overlay | IMAGE | — |
| face_tracks | FACE_TRACKS | — |