Nodes/ComfyUI-BFSNodes/BFS H3 Duet (pinned panel, all in one)
ComfyUI Node

BFS H3 Duet (pinned panel, all in one)

Swap the actor in a clip without training a LoRA

By alisson-anjos·Created 7 months ago·Updated about 10 hours ago· 129
BFS H3 Duet (pinned panel, all in one)
  • model
  • clip
  • vae
  • audio_vae
  • panel
  • guide
  • ref_images
  • ref_videos
  • ref_video_audios
  • ref_audios
  • setting
  • vlm
  • images
  • audio
  • canvas
  • layout_text
  • latent
  • prompt
  • side_by_side
◄taskcharacter swap►
◄instruction►
◄prompt►
◄width448►
◄height800►
◄length0►
◄positionleft►
◄size1.00►
◄fitcontain►
◄gap0►
◄panel_noise0.00►
◄holdall frames►
◄rope_modecanvas►
◄rope_gap0►
◄ref_image_sizematch►
◄steps20►
◄sampler_nameeuler►
◄schedulerbeta►
◄seed42►
◄decode_canvasfalse►
◄guide_frame_idx0►
◄source_bluroff►
◄source_blur_segmenterSAM 3.1►
◄source_blur_effectblur►
◄panel_staticoff►
◄vlm_max_tokens1024►

The idea in one line

You have a clip of someone doing something. You want a different person doing exactly that thing - same gestures, same cuts, same camera, no reshoot. The normal answer is a body-swap LoRA trained on your model. This node is the answer for people who don't have one: it pins the source clip beside the video as a panel, tells MiniMax H3 to generate the new performance in sync with it, and cuts the panel off before you ever see it. No training, no LoRA, no dataset.

That's the trick. H3 treats the pinned strip like its own conditioning rows - clean, at the condition timestep - so it copies the motion frame for frame instead of being loosely "guided" by it.

Where this sits in the H3 world

MiniMax H3 is the 33B omni-modal video model that opened its weights in August 2026 with native stereo audio, and it quickly became the reference open model for video editing. Two things matter here: it has real reference conditioning built into ComfyUI's native H3 nodes, and it's big. Note the pack author's own credit - the latent-pin duet idea came from TSC, who pinned the source with a noise mask first; the BFS contribution is the shifted RoPE layout, dynamic references and this all-in-one node.

How it works, mechanically

Inside, BFS H3 Duet is a whole workflow: it calls H3's Reference to Video to build positive conditioning and the AV latent, optionally adds an aligned guide, pins the panel strip into the latent with a noise mask, samples with Guider_Basic (which means CFG 1 - there is no negative prompt here, and no CFG knob), crops the strip out of the latent, then decodes picture and audio. So it both builds conditioning and runs the sampler. Two layout modes: canvas makes panel and video share one wide RoPE grid (TSC's approach), and shifted keeps the video on exactly the RoPE positions it would have had with no panel at all, putting the panel past its edge - that's the layout the author says is new. If you'd rather use your own sampler, use BFS H3 Duet Conditioning instead; same panel, stops before sampling.

The inputs you'll actually touch

  • model, clip, vae - the H3 model, the H3 Qwen3-VL text encoder and the video VAE. clip is where the panel content gets seen; the panel itself never goes to the text encoder.
  • task + instruction - with an empty prompt box, these write a draft prompt; instruction is "what changes, in a few seen words". Be honest about the drafts: the node can't see your clip, so they're generic, and the ones that worked best in testing were written for the clip.
  • prompt - your own REF2VA prompt and always the winner over task. The panel has no tag: it is not <Picture n> and not <Video n>, so name it by position ("the LEFT half is the kept footage") or write {layout} and let the node insert that sentence for the current side and size. And don't describe the panel's person, clothes or room even to contrast it - what you leave undescribed is what gets copied.
  • width / height / length - output size and frames at 24 fps, snapped to 17k+5. length of 0 means "as long as the panel clip or guide".
  • position / size - which side the strip goes on, and how big it is relative to the video (1.0 = two equal halves).
  • panel_noise - 0 pins the panel exactly, which is what a character swap or a lighting change wants. Push to 0.1–0.2 when you want room to invent (style transfers, turning someone into a creature).
  • rope_mode / rope_gap - leave canvas unless you're experimenting with shifted, in which case rope_gap is a trap: keep it small (0–2 at low resolution), because a gap close to the video's own width makes the model draw its own split screen.
  • Optional panel - the clip or still you're pinning. Leave it empty and this is just a plain guided H3 render. guide is the aligned latent guide the body-swap LoRAs want. ref_images / ref_videos / ref_audios grow as you connect them, like the native Reference to Video node.

