40 KiB
BFS Shot Planner / Shot Loop
Turn a long video into shots a video model can actually follow, give every shot its own reference image and prompt, run the rest of the workflow once per shot, and join the results back into one video with the original timing and soundtrack.
Why
A video model only follows a guide reliably inside the clip length it was trained on. An H3 body-swap LoRA trained on 107-frame clips keeps the scene, framing and motion at 4.5 s and loses them at 10 s, where it starts summarising the clip instead of following it frame by frame. Splitting the source into model-sized shots, at camera cuts when there are any, keeps every shot inside that range.
Nodes
| node | what it does |
|---|---|
| BFS Shot Planner | Pick a video, split it on a timeline, set a reference and prompt per shot. Outputs the shots as a ComfyUI list. |
| BFS Shot Unpack | Opens one shot into plain values: guide frames, reference, second reference, prompt, length, first frame, audio, size, index. Use it with any model. |
| BFS Shot Repack | Puts edited pieces back into a shot: Unpack's shot output goes to Repack's shot, the edited piece (e.g. the reference with its background removed) to its input. Unconnected inputs keep their values; timing, cuts and audio are unchanged. |
| BFS Shot H3 Conditioning | Ready-made MiniMax H3 conditioning for one shot, built with the native nodes (Reference to Video + Add Guide). Optional duet: the shot's clip pinned in a side panel (canvas or shifted RoPE; route the model through the node for the shift); the join cuts the panel off. Optional prompt writer: pick a task (character swap, style, setting, appearance, lighting / weather, custom) and an instruction; with a VLM on its vlm input it writes each shot's duet prompt from the shot and its references, otherwise a template for the task (planner prompt, the default, keeps the planner's prompt). The written text comes out on prompt. Optional setting_ref (TSC's tip): one more reference, the shot's middle frame with the person covered in TV static, so the model sees the place in full detail; it is the last <Picture n> (write {setting} for its tag, otherwise a sentence is added to subject_definitions). |
| BFS Shot Join | Concatenates the decoded shots in order, trims each to its true length, cross-fades soft joins and returns the soundtrack. With comparison on it also returns a side-by-side video (original shot | references | result) with the shot's info and your label on top and its prompt below, ready for Create Video. |
| BFS Shot H3 Duet | Renders one shot with H3: the shot's clip pinned beside the video (duet, no LoRA), the shot as an aligned guide (for body-swap LoRAs), or both. |
The panel
Use the language selector in the top bar to switch the interface between English, Portuguese, Spanish and Simplified Chinese. The choice is saved in this browser and does not alter the workflow plan or prompt text. Tour opens an optional step-by-step introduction when clicked; it never appears automatically.
The planner's panel has five tabs, in the order you work. The timeline stays on top of every tab.
| tab | what is there |
|---|---|
| 🎞 Video | pick or upload the video, audio, how it is split into shots (cuts or fixed length), generation size |
| 🎬 Shots | one card per shot (scroll sideways, ← → move between shots), the preview player and the selected shot's editor: reference & prompt, who is replaced (mask, generation mode, {target}), continuity, Copy to other shots |
| 👥 People & masks | the cast found by face (a reference per person) and the global mask settings |
| 📝 Prompts & refs | the global reference and prompt, the VLM and the reference descriptions ({details}) |
| ▶ Run | Checks (what to fix before running, with a button to each shot), auto / queue loop, filters, test limits |
Shot cards show badges for what each shot has: 🎯 mask, 🎭 / ✂ generation mode, 🧑 {target}, ⛓ continuity and ⚠ problems.
The header's ⚠ count opens the checks.
Copy to other shots (in the shot editor) copies the chosen parts of the selected shot to all shots, or to the
shots after it: references, prompt, mask text, mask points (each shot gets them on its frame at the same relative
position), generation mode, {target}, continuity. It replaces the old "→ all" buttons.
How the loop works
ComfyUI runs any node that receives a list once per item and pairs several lists by index. The
planner's shots output is a list, so everything downstream of it (conditioning, sampler,
decode) runs once per shot, and shot i always gets guide i, reference i and prompt i.
Single values such as the model and VAE are shared by every run. The join takes the whole list
back (it is an INPUT_IS_LIST node) and puts the video together.
Two run modes:
- Auto loop (one run): every shot in one queue run. Simple; holds all decoded shots in memory.
- Queue loop (one shot per run): each run generates the next pending shot and stores it on disk. The join blocks every node after it until the last shot, then outputs the full video, so a Save Video node only fires once. With Auto-queue the next shot on, the panel re-queues the workflow after each shot. Reset loop starts over. Changing the plan starts a new run.
Guide frames and lengths
Each shot is generated at a length the model accepts (frame grid: H3 17n+5, LTX/Wan 8n+1,
Wan 4n+1, or any). When a shot is shorter than that length, the extra guide frames come from
the video that follows it, so the guide never repeats or stretches. The join cuts each result
back to the shot's own frames, which keeps the timing identical to the source. Those extra
frames are also a real overlap, which the join uses to cross-fade boundaries that are not
camera cuts.
Splitting
- Camera cuts (default): cuts from PySceneDetect (
adaptiveorcontent; installscenedetect), or the built-in detector when the package is missing. Shots shorter than the minimum merge into a neighbour when the merge still fits; shots longer than the maximum split into equal parts. - Fixed length: equal parts no longer than the maximum.
- One shot (whole range): the whole range (start / end frame, Max total seconds) as a single shot, whatever its length; Max seconds / shot is not applied. For "N seconds in one piece" set Range length, seconds = N, right next to the mode (it moves the end frame; Max total seconds in the Run tab does the same for quick tests). For pieces of at most X seconds use Fixed length with Max seconds / shot = X.
- By hand: drag the white handles on the timeline to move a boundary, double-click the shot bar to split, ⇥ Merge to join, or select a shot and press Delete to remove the cut at its start (it merges into the previous shot). Your edits are what runs.
Range (🎞 Video tab, with its own player): Start frame / End frame (timeline frames; end 0 = the end of the video) limit the plan to a part of the video, before anything is split, analysed or generated. Play or scrub the player (or click the timeline) and press ⇤ start here / ⇥ end here to take its exact frame. The timeline dims what is outside; the join and the soundtrack cover only the range. Max total seconds counts from the start of the range.
Max seconds / shot is converted to frames and snapped down to the grid (4.5 s at 24 fps = 107 frames for H3). Max shots and Max total seconds cap a long source.
Audio
The source card shows whether the video has a usable audio track (rate, channels) or why not. Use the
video's audio or silent track. Without a usable track (none, unreadable, or silent chosen) the planner's
audio, BFS Shot Join's audio and the H3 Duet nodes return a silent track of the right length instead
of nothing, so Create Video / Save Video always work. Audio is read with ffmpeg, or with PyAV when the ffmpeg
binary is missing.
Preview
Play shot plays only the selected shot, from its first to its last frame, and stops (or loops with loop shot); every shot card has its own ▶. Play all plays the whole plan and jumps over shots that will not run. The panel shows the shot's start, end and current time (and frame), a yellow playhead follows on the timeline, and clicking the timeline seeks. The source video streams from the input folder, so only what you play is loaded.
Filters
Skip shots that should not run, decided per shot from a few sampled frames:
| filter | skips a shot when |
|---|---|
| Needs a person | no person is detected |
| Min person size | the largest person covers less than that fraction of the frame (wide shots) |
| Max people | more people than that (crowds) |
| Needs a face | no face is detected |
| Skip dark / fades | the mean brightness is below the level |
| Skip static | there is almost no motion (title cards, freeze frames) |
| Min frames | the shot is shorter than that |
People and faces use YOLO from models/ultralytics (person_yolov8m-seg.pt, face_yolov8m.pt, the
Impact Pack files) with OpenCV fallbacks. Analyse people & faces shows the numbers on every shot
card. Each shot can override the filters (always run / always skip).
Skipped shots do not run. Connect the planner's timeline output to BFS Shot Join and they are
filled with the original video, keeping the full duration and soundtrack, or removed (with the
matching audio) when Skipped shots in the output is set to remove them.
Cast (people by face)
Find people samples the video every 0.5 s, detects faces and groups them into people by face
identity, on the CPU (it never touches the GPU; the result is cached per video). No extra package is needed: by
default it uses OpenCV Zoo's YuNet (detection) + SFace (recognition), Apache-2.0 models run by OpenCV itself and
downloaded once into models/opencv_face (0.2 + 37 MB). If InsightFace is installed (buffalo_l in
models/insightface) it is used instead (a bit more accurate; mind its license). Last resort, without either: SAM 3.1
faces grouped by colours (hair, skin, clothes). Each
person card shows the face, screen share and first/last appearance (click the face to seek).
Give a person a reference (and a second one) by clicking the slot, or pick one from the recent
references; ignore leaves that person out. Every shot card shows who is in it, with the main
person (most screen time) highlighted and linked people outlined in green.
| option | effect |
|---|---|
| Shots use their main person's reference | a shot without its own reference takes its main person's (shown with a dashed green outline) |
| Split where the main person changes | adds a boundary where the largest face switches to another person for at least a second, then re-splits |
| Only run shots with a linked person | shots where no linked, non-ignored person appears are skipped ("no linked person") |
A reference set on the shot itself always wins; the global reference is the last fallback.
References and prompts
Every shot can have its own reference, second reference and prompt; shots without them use the
global ones. Click an empty reference slot to upload an image straight from your computer; a filled
slot has ✕ to remove it; Copy to other shots gives a shot's references to every shot. The last ten
references you picked are shown under the shot editor for one-click reuse (click = reference,
shift+click = second reference). Connected ref_image, ref_image_2 and prompt inputs override
the panel's global values, so an edited first frame or a prompt from another node can drive the
defaults.
The planner also outputs ref_image and ref_image_2: the references the shots actually use,
without repeats, so a plan where every shot shares one reference returns a single image.
Mask & crop (SAM 3)
Generate only part of the frame. In a shot's editor (Who is replaced), type what to segment in English
(person in white, woman with red hair, red car; up to 32 tokens, commas for several things) or click
🎯 Points to pick positive / negative points on any frame of the shot, then 👁 Preview (red = mask,
yellow = crop box). Then pick how the shot is generated:
| mode | what is generated | crop / uncrop |
|---|---|---|
| Full frame | the whole frame; the mask only feeds {target} and the setting picture |
no |
| 🧩 Frame + paste | the whole frame (so the model follows the guide's pose freely and the new person may be bigger); BFS Shot Join then pastes only the person onto the original frame: the shot's mask plus the new subject's outline (SAM 3 on the result with the shot's new subject text, default person; paste_new_outline) |
no crop; paste in the Join |
| 🎭 Mask only | only the mask, on the whole frame; everything else stays exactly as it was | no |
| ✂ Crop | a box around the mask (more pixels for a small person); BFS Shot Join pastes it back | yes |
| ✂🎭 Crop + mask | only the mask, inside the crop: the most detail with the background kept | yes |
Frame + paste is the safest for a different body (e.g. a woman in profile replaced by a man facing the camera):
Mask only binds the new person to the old outline, Frame + paste does not. The two mask modes need BFS Shot H3 Conditioning → inpaint = per shot (planner) (the default; only the mask
forces it on every shot, off never). The crop is one box around the mask (the union over all its frames, so it
does not shake), at the source resolution, sent to the model at the generation size; BFS Shot Join pastes the
result back, feathered by the mask (or the whole box). BFS Shot Unpack also outputs the shot's mask (mask),
e.g. for an inpainting model. In BFS Shot Join's comparison video, cropped shots show their mask (red) and crop
box (yellow) over the original column (comparison_mask, on by default).
Mask opacity (mask modes, per shot, slider under the modes): the value of the white inside the generation mask.
1 regenerates the masked area completely. A grey mask (e.g. 0.85) keeps part of the original there: H3 puts those
rows at opacity × the noise level, so they start from the original partly visible. 0.8-0.9 keeps pose, outline and lighting while still swapping; too low copies the original
person. With guide_mode = aligned guide the model also sees the whole original shot as its guide, so it follows
the motion either way. (Not applied with the duet panel.)
🎨 Color Guide is a separate optional visual cue. Turn it on for a shot with a mask and choose the marker color
for ref 1. The default colors for the global references are set below each image in Prompts & refs; a shot may
override them. The selected region is tinted in the source guide, while the image detail,
movement and expression remain visible underneath. This does not turn on Mask only or create an H3 generation
mask. The prompt must explain that the tint is temporary and that the matching reference replaces the tinted person.
For consistency across shots, People & masks → Find people groups sampled faces by identity (ArcFace through
InsightFace when installed; otherwise YuNet + SFace, or a weaker appearance fallback). Choose the matching Person N
in each shot to inherit that cast member's Color Guide color. This is a user-confirmed link: profile views, small faces
and occlusion can make identity grouping uncertain. Leave the shot on manual identity when unsure. The shot's mask
still selects the actual region to tint, and its color can be overridden for that shot.
For two people in one shot, open + Add a second person, use ref 2 for that selection, and choose a different color (purple by default; duplicate colors are disabled). Enable use_ref_2 on BFS Shot H3 Conditioning. The second SAM 3 text selection is optional; preview it before queuing. Color opacity and ArcFace identity links are under the optional settings. Use this prompt and replace the color names if you choose another palette:
body_swap: subject_definitions: <Subject 1> is the character shown in <Picture 1>, whose identity, face, head, hair or fur, skin, body shape, proportions and clothing define the green-marked replacement. <Subject 2> is the character shown in <Picture 2>, whose identity, face, head, hair or fur, skin, body shape, proportions and clothing define the purple-marked replacement. In the guide video, the translucent green and purple tints are temporary editing markers, not character appearance. Replace the green-tinted person only with <Subject 1> and the purple-tinted person only with <Subject 2>. The guide video provides the scene and the complete performance of both people, including their body poses, gestures, head motion, gaze, speech and facial expressions.
summary: [video generation + references] Replace the green-tinted guide person with <Subject 1> from <Picture 1> and the purple-tinted guide person with <Subject 2> from <Picture 2>, preserving the scene, camera and complete motion and expression of both people.
detailed_description: [Shot 1] Replace the person marked by the translucent green tint with <Subject 1> from <Picture 1>, and the person marked by the translucent purple tint with <Subject 2> from <Picture 2>. Each character follows that person's exact position, timing, body pose, gestures, head motion, gaze, speech and facial expressions in the guide. Remove both temporary color markers completely. Keep the two reference identities distinct and stable in every frame. Preserve their interaction, the background, lighting, camera and all unselected content from the guide video. Prevent identity drift, clothing changes, texture crawling and flicker. The result is a real, photographic video.
The VLM's shot analysis suggests a SAM 3 text selection from the original frames; it does not assign guide colors or
decide that two detections are the same identity. Describe target only describes the first shot selection. For
two-color swaps, choose both selections and identities yourself, and use the explicit mapping above. The VLM prompt
writer for duet is a separate split-screen task; its generic character-swap draft assumes the supplied pictures
show one character, so use planner prompt for this two-character template. Describe refs also treats ref and ref 2
as views of one identity; write the two subject definitions explicitly when ref 2 is a different person.
Mask guide (experimental, Mask only shots, BFS Shot H3 Conditioning → mask_guide): the shot with only the
masked region visible, the rest grey, so the model looks at the subject's pose and outline on its own while the mask
limits where it generates. + extra guide adds it as a second aligned guide next to the normal one (the model sees
both); instead of the full guide replaces the normal one; + reference video gives it as a native reference video
(<Video n>, after the shot's own one when guide_mode uses it; write {mask_video} in the prompt where its tag goes, otherwise a sentence is added). mask_guide_look sets what it shows of the subject: grey blurred (default: volume, light and head direction, no colours or face, so the model follows the motion without copying the old subject), colour (as it is; can make the model copy the old subject), silhouette (flat shape), edges (outlines) or pose (people) (the skeleton of the people in the mask, OpenPose colours; needs ultralytics and a YOLO pose model in models/ultralytics, e.g. pose/yolov8m-pose.pt). mask_ref_size makes that reference video smaller (default 1/2, ~1/4 of the tokens): it only has to show the pose and outline. Compare with
off: the LoRAs were trained with one full guide, and it can also pull the old subject's look.
Masks from a video (rotoscoping) instead of SAM 3
A mask made elsewhere (After Effects Roto Brush, DaVinci Resolve Magic Mask, another ComfyUI workflow…) can replace
SAM 3. It is a black and white video, white = the subject, and it works with every mode (Mask only, Crop,
Crop + mask), {target}, the setting picture and the comparison overlay. Three places, the most specific wins:
| where | covers | wins over |
|---|---|---|
| shot editor → 🎞 mask video | only that shot (its first frame = the shot's first frame) | everything |
| the shot's own SAM 3 text / points | that shot | the two below |
the planner's optional mask input (MASK or IMAGE batch, e.g. Load Video + Convert Image to Mask) |
the whole video, one mask per source frame | the panel's mask video |
| People & masks → 🎞 Mask video (whole video) | the whole video, matched by time | SAM 3 for shots without a mask |
Any size and frame rate: it is matched to the shot by time and resized. The console says which mask each shot used.
Stitch finishing (BFS Shot Join, cropped shots only; adapted from Neko (Nekodificador)'s NKD Inpaint Stitch, MIT, built on AbleJones's workflow and nodes):
| Option | What it does |
|---|---|
edge_hardness (0-1) |
hardens the soft edge of the paste; raise it when a faint ghost of the original person shows around the new one |
match_colors (0-1) |
corrects the colour / brightness drift of the generated patch (Reinhard in LAB), with statistics over the whole shot so it does not flicker |
match_region |
around the subject (swap): measured on a ring of background around the mask, so a new person keeps their own colours. inside the subject: measured inside the mask, for retouching the same content |
seamless_edges |
Poisson blend (OpenCV) for stubborn seams; slower |
Which mode. A generation mask alone keeps the background exact but generates the person at the frame's own size, so a small person gets few pixels; a crop alone gives the person more resolution, but the whole box is regenerated and pasted back (that is what the finishing above is for). Mask only is the simplest when the person fills a good part of the frame; Crop + mask is best for small people. In the mask modes the latent starts from the shot's own frames and its SAM 3 mask (grown by expand plus one latent cell) becomes the H3 generation mask. Raise expand when the new person is bigger than the old one (longer hair, wider body): the generated area cannot go past the grown mask; their shadow or reflection outside the mask stays from the original.
Credit to Neko (Nekodificador) and AbleJones, whose workflow and nodes the crop + generation mask follows. ComfyUI builds without native H3 generation masks need a per-row mask patch on the model (e.g. ComfyUI-MiniMaxH3-PerRowMasking).
The People & masks tab holds the global mask settings, with defaults that work as they are: fill holes on,
temporal expand 2 frames (less flicker), expand 16 px, feather 12 px, padding 15 %, paste by mask, blockify off
(16 aligns the mask to H3's latent grid), threshold 0.5, 4 objects. Show masks on shots overlays the preview
on the shot cards. SAM 3 uses the official sam3.1_multiplex_fp16.safetensors in models/checkpoints,
downloaded from Comfy-Org/sam3.1 the first time.
VLM suggestions
Connect a vision-language model to the planner's vlm input (CLIPLoader with qwen3vl_4b or qwen3vl_8b).
It looks at a few frames of every shot and suggests what to segment, a description of the shot (camera,
framing, action) and whether to run it. In the Prompts & refs tab: Use the VLM when the workflow runs applies the
suggestions at run time (mask text for shots without one; {shot} in a prompt becomes that shot's
description), Analyse shots asks from the panel (after one run with the VLM connected), and each shot's
editor shows its suggestion with buttons to apply it. The summary output lists them too.
Describe references ({details})
The VLM can describe the references, for any task (not tied to swaps), and any subject: a person, a creature, a robot,
an animal or a cartoon character (it says what the subject is first). In the Prompts & refs tab pick an instruction
preset: short (the default: one noun phrase of at most ~20 words that fits inside a sentence, e.g. green goblin with long pointed ears in a torn brown leather vest, for <Subject 1> is the {details} shown in <Picture 1>), full body (face, hair, skin, age, build, clothing piece by piece), head / face, face attributes (a
short comma-separated list), outfit, or custom (your own instruction). 📝 Describe refs writes one
description per reference set: the global references, every shot's and every cast person's. They are saved in
the plan and editable. Write {details} anywhere in a prompt and every shot gets the description of its own
references; a set without a description is described by the VLM at run time (no click needed).
Fit the prompt (on by default, next to 📝 Describe refs): the VLM gets the sentence of the prompt that holds the
marker, with a blank in its place (e.g. <Subject 1> is the ___ shown in <Picture 1>), and writes the description for
it, so the description adapts to the prompt and not the other way round (no "the A goblin", no paragraph inside a
sentence). The panel buttons use the global prompt (or the shot's own); at run time the prompt the shot really uses.
The VLM buttons in the panel work after the workflow has run once with the VLM connected (ComfyUI only hands models to nodes when they run). Tested with Qwen3-VL 8B; some smaller or modified models return empty answers for some wordings: the planner retries with a reworded request and with sampling, but if a preset stays empty, use the 8B or another preset.
Settings reference
Every setting in the panel has a hover tooltip (ⓘ). The main ones:
| setting | what it does |
|---|---|
| Mode | Camera cuts (shots start at cuts, long ones split evenly), fixed length, or one shot (the whole range) |
| Detector / Sensitivity | PySceneDetect adaptive or content, or the built-in one; higher sensitivity finds more cuts |
| Frame grid | frame counts the model accepts (H3 17n+5, LTX/Wan 8n+1, Wan 4n+1) |
| Max / Min seconds per shot | longest shot sent to the model; shorter shots merge into a neighbour |
| Timeline fps | the frame rate the planner works on (generation and audio use it) |
| Max shots / Max total seconds | test on the first shots or seconds only |
| Megapixels / Size multiple | generation size at the source's aspect ratio, snapped to the multiple |
| Run | auto loop (all shots in one run) or queue loop (one shot per run, stored on disk) |
| Skipped shots in the output | keep the original video there or remove those shots |
| Mask: padding / expand / feather / temporal expand / blockify | crop context, paste-back growth and softness, flicker hold, square blocks |
| Mask: threshold / max objects / paste back | SAM 3 text matching, objects tracked, paste only the mask or the whole box |
| VLM: frames per shot / max tokens | how much the VLM sees and how long it may answer |
One seed per shot: BFS Shot Seed (1.94.0)
A seed widget on the sampler gives every shot the SAME seed (the node runs once per shot with the same value).
BFS Shot Seed (shot in, seed out) makes it a choice: same for every shot, increment per shot (seed, seed+1 ...)
or random per shot (unrelated but repeatable seeds from the base seed). overrides pins single shots, e.g.
3:12345, 5:777, to keep the shots that came out well and re-roll only the others.
Structured starting noise: BFS Shot Noise (1.94.0, experimental)
Optional. BFS Shot Noise gives the NOISE for SamplerCustomAdvanced; with every amount at 0 it is exactly RandomNoise.
| Input | What it does |
|---|---|
shot_seed (+ shot, overrides) |
the seed of each shot: same / increment / random per shot, as BFS Shot Seed |
shared_base |
the coarse pattern of the noise comes from base_seed: the same for every shot, at any resolution and any length (it is a tape along the timeline); the fine detail stays per shot. Put the seed of a result that worked in base_seed to keep its coarse noise |
distribution |
gaussian (default), laplacian, student-t or uniform, all at unit variance |
spectrum_tilt |
1/f^alpha spectrum: > 0 puts more energy in the large blobs ("fractal" noise), < 0 in the fine grain |
pyramid |
coarser layers of noise added on top |
static_share |
a part of the noise identical in every frame |
guide_structure (vae, guide_frames, mask) |
outside the mask, the guide's latent is mixed into the noise |
ref_tint (vae, ref_image, mask) |
inside the mask, the noise leans toward the reference's average latent |
All mixes keep unit variance. The model was trained on plain noise: use small amounts and compare with 0.
{details} filled automatically (1.92.0)
A {details} in a shot's prompt is filled without pressing 📝 Describe refs: the planner's VLM describes the shot's
references before any SAM 3 work; with no VLM on the planner, the slot is left for BFS Shot H3 Conditioning or
BFS Shot H3 Duet, whose vlm input (the same Qwen3-VL CLIPLoader) describes them (the short preset, fitted to
the sentence). Edited descriptions always win. With no VLM anywhere it becomes "character" and the log says so.
Source blur (keep the mouth) (1.91.0)
source_blur on BFS Shot H3 Conditioning, BFS Shot H3 Duet, BFS H3 Duet and BFS H3 Duet Conditioning
blurs the source person in what the model sees of the source (aligned guide, native reference video, duet panel),
keeping the mouth sharp: the model still reads the pose, the motion and the lips but has no face to copy, so the
identity comes from the reference. Masks from SAM 3.1 (the part, minus a slightly grown "mouth" mask), cached per shot.
face (keep mouth), head + hands (keep mouth) (also hides hair and hands), person (keep mouth) (the whole body:
only for big changes, the pose then comes from the blurred shape). The kept area of Mask only and the output are
never blurred. Trick shared by JalenBrunson (Sapiens2 there) for reliable ref2v swaps.
Big videos (upload)
ComfyUI refuses any single request bigger than --max-upload-size (100 MB by default), which is why big videos fail
to upload in most nodes. The planner's ⬆ Upload (source video, mask videos, pictures) sends files of 32 MB or more in
8 MB pieces that are written straight to disk and assembled in the input folder, so any size works without changing
how ComfyUI is started (a dropped piece is sent again). For other nodes, start ComfyUI with a higher limit, e.g.
--max-upload-size 4096, or copy the file into ComfyUI/input and pick it from the list.
A newly selected or uploaded source starts as one shot covering at most its first 15 seconds. This is only the initial plan: widen the range or choose Camera cuts / Fixed length and press Auto split when you want multiple shots. Existing saved plans keep their own range and split mode.
Continuity between shots
Each shot (after the first) can continue from the previous shot's generated result: in the shot editor,
Continuity = previous shot as reference adds a frame of it as one more <Picture n> after the shot's own
references (good across camera cuts, keeps the person consistent), and previous shot as first frame anchors it
at frame 0 of the shot (for continuous action without a cut). Pick which frame: the previous result's first,
middle or last. Copy to other shots with continuity ticked applies it to every shot after the first.
It needs the previous result to exist: it works in the queue loop (the planner reads the stored result) and in
the auto loop with BFS Shot H3 Duet (it renders shot by shot and keeps the last result). BFS Shot Unpack also
outputs it as previous_result. This is different from H3 Conditioning's first_frame, which anchors the shot's
own frame from the source video.
History: a long take, window by window (1.87.0)
Continuity = previous shot's end as history carries the last 17, 34 or 51 frames of the previous shot's result into the next shot as clean frames placed just before frame 0 on H3's timeline: the model sees them as the immediate past of the video it is generating, so motion, camera, light and the new look continue across the join (instead of one still frame). Idea from Akatz's H3 Relay windowed edit and Ethanfel's MiniMax H3 Context Loop; this is BFS's own implementation (a process-local patch of H3's packed layout that moves the video, its audio and the other guides past the history; ComfyUI files are not edited).
- On by default (1.88.0) for every shot after the first; set Continuity = off on a shot to drop it. Plans saved before keep what they had.
- Same take only. A shot that starts at a camera cut skips it (nothing to continue). For one long take, split it with Mode = fixed length (or let Camera cuts split long shots) and set history on every shot after the first with Copy to other shots → continuity. Shots still exist: they are the windows, and cuts reset the history.
- Every mode: full frame, Frame + paste, Mask only (the mask regenerates the target; the history is a condition outside it), Crop (the history is cut with this shot's box) and the duet canvas (the source frames under the history go into the panel strip). Not with duet shifted RoPE. The aligned guide still drives this shot's own frames, so sync stays frame-exact.
- BFS Shot H3 Conditioning and BFS Shot H3 Duet both use it (duet canvas or aligned guide).
- Queue loop: shots run in order and each reads the stored result of the one before. Auto loop: taken from the previous render when it matches.
- Video only for now (the previous audio is not carried). Costs one more 17/34/51-frame condition per shot.
MiniMax H3 with an aligned guide (example)
BFS Shot Planner ── shots ──► BFS Shot H3 Conditioning (clip, vae, guide_mode = aligned guide)
└─ positive, latent ─► CFGGuider / sampler ─► VAE Decode ─┐
BFS Shot Join ◄── images ─────────────────────────────────────────────────────────────────┘
└─► Create Video (fps, audio) ─► Save Video
For other models, use BFS Shot Unpack and wire guide_frames, ref_image, prompt and
length into that model's own guide and reference nodes; the list mapping works the same way.
Choosing who is replaced ({target}), step by step
With several people on screen, a prompt that says "replace the person" leaves the model to guess. {target}
puts a short description of the right person into the prompt of every shot ("the young woman with dark hair in a
pink top"), so the model knows WHICH one to swap. The selection is made with SAM 3, and the description by the
VLM, so it works for anyone or anything you can click on.
What you need
- The planner's
vlminput connected to a Qwen3-VL (CLIPLoader, type stable_diffusion,qwen3vl_8brecommended). - The workflow run once with it connected. ComfyUI only hands models to nodes when they run; after that the panel buttons can use it.
- Without a VLM it still works: you get the cut-out and type the description yourself.
1. Put {target} in the prompt
Write {target} wherever the prompt names the person being replaced: in the global prompt (all shots) or in a
shot's own prompt. For example, the swap caption the H3 LoRAs were trained with:
... only the face, body and clothing of {target} are replaced. summary: [video generation + reference] Replace
{target} in the guide video with <Subject 1>, keeping the scene, camera and the complete motion and facial
performance of the guide video. detailed_description: [Shot 1] <Subject 1> takes the exact place of {target}
and moves with the same timing, body pose, gestures, ...
{target} can be combined with {details} (the description of the references) and {shot} (the VLM's
description of the shot).
2. Select the person
Open a shot and click 🎯 Points…. The modal shows one frame of the shot; the slider below picks the frame.
- Click on the person's body (torso), not only the face. A click on the face selects the head, and the description then misses the clothes.
- Right-click (or shift+click) puts an exclude point, e.g. on a second person standing close.
- 👁 Segment shows what SAM 3 picked (red). Add or remove points until only that person is red.
- Instead of points you can just type what to segment (
woman in pink,man with glasses,dog): no clicking is needed, SAM 3 finds and tracks it through the shot. Points are for when the text is ambiguous (two similar people). Points win when both are set. Copy to other shots with mask text (and mode) ticked gives the text (and the generation mode) to every shot.
Then save:
| Button | What it does |
|---|---|
| Save (this shot) | keeps the selection on this shot only |
| Save → all shots | applies the same points to every shot, each on its frame at the same relative position as the frame you clicked on (e.g. clicked in the middle of this shot → every shot uses its middle frame) |
Save → all shots is for a person who stays in the same place across shots (a sequential video, a fixed camera).
🎯 Track → whole video (in the points window) is for a person who moves around: the selection is tracked by SAM 3
through the whole video (or the range), forward and backward from the frame you clicked on, and every shot without
its own selection takes its part of that track. Also type a short text (e.g. woman in pink top): after a camera cut
the tracker uses it to find the subject again. The People & masks tab shows the track and removes it. The first run
tracks the whole range (a minute or two per few hundred frames); the result is cached until the selection changes.
3. Describe the person
Click 🧑 Describe target in the shot's editor:
- SAM 3 cuts the person out of the selected frame, and a thumbnail of the cut-out appears next to the field.
- The VLM writes a short phrase into the
{target}field. - The field stays editable: correct it or write your own. Keep it short and visual (who, hair, main clothes with colours) and start it with "the".
- Copy to other shots with {target} ticked copies the description to every shot. Use it when the same person is replaced throughout; otherwise describe shot by shot.
4. Check and fix
- 👁 Preview on a few shots shows the selection on several frames of each shot.
- In a shot where the selection picked the wrong thing, ✕ Clear removes its points, text and mode. Select again in that shot only (Save (this shot)) and describe again.
- Copy to other shots with mask points ticked copies that shot's points to every shot without opening the modal.
What happens when the workflow runs
Every shot's prompt gets its own {target} text: the one you wrote or made with 🧑 Describe target; when the field is
empty but the shot has a selection (mask text, points or a mask video) and the VLM is connected, the subject is cut out
and described automatically (written for the prompt's {target} sentence). A shot with neither gets "the person". The selection is
only used to make the description: it does not crop or mask anything unless the shot's mode is Mask only, Crop
or Crop + mask.
Examples
- Two people, swap only one: in the first shot, click on the woman's torso, put an exclude point on the man, then Save → all shots and 🧑 Describe target → "the young woman with long black hair in a white blouse". Copy to other shots with {target} ticked.
- The person changes between shots: describe shot by shot, e.g. shot 1 "the man in the grey suit", shot 2 "the woman in the red dress". Each shot keeps its own text.
- Animals: it works the same way, e.g. "the small black dog in front".
Troubleshooting
| Problem | Fix |
|---|---|
| "No VLM yet" | connect the vlm input and run the workflow once, or type the description |
| "Select the person first" | the shot has no points or mask text: use 🎯 Points… |
| "SAM 3 found nothing" | click again on the person (on the torso), or try another frame with the slider |
| Only the face or the hair is described | the click was on the head: click the body |
| The other person is described | add an exclude point (right-click) on the other person, 👁 Segment, save, describe again |
| The swap still hits the wrong person | make the description more specific (position: "on the left", clothing colours) |