--- license: mit tags: - comfyui - comfyui-nodes - custom-nodes - video - video-generation - text-to-video - image-to-video - minimax-h3 --- # Hand Tie Clips **Multi-hop MiniMax H3 video from one node.** Write a shot plan, drop in your reference stills, queue. The pack's whole job is the *join* — the place where one generation ends and the next begins. ![Six consecutive frames across a join](https://media.githubusercontent.com/media/dntpi/ComfyUI-Hand-Tie-Clips/main/docs/img/seam-frames.png) *Six consecutive frames spanning the join between hop 1 and hop 2. One of these is the last frame the first generation produced and the next is the first frame of a second, separate generation. The pack exists so that you cannot tell which.* Each hop is native **MiniMax H3 Reference-to-Video**. Hops after the first are guided by the previous hop's sampler AV latent through [ComfyUI-H3-Motion-Context](https://github.com/NikoDemon80/ComfyUI-H3-Motion-Context) when it is installed; stock `MiniMaxH3AddGuide` is the fallback, and the console says which one each hop took. --- ## What it gives you | | | |---|---| | **One node, five tabs** | SCRIPT, REFS, MEDIA, WRITE and SWAP on the node body. No `Load Image` chains, no JSON in a textarea. | | **A shot plan** | One card per hop — the shot count *is* the hop count. Beat, duration, seed, and five directive axes compiled to vetted prose. | | **A reference register** | Stable `@tags` for your stills, grouped per subject, each with a retention rule and its own pixel budget. Pulling one out of the middle no longer renumbers ``. | | **WRITE** | A plan writer on the node. Point it at any OpenAI-compatible server, describe the scene in a sentence, and it fills the script *and* the reference rows — reading the pictures you already dropped in. | | **SWAP** | One-hop identity swap from a reference clip. Four named modes, so what *stays* with the clip is stated rather than left to omission. | | **A lossless hop cache** | Re-roll shot 5 of 8 and only 5–8 re-render. Resume after a crash. Hold about one hop in RAM instead of the whole film. | | **A lip-sync lock** | `master_audio_file` — one continuous take every hop locks to, delivered as a passthrough. | | **Instruments** | Dry run, contact sheet, seam report and a live preview panel, so you can find the hop that broke without scrubbing the file. | --- ## Install From your `ComfyUI/custom_nodes/` folder: ``` git clone https://github.com/dntpi/ComfyUI-Hand-Tie-Clips.git ``` Or from the HuggingFace mirror, which is the same repository: ``` git clone https://huggingface.co/sandpies/ComfyUI-Hand-Tie-Clips ``` Cloning puts the folder at the right depth, which is the mistake the zip route invites — one level too deep (`custom_nodes/ComfyUI-Hand-Tie-Clips/ComfyUI-Hand-Tie-Clips/`) and the pack simply will not appear. Updating later is `git pull` and a restart. **No dependencies to install.** Everything it imports — `torch`, `numpy`, `PIL`, `av`, `aiohttp` — already ships with ComfyUI, which is why `dependencies` in `pyproject.toml` is empty. Then **restart ComfyUI** (Python changed) and **hard-refresh the browser** (Ctrl+Shift+R). The editor is served from `/extensions/ComfyUI-Hand-Tie-Clips/`, and a stale cache is the single most common reason the node mounts with no UI on a fresh install. It is installed correctly when all three are true: - the startup log carries a line beginning `[HandTieClips]` - the browser console says `[HandTieClips] editor ui v2.0.0 loaded` - node search shows a **Hand Tie Clips** category with five nodes, each once Workflows saved before the 2026-08-29 rename keep loading — the old ids are registered as deprecated aliases. Nothing needs migrating. ## Needs - ComfyUI new enough to include **Add Guide for MiniMax H3** (`MiniMaxH3AddGuide`) - A **ref2va** (or hybrid ref2va) checkpoint — fl2va has no reference rows - Video VAE + audio VAE + MiniMax text encoder - **Recommended:** [ComfyUI-H3-Motion-Context](https://github.com/NikoDemon80/ComfyUI-H3-Motion-Context) for the latent join. Without it the node falls back to `MiniMaxH3AddGuide`, which still works and still chains — it is a different join, so a seam you are comparing against someone else's render may not be the same code path. The two shipped workflows wire the **turbo stack** this node is actually run with, because an example without it is not the graph anyone uses: ``` UNETLoader -> LoRA Loader Stack -> H3 AdaLN LoRA Fix -> MiniMax H3 Low VRAM -> H3 SLA Attention -> Model Preview Override -> Hand Tie Clips ``` | pack | nodes | |---|---| | [ComfyUI-PlagueKind-Nodes](https://github.com/PlagueKind/ComfyUI-PlagueKind-Nodes) | LoRA Loader Stack, H3 AdaLN LoRA Fix, H3 SLA Attention | | [ComfyUI-KJNodes](https://github.com/kijai/ComfyUI-KJNodes) | MiniMax H3 Low VRAM Attention (experimental), Model Preview Override | **CLIP reaches the node from the LoRA loader, not from the encoder** — that is what makes the text half of every LoRA land. Do not rewire it back. None of it is required by the node itself. Missing a pack, its nodes load as red boxes: delete them, wire the loader straight into `model` and the encoder into `clip`, and raise `steps` from 7 to around 20. Both shipped workflows are saved pointing at the exact quantised files they were rendered with. These are **not** the only builds that work — any ref2va or hybrid ref2va set will do. Repoint the loaders at what you have; a loader showing an empty or red filename is naming a file you do not have, not a broken workflow.
The exact files the examples name | loader | file | |---|---| | `UNETLoader` | `minimax_h3_hybrid_fl2va_ref2va_b30-49-int8.safetensors` | | `CLIPLoader` | `qwen3vl_32b_minimax_h3_int8_convrot.safetensors` | | `VAELoader` (video) | `minimax_h3_video_vae_int8_convrot.safetensors` | | `VAELoader` (audio) | `minimax_h3_audio_vae_fp32.safetensors` | | LoRA Loader Stack | `minimax_h3_ref2v_turbo_4step_v0.1_comfyui_bf16.safetensors` | | `tiny_vae` | `taeh3.safetensors` (or `none`) |
--- ## Start here 1. Load **`workflows/HandTieClips_Starter.json`** — two hops, no references, runs as soon as the loaders point at your files. It carries a **READ ME** board on its own canvas, to the left of the loaders: the laws, the directive table, the reference rules and the failure table, where you need them rather than in another file. 2. Point the loaders at your ref2va DiT, encoder, and both VAEs. 3. In **REFS**, add a row per identity still and **drop a picture onto its thumbnail**. Give it a `@tag`; group photos of the same person under one subject number. Files land in `ComfyUI/input/h3_refs` — there is nothing to wire. 4. In **SCRIPT**, write one card per hop. Shot 1 is the whole opening; every later card is **only the new beat**. 5. Optional: a first frame, up to three reference clips, up to three voices, a music bed, a master audio take — all in **MEDIA**. 6. Queue. Wire **`info`** to a Preview Text node to read the fully assembled prompt for every hop. `workflows/HandTieClips_Showcase.json` is the six-hop version and needs three pictures of your own. Both carry the turbo stack above. ![The editor's tabs, with SCRIPT open on a ten-shot plan](https://media.githubusercontent.com/media/dntpi/ComfyUI-Hand-Tie-Clips/main/docs/img/editor-tabs.png) *Each tab carries its count, and RUN stays pinned below them. SCRIPT is one card per hop: the beat, its duration, and its own join, camera, framing, pace and tail, with `lock` and the range button on the card header.* Under the script sits **RUN**, collapsed, holding everything that is not per-shot: output size and length, sampling, the join and pin controls (`pin_mech`, `last_frame_guide`, tone), and the hop cache. Its title line summarises the run — `1344x768 · 10s ×3 · 14 steps res_multistep · cache off` — so you can read the setup without opening it. Two script modes: **Simple** (one prompt box and a hop count, the way it always worked) and **Shots** (one card per hop with directives; switching from Simple carries your prompt over and splits it on `---`). Only one is on screen at a time, so no text box is ever quietly doing nothing. Keep later-hop beats on **what happens next**. Do not re-describe the face; the photos, the register and the pin already carry it. --- ## The one law Sampling runs at **cfg 1.0 with no negative branch**, so the prompt is purely additive: **anything you name is added, and nothing can be removed by mentioning it.** Almost every rule in this pack is a consequence, and each of the rules below was confirmed against renders rather than reasoned from code. **Never name the thing you want to end.** `The cook stops talking` keeps her talking. Write the state you want as a pose plus a sound: > The cook leans back against the counter with her lips closed, and lets her > eyes move slowly across the room. The kitchen is quiet apart from the hum of > the refrigerator. H3 generates audio for the whole hop no matter what, so silence has to be written as a sound. Written as an absence, you get speech. The ban is on the idea, not on a word list: *fades, subsides, dies down* all name an ending as surely as *stops* does. **Point the action line at the reference you want used.** Describing a reference in the register is not enough — the beat is what drives the frame. Write the tag into the action: `The cook stands at the counter in @kitchen, looks up…`. Phrase a place as depicted, not as a container to be placed inside, or you can get a literal composite of the photograph. **Put a state change at the end of the previous shot.** Every hop after the first opens by holding the frames it was handed, and the audio pin carries the previous hop's tail across the join. If shot 2 ends mid-sentence, no wording in shot 3 will make it start quiet — arrive there before the previous shot ends. **Pick ambience that is narrowband and specific.** "faint street noise through the window" is broadband and renders as a five-second hiss; "the low hum of the refrigerator" does not. **Set `tail` on your last shot.** Left on `ongoing`, the model is told action is still underway at the final frame and will invent something to satisfy it — a stray gesture, or a stray line of dialogue in the closing second. > The full authoring guide is **[PROMPTING.md](PROMPTING.md)**. Its rules come > from what this model actually does, not from taste. `prompt_pack/` is the > same writer as a copy-paste prompt, for when you would rather work in a chat > window. --- ## Let a model write the plan Open **WRITE**, point it at any OpenAI-compatible server, say what you want in one sentence, and press **Write plan**. It fills the SCRIPT cards and the REFERENCES rows together — and the pictures already on those rows go with the request, so the model describes what it is actually looking at rather than guessing from a filename. ![The WRITE bar](https://media.githubusercontent.com/media/dntpi/ComfyUI-Hand-Tie-Clips/main/docs/img/write-panel.png) - **Context 32768.** The system prompt alone is ~6,000 tokens, the reply another 1,000–2,000, and every reference picture costs ~260 on top. - **Reasoning off.** Thinking tokens come out of the same budget; a reply that stops before the JSON closes is the tell. - **Temperature 0.3.** Higher and the JSON grows trailing commas and smart quotes. Server settings are saved on this machine only — a shared `.json` never points at your server. **Treat what comes back as a strong draft, not a finished plan.** It gets the structure right — hop count, join types, which reference rides which hop, a sound bed on every quiet hop — which is the part that is tedious by hand. Read two things every time: each reference's `desc`, which can be confidently wrong about its own photograph, and the spoken words in every beat. The node lints both and prints what it finds before you render. --- ## SWAP A **one-hop identity swap** from a reference clip: the clip supplies the motion and the scene, a still from your rail supplies the person. Trim the clip in MEDIA, pick an identity, press **Write**, then **Accept** — which writes exactly one shot and the clip's description, and never touches `ref_plan`. Your register is not rewritten. Four modes, because at cfg 1.0 **a mode that merely omits the swap line does not keep the clip's person** — the identity still is in front of the encoder either way and governs the subject anyway. Each mode states positively what stays: | mode | what the photograph contributes | |---|---| | `replace_person` | Face, build, hairstyle **and** wardrobe. | | `head_swap` | Face, hair and skin tone. The body stays with the clip: build, posture, hands and every garment. | | `face_only` | Facial features only. Hair, ears, expression, build and clothes stay with the clip. | | `keep_person` | Nobody is swapped; the clip is a scene and motion plate. The identity picker greys out. | Alongside them: **background** (from the clip / from a picture `@tag` / free) and an optional **wardrobe plate** — a `@tag` whose garment is *worn*, draping on the body in frame and creasing where it bends, not pasted. **Two things to get right, both of which cost renders to find out:** - **Do not run MEDIA's describe on the clip before a swap.** That caption reaches the encoder as what `