Nodes/ComfyUI-MiniMaxH3-Inpaint/MiniMax H3 Inpaint Latent (plate + mask)
ComfyUI Node

MiniMax H3 Inpaint Latent (plate + mask)

MiniMax H3 Only Ever Paints On An Empty Canvas — This Node Hands It Your Footage

By nuvulabs·Created 2 months ago·Updated 2 months ago· 2
MiniMax H3 Inpaint Latent (plate + mask)
  • vae
  • plate
  • mask
  • LATENT
◄width1024►
◄height1024►
◄length124►
◄invert_maskfalse►

MiniMax H3 generates its own audio and landed in ComfyUI on day zero. What that support does not give you is a way to keep footage you already shot. The stock MiniMaxH3ReferenceToVideo node hands the sampler an empty latent - the whole canvas gets generated from your prompt and reference images. So "put this character into this plate, leave the rest of the shot alone" was simply not expressible.

This pack fixes exactly that, and nothing else. Roughly 5KB of Python, one commit, no dependencies - just the plumbing that seeds H3's latent from your plate plus a mask instead of from nothing.

Why bother with a mask in 2026

Masked inpainting is the thing the video stack quietly abandoned. Object removal and region fill are now conditioning modes of the models themselves - LTX-2's trainer folds inpainting and outpainting into one config, Wan does it through VACE, Bernini does it maskless from a sentence. H3 has none of that.

What a mask still buys you is the guarantee: regenerate here, leave the rest alone. An edit model re-emits the whole frame, and every untouched pixel drifts a little. ComfyUI's sampler has supported masked inpainting on nested latents for a while; what was missing was something to build the seeded latent and its mask.

How it works

H3's sampler doesn't take a plain image latent. It takes an audio-video NestedTensor - a video branch [B,24,T,H/16,W/16] and an audio branch - which is why a generic Set Latent Noise Mask doesn't apply.

The node does four things. It resizes your plate to the width/height you set and pads or trims it to length frames, snapping to H3's 17k+5 frame grid (the default 124 is 17×7+5). It VAE-encodes those frames with the H3 video VAE. It builds the audio branch as zeros at H3's 40 latents-per-second and packs both into a NestedTensor. Then it resizes your mask with nearest-exact down to the latent grid, aligns its frame count to the latent's temporal length, and returns {"samples": ..., "noise_mask": ...} - the dict shape the sampler already inpaints with.

Inputs you actually touch

Only three inputs are real decisions; the rest are housekeeping.

  • vae - the H3 video VAE, minimax_h3_video_vae. Not a generic SD VAE.
  • plate - your footage as IMAGE; LoadVideo → GetVideoComponents is the usual source. What's outside the mask comes back unchanged.
  • mask - white regenerates, black keeps the plate. A single mask broadcasts across the whole clip; a mask batch (say from SAM 3's text-prompted SAM3_Detect) gets resampled to the latent's temporal length.

width, height and length must match your MiniMaxH3ReferenceToVideo node, or the conditioning and the latent describe different canvases. invert_mask exists because half the mask nodes in this ecosystem hand you the polarity you didn't expect.

One output: LATENT, into SamplerCustomAdvanced's latent_image. Keep MiniMaxH3ReferenceToVideo for the conditioning (reference image(s) + prompt); the masked region comes back as the reference subject, everything outside it stays your plate.

Installing it

It's in ComfyUI Manager - search ComfyUI-MiniMaxH3-Inpaint, install, restart. By hand:

cd ComfyUI/custom_nodes
git clone https://github.com/nuvulabs/ComfyUI-MiniMaxH3-Inpaint

Then restart ComfyUI. Nothing else to install: pyproject.toml declares an empty dependency list, and the code imports only torch, comfy.model_management, comfy.nested_tensor and comfy.utils. No model downloads either - the H3 weights and VAE stay wherever you already put them.

Where people get burned

The node doesn't appear in the menu at all. The pack is written against ComfyUI's newer backend authoring API (io.ComfyNode, comfy_entrypoint) rather than the old NODE_CLASS_MAPPINGS dict, so it silently won't register on an older build. Update ComfyUI first.

Dimensions drift apart from the conditioning. The plate is scaled to exactly the width/height you type, with cropping disabled - a 16:9 plate pushed into 1024×1024 comes out squeezed, and the mask is resized to the same latent grid, so the mismatch compounds instead of erroring. Match your plate's aspect and use the same numbers on both nodes.

Plate shorter than length. The node repeats the last frame to fill the gap, so a 3-second clip at length 124 gives you a held freeze, not a loop.

Masks that move fast. A multi-frame mask is thinned down to the latent's temporal length by picking evenly spaced frames, so one that flickers gets smeared.

Audio. The audio branch is seeded as zeros, same as the stock empty-canvas node, so this isn't audio inpainting.

The elephant. H3 is a 33B model with roughly 42.5GB of reported weights and no published consumer-VRAM floor; the launch-window "it runs on a 3060" chatter was speculation before the weights existed. And in the US, EU, UK or South Korea the MiniMax H3 Community License doesn't grant you the right to run the local weights at all.

Where it shines: LoadVideo → SAM3_Detect (text prompt in, mask batch out) → this node → SamplerCustomAdvanced, and you have an automatic character swap that regenerates the masked region and hands back the rest of the shot untouched.

Categorymodel/latent/minimax

Inputs (7)

NameTypeDefaultDescription
vaeVAEH3 video VAE (minimax_h3_video_vae)
plateIMAGEPlate/background video frames
maskMASKWhite = region to regenerate/insert; black = keep the plate
widthINT102432–8192—
heightINT102432–8192—
lengthINT1245–3600Frame count @24fps, snapped to the 17k+5 grid
invert_maskBOOLEANfalseInvert the mask if your white/black regions are swapped

Outputs (1)

NameTypeDescription
LATENTLATENT—