APNext H3 Mouth Guard (protect lips in refine)
Keep the lip-sync when you re-render the face
- latent
- masks
- latent
- mask_preview
- report
APNext H3 Mouth Guard solves the specific, maddening problem you hit the moment you try to refine an H3 face: the refine pass fixes the likeness and, in the same breath, erases the lip-sync. H3 renders native synced audio - the character is actually talking. Re-render v2v toward a reference face and the sampler re-denoises the mouth, and your carefully generated dialogue comes back as a fish flapping its lips. This node makes the sampler leave the mouth alone.
It's the second half of the pack's mouth-guarded face-refine pass. First H3RefineEncode turns your upscaled pass-1 clip into a clean AV latent (video + the pass-1 soundtrack). Then this node takes that latent plus a per-frame lips mask, reduces the mask down onto the H3 latent grid, and installs it as the latent's noise mask: 0 means preserve, 1 means generate. The sampler keeps the mouth - and, with protect_audio on (the default), the audio stream - from the input latent at every step while everything else gets re-rendered toward your reference images.
The inputs that matter
latent- the clean encoded pass-1 content fromH3RefineEncode. This matters more than it looks: sample it withRandomNoise+BasicScheduleratdenoise < 1, and never run a pre-noised /DisableNoiseflow. The sampler restores protected regions from the input latent, so a pre-noised input preserves noise, not lips.masks- per-frame lips mask batch, white = lips, one per pixel frame. Core ComfyUI'sDetect Face Landmarks (MediaPipe)→Draw Face Mask (MediaPipe)withregions=custom, lips only, is exactly what this is made for. Any resolution; it gets reduced onto the latent grid.grow_pixels- default 8. Dilates the lips mask first as a safety margin around the mouth. Leave it unless the boundary is clipping.protect_audio- default on. Locks the pass-1 soundtrack so lip-sync timing survives. Off regenerates the audio fully.
The advanced toggles (grow_cells, feather_cells, existing_mask) tune the edge - feather_cells softens the protection boundary, and existing_mask: merge (default) combines with a noise mask already on the latent so protected stays protected.
Outputs: the latent with its noise mask installed, a mask_preview showing the protected region per frame (worth a look the first time), and a report string with the geometry and whether the H3 mask engine is active.
Installing it
Part of the comfyui_dagthomas pack - Manager, search "comfyui_dagthomas", or:
cd ComfyUI/custom_nodes
git clone https://github.com/dagthomas/comfyui_dagthomas
cd comfyui_dagthomas
pip install -r requirements.txt
One one-time extra: the MediaPipe face landmarker, mediapipe_face_fp32.safetensors, has to be in models/detection/ (the pack's example workflow has the exact URL). And for the clean version of this trick, install ComfyUI-H3-Motion-Context-MultiRef too - see below.
Quality, and the gotcha people hit
The guard works with any model via ComfyUI's sampler-level inpaint blending. But when ComfyUI-H3-Motion-Context-MultiRef is installed, it also enables that pack's H3 mask engine - protected tokens run at the model's clean-conditioning timestep, the same mechanism H3 keyframes use, and the boundary comes out dramatically cleaner. Without it the mouth is still preserved, just seamier. The console will tell you which path you're on.
Second gotcha: mask granularity is roughly 32 source pixels per latent cell, so refine at 2x upscale (the workflow default) or a small face gives the mouth too few latent cells to protect. And check the report - if your mask frame count doesn't match the latent's frame count, indices get clamped and the guard is less effective than you think.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | H3 AV latent whose samples already contain the CLEAN encoded pass-1 content (from APNext H3 Refine Encode). Sample it with RandomNoise + BasicScheduler denoise < 1 - never a pre-noised / DisableNoise flow. | |
| masks | MASK | Per-frame lips mask batch (white = lips), one mask per pixel frame - e.g. Detect Face Landmarks + Draw Face Mask (MediaPipe) with regions=custom, lips only. Any resolution; it is reduced onto the latent grid. | |
| grow_pixels | INT | 80–256 | Dilate the lips mask this many source pixels first (safety margin around the mouth). |
| protect_audio | BOOLEAN | true | Lock the input latent's audio stream so the pass-1 soundtrack (and its lip-sync timing) survives the refine. Off = the audio is fully regenerated. |
| grow_cellsopt | INT | 10–8 | Extra dilation in latent cells (one cell covers 16 source pixels; the model reads 2x2 cells per token). |
| feather_cellsopt | FLOAT | 0.50–4 | Gaussian feather (in latent cells) on the protection edge. Fractional mask values run at intermediate strength when the H3 mask engine (ComfyUI-H3-Motion-Context-MultiRef) is installed. |
| existing_maskopt | COMBO | merge | merge = combine with a noise mask already on the latent (protected stays protected). |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |
| mask_preview | MASK | — |
| report | STRING | — |