H3 Motion Context Upload Latent (Distributed)
How a Worker's Latent Gets Back to Master Without a Shared Drive
- latent
- LATENT
Here's the hard truth about multi-GPU ComfyUI that nobody wants in a launch post: you can't split one generation across two cards. The community has been saying this for years - two GPUs give you more total VRAM, or two separate generations, not one faster image. H3 makes it worse, because chain generation is inherently sequential: clip N needs clip N−1's latent before it can start. There's nothing to split.
The honest use for the second card is candidate racing: render three or four takes of the next clip at once with different seeds, watch them, keep the best. ComfyUI-Distributed already does that. What it doesn't do is get each worker's latent back to Master. That's this node.
How it works
You put it right where H3 Motion Context's own Save Latent node used to go:
Sampler → H3 Motion Context Upload Latent → VAE Decode → … → Distributed Collector
It has one job with two halves. The visible half: latent in, latent out, unchanged, so your decode path is untouched. The invisible half: it also hands a copy of that latent to Master.
On Master, "hand it over" means writing a safetensors payload into ComfyUI/temp/h3_distributed_latents/, keyed by job id and Master's own slot. On a worker, it means an HTTP POST to /h3_distributed/latent on Master's ComfyUI server, keyed by job id and that worker's position in the panel's worker list. Never a shared numbered slot two machines could race to write - which is why you can't silently continue from the wrong take.
The pass-through isn't cosmetic: Distributed only ships a worker the nodes reachable from its Collector, so a dead-end output would be pruned and never run there. Passing the latent through keeps the node on the shipped chain, with zero changes to upstream H3 nodes.
The inputs that matter
latent is the only required input - wire it from your sampler, exactly where the old Save Latent went.
keep_tail_frames (optional, 0–100000, default 56) is the one you might actually touch, and it's smarter than it looks. H3 Motion Context only ever reads the end of context_latent: at most 56 frames of video plus audio_context_length frames of audio. So the node keeps only that tail. Only the copy sent to Master is trimmed - the latent you pass through to VAE Decode is the full clip.
The cut is aligned to H3's latent grid in 15-step units, so any context window shorter than the kept tail slices to the same values it would from the full latent. In practice a 15-second 2 MP clip's embedded latent drops from 84 MB to 13 MB, and the saved video from ~129 MB to ~35 MB - the difference between fitting under ComfyUI's default 100 MB upload cap and Cloudflare tunnel limits, and not.
One output: LATENT. Straight into VAE Decode.
Installing it
Both upstream projects first - ComfyUI-Distributed and ComfyUI-H3-Motion-Context - working on Master and every worker, before this touches anything.
cd ComfyUI/custom_nodes
git clone https://github.com/ymiyad/ComfyUI-H3MC-Distributed.git
Restart ComfyUI everywhere - this goes on every machine, since whichever one draws a candidate has to run the node. Manager finds it by pack title too. No config file; the node reads its context from the running prompt. Dependency: av.
Where it goes wrong
"could not reach Master at http://…". A worker couldn't POST. The error prints the exact URL it tried. Usual suspects: wrong master_url in the Distributed panel, a firewall, or Master's ComfyUI bound to localhost only.
Uploads fail on long clips. Setting keep_tail_frames = 0 restores the pre-trim behavior: the full base64 payload, around 112 MB for a 15-second clip. That sails past request size limits and the POST dies. Leave it at 56 unless you have a reason.
H3 warns about pinned audio. If the next clip uses an audio_context_length above keep_tail_frames, only the audio that survived the trim is pinned. Raise keep_tail_frames to match. The default 56 covers H3's largest context_length.
No candidate appeared on Master. Confirm the node sits between sampler and decode on the branch that reaches Distributed Collector, and that Parallel Generation is on - a single-prompt run produces no other participants. If you just updated ComfyUI-Distributed, start here: this add-on leans on a couple of its internal, undocumented shapes, and the author flags that as the first thing to re-check.
One trust caveat. The upload endpoint is unauthenticated - anything that can reach Master's port can POST a latent. Same trust model as Distributed's own traffic: keep it on your network, don't port-forward it.
Last thing: those temp latents are pruned after six hours, so an overnight stall fails at the save step with "no latent stored". Once the latent is embedded in a video, the video is the record.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | — | |
| keep_tail_framesopt | INT | 560–100000 | Frames at the end of the clip to keep for continuing the chain (video and audio). H3 Motion Context only reads the tail: 56 covers its largest context_length. Raise it if the next clip uses an audio_context_length above this. 0 keeps the whole latent (old behavior, much larger files). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LATENT | LATENT | — |