Outputs: images (the generated video, panel already cropped), audio, canvas (the full split screen - worth a look, it's how you check the two halves are actually in sync), layout_text (the {layout} sentence), latent, and prompt (what was actually sent, draft included).

Install

Search ComfyUI-BFSNodes in ComfyUI Manager, or:

cd ComfyUI/custom_nodes
git clone https://github.com/alisson-anjos/ComfyUI-BFSNodes
cd ComfyUI-BFSNodes && pip install -r requirements.txt

Restart ComfyUI. The pack is large - one clone gets you the whole BFS toolbox (LTXV Edit Anything, shot loop, LoRA surgery), and requirements.txt pulls insightface==0.7.3, onnxruntime, scenedetect, librosa and opencv-python along with it. That insightface pin is the one most likely to fight something else in your environment.

Where people get burned

If the node doesn't appear in the Add Node menu but the Side Panel nodes do, your ComfyUI build is too old - BFS H3 Duet is registered through the newer comfy_api.latest interface and silently skips registration when that import fails. You also need a build with native H3 support; BFS H3 Side Panel throws an explicit "expects a MiniMax H3 AV latent" error (and one about sizes needing to be multiples of 32) rather than doing something surprising, which is the good kind of failure.

And the boundary is worth stating plainly: this copies motion and timing, it is not an identity LoRA. The author's own tests found the pinned panel pulls the background toward the reference's backdrop when used with a guide, so pick one mechanism until a LoRA is trained for both.

CategoryBFS/MiniMax H3

Inputs (38)

NameTypeDefaultDescription
modelMODEL—
clipCLIP—
vaeVAE—
taskCOMBOcharacter swapWrites a DRAFT prompt when the prompt box is empty: character swap (the person from the pictures), style, setting, appearance or lighting / weather (the instruction says what). The draft cannot see the video, so it is generic: a prompt written for the clip (see the example in the prompt tooltip, and the 'prompt' output for the draft) gives much better results. A text in the prompt box always wins.
instructionSTRINGWhat changes, in a few seen words: 'a 1990s anime cel style', 'a beach at sunset', 'an elderly woman with grey hair', 'night lit by pink and blue neon', or the new person's look.
promptSTRINGYour own REF2VA prompt (overrides the task). <Picture n> / <Video n> / <Audio n> are the references in order. How to write a duet prompt (TSC, newer strategy): H3 has no idea what is pinned and what is noised, it reads one description of one video. Describe the finished split screen as plain footage: never write 'kept footage', 'kept exactly', 'generated', 'preserved', 'in sync with', 'seamlessly'. The pinned clip gets no tag and no retention line: name its region by where it is plus what is seen there, in one clause ('the top quarter shows a man dancing in a small bedroom'; {panel_region} / {video_region} give the region names for the current layout, {layout} the whole split-screen sentence). Write the other region as its own scene in a real place with concrete details. Spell out the motion beat by beat, with times for the big moments (cuts, strikes, jumps, a line), on the video's clock. The soundscape is the sounds of the new scene. Cuts: '[Shot 2] At MM:SS.mmm, both halves cut together to ...'. Example (character swap, source clip pinned on the left half, one face picture and one full-body picture): subject_definitions: <Subject 1> is a young woman whose appearance comes from <Picture 1> and <Picture 2>: fair skin, a narrow oval face, grey-green eyes, light brown hair in a low ponytail, wearing a white long-sleeved top under a navy denim apron. summary: [reference generation] The target video is a split screen: the left half shows a man talking to a phone camera in a bedroom, and the right half shows <Subject 1> doing the same movements at the same moments, in a sunny kitchen with white tiles. retention_analysis: <Subject 1> (appears in [Shot 1], [Shot 2]): fully_preserved - her face, ponytail, white top and navy apron are retained. detailed_description: The target video is in a realistic style, as handheld vertical smartphone footage under soft daylight. A split screen divided by a thin straight vertical line: the left half shows a man talking to a phone camera in a bedroom; the right half shows <Subject 1> performing the same movements at the same moments, in a sunny kitchen with white tiles and a window behind her. [Shot 1] A medium close-up at chest height, the phone steady at eye level. <Subject 1>, her face from <Picture 1> with grey-green eyes and soft pink lips, talks to the camera with small nods. At 00:01.800 she laughs and tilts her head back. [Shot 2] At 00:03.708, both halves cut together to a closer framing. <Subject 1>, her face from <Picture 1>, the white sleeves and apron straps visible, raises one open hand beside her mouth and smiles. overall_soundscape: Her voice speaking to the camera, a kettle starting to hiss in the kitchen at 00:02.500. non_diegetic_music: None.
widthINT44832–4096Generated video width (the output).
heightINT80032–4096—
lengthINT00–3600Frames at 24 fps, snapped to 17k+5. 0 = the guide's or panel clip's length.
positionCOMBOleft4 options: top, left, right, bottom
sizeFLOAT1.000.1–1.5Panel size against the video (1.0 = two equal halves).
fitCOMBOcontaincontain keeps the whole clip (smaller, with grey around) so nothing is cropped; cover fills the panel and crops; stretch distorts.
gapINT00–8Grey separator in 32 px patches (pixels, held).
panel_noiseFLOAT0.000–10 copies the panel exactly (swaps, light). 0.1-0.2 gives room for big changes (style, creatures).
holdCOMBOall frames2 options: all frames, first latent frame
rope_modeCOMBOcanvascanvas: panel and video share one wide grid (TSC). shifted (BFS): the video keeps the RoPE positions of a render without the panel and the panel sits past its edge.
rope_gapFLOAT00–256shifted only: empty RoPE steps (2x2 patches) between video and panel. Keep it small against the video width (0-2 at low resolution): a large gap makes the model draw its own split screen.
ref_image_sizeCOMBOmatch2 options: match, max
stepsINT201–200—
sampler_nameCOMBOeuler44 options: euler, euler_cfg_pp, euler_ancestral, euler_ancestral_cfg_pp, heun, heunpp2, +38
schedulerCOMBObeta9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3
seedINT420–18446744073709550000—
decode_canvasBOOLEANfalseAlso decode the whole canvas, to check the sync.
audio_vaeoptVAE—
paneloptIMAGEPinned beside the video: the source clip (copied in sync) or a picture.
guideoptIMAGEOptional aligned latent guide in the video area (for body-swap LoRAs).
guide_frame_idxoptINT0-9999–9999—
ref_imagesoptCOMFY_AUTOGROW_V3—
ref_videosoptCOMFY_AUTOGROW_V3—
ref_video_audiosoptCOMFY_AUTOGROW_V3—
ref_audiosoptCOMFY_AUTOGROW_V3—
source_bluroptCOMBOoffBlur the source person in what the model sees (the panel / the guide): '<region> (<what stays sharp>)'. keep mouth: lips, teeth and tongue stay sharp; keep lips + tongue: the teeth are blurred too; mouth too: nothing stays sharp. masked area: the shot's own mask (elsewhere: the whole person). It reads the pose and motion but has no face to copy. The output is never blurred. Works with or instead of panel_static.
source_blur_segmenteroptCOMBOSAM 3.1What finds the face / head / hands / person and the mouth for source_blur. SAM 3.1: text prompts. Sapiens2: body-part classes in one pass, finer lips and teeth (downloads facebook/sapiens2-seg-0.4b, 1.6 GB, to models/sapiens2 the first time). 'keep lips + tongue' always uses Sapiens2 for the lips.
source_blur_effectoptCOMBOblurblur: no detail left. invert colours: keeps the shapes and the expression but not the skin tone or colours.
panel_staticoptCOMBOoffTSC's fix for swaps in the same world: cover the face (or the whole person) of the pinned clip in TV static, every frame (SAM 3), so the generated half cannot copy it and takes the identity from the reference. The output is the generated half only, so the static never shows.
settingoptIMAGEThe place picture (BFS Setting Picture: a frame of the clip with the person in static). It becomes the last <Picture n> and the prompt says it shows the setting (write {setting} to place its tag).
vlmoptCLIPOptional VLM (CLIPLoader with a Qwen3-VL text encoder) for the prompt. Empty prompt: it writes one from the task + instruction (TSC's rules). Prompt with {...} fields (TSC's template): it fills them from the clip and the pictures. Prompt without fields: used as written.
vlm_max_tokensoptINT1024128–4096—

Outputs (7)

NameTypeDescription
imagesIMAGE—
audioAUDIO—
canvasIMAGE—
layout_textSTRING—
latentLATENT—
promptSTRING—
side_by_sideIMAGEComparison like BFS Shot Join's: original | references | result, the info on top and the prompt below.