Nodes/ComfyUI_zura_nodes/Zura · Prepare H3 Control Pixels
ComfyUI Node

Zura · Prepare H3 Control Pixels

Getting your source, depth and mask onto H3's grid

By ZURAVFX·Created about a month ago·Updated a day ago· 2
Zura · Prepare H3 Control Pixels
  • source_video
  • control_video
  • mask
  • target_latent
  • model_patch
  • fitted_control
  • masked_source
  • visibility

Every H3 control pass starts here, and almost every H3 control error traces back to skipping something this node does. It's the unglamorous step: it takes your source video, your depth guide and your edit mask, and forces all three onto the exact grid the target latent implies - same raster, same frame count, same multiples of 32 that H3's patch structure needs.

What it's really solving

H3's video VAE compresses 16× spatially and its DiT works in patches on top of that, so a latent carrying H × W spatial dims corresponds to H×16 × W×16 pixels, and both need to be even at the latent level. Get that wrong and you're not "slightly off" - you get a hard error at sampling time, or control that's subtly misaligned frame by frame.

So this node works backwards from the target latent. It reads target_latent to derive the native pixel raster and the frame count, then:

  • resizes the depth guide to that raster and frame count (bilinear, center crop/pad),
  • resizes a down-mixed version of the source the same way,
  • thresholds your mask at 0.5 and inverts it - the output visibility is 1 where the model may repaint and 0 where the source is frozen,
  • multiplies the fitted source by that visibility.

Hence masked_source: the performer's region knocked out to black, everything else intact.

The bit nobody expects

If the loaded H3 control patch uses inpaint_post_norm, the node adds the ImageNet mean back into the masked region after zeroing it. That's the native H3 inpaint preprocessing convention, and skipping it is why hand-rolled H3 control sometimes produces a grey halo around the subject - the VAE was trained to see normalised pixels there, not zeros. You don't have to think about it, but it's the reason this node reads model_patch at all.

Two more things worth knowing: the tail is padded by repeating the last frame, and the visibility channel uses max-pooling-style fitting rather than smooth interpolation, so a partially-covered pixel counts as visible. Conservative on both counts - you get real data rather than invented motion, and the model gets slightly more room to edit than you drew.

Inputs and outputs

  • source_video - IMAGE, the original frames.
  • control_video - IMAGE, the depth guide (or whatever structure you're conditioning on).
  • mask - MASK, [H, W] or [frames, H, W], values in 0–1. This is the region you want changed.
  • target_latent - LATENT, the H3 target the whole thing is measured against.
  • model_patch - MODEL_PATCH, the loaded H3 fun-control patch. Needed because of that normalisation branch.
  • fitted_control - IMAGE, into a stock VAEEncode.
  • masked_source - IMAGE, into a second stock VAEEncode.
  • visibility - MASK, into Zura · Pack H3 Control Latents.

Install

cd ComfyUI/custom_nodes && git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
pip install -r ComfyUI_zura_nodes/requirements.txt

Restart. Weights are the usual H3 set plus the fun-control union patch in models/model_patches/. Nothing this node does requires a GPU - it's resize and threshold arithmetic - but the downloads around it are measured in gigabytes, and they're under H3's territory-restricted community licence.

Common issues

"Prepared H3 raster exceeds the dimension limit." The target latent implies something bigger than 4096 on a side. You're controlling at a resolution H3 wasn't asked to render; drop the target resolution.

"Edit mask must lie in [0, 1]." A mask that came out of a matte or an upscaler in 0–255 land. Normalise it, or check the upstream node's output range.

"Use the actual loaded native H3 ControlNet MODEL_PATCH." Same trap as the apply node: model_patch wants the loaded patch, not a patched MODEL. The node introspects the patch to check it really is the H3 control patch with injection layers, so a wrong object type fails loudly.

Mask and video out of step. If your mask has a different frame count than the source, the fit silently repeats the last frame of whichever is shorter. Check the counts before you blame the sampler - a mask that's one frame short produces a single wrong frame at the end, which is easy to miss in a preview and obvious in a render.

Everything is black. You probably want the inverse of what you drew. mask is the area to change; visibility is derived from it. If your mask covers the performer and you expected the background to stay, that's correct behaviour - just confirm which one you meant before the sampler tells you the hard way.

CategoryZura/Artist Studio/Data

Inputs (5)

NameTypeDefaultDescription
source_videoIMAGE—
control_videoIMAGE—
maskMASK—
target_latentLATENT—
model_patchMODEL_PATCH—

Outputs (3)

NameTypeDescription
fitted_controlIMAGE—
masked_sourceIMAGE—
visibilityMASK—