A long on-camera host segment on the Spark is never one render: it is short windows joined at pauses. We tested where to cut, restarting every window from the reference photo, and which ElevenLabs settings give you pauses to cut on. The joins work, though each one reads as a camera cut. What did not work was getting our AI host's mouth to close at the pauses.
The problem
Every lip-sync model we run on the Spark works in windows. InfiniteTalk generates 81 frames at a time at 25 fps and carries the last 9 frames into the next window as motion context. Wan 2.2 S2V works in 77-frame chunks at 16 fps. LTX 2.5 image+audio renders the whole clip in one pass. But our house rule caps each clip at 20 seconds (our host tool refuses anything longer), because a longer take is a long, uninterruptible GPU job, so a bad take is expensive to throw away.
So a 60 to 90 second on-camera host segment is never one render. It's a chain. Two questions decide whether the chain looks like one take:
- Where you cut, and how each window starts. Back to the reference photo every time, or from the last frame of the window before?
- What the voice gives the model to work with. A pause is the only moment you can check that the mouth closes, and it's the only clean place to cut. The ElevenLabs model and settings decide how many pauses you get and how long they are.
We measured both, on our DGX Spark on Nov 2. The voice side is a five-setting sweep of one script. The video side is one chained take (test 180: Daisy's 51-second story in three windows) and one window rendered nine ways (180's first window, 179, and 183 to 189). Four more tests were planned and did not run: the same story chained from each window's last frame (182), and a 48-second line as one take against the same line in three windows (181, with the single takes 323 and 313). The box froze twice during the day and the queue ran out of time. Where those would have answered something, I say so.
How we tested
The script. One 115-word story in the voice of Daisy Lane (our AI host; ElevenLabs premade voice Jessica, cgSgspJ2msm6clMCkdW9): a road trip to the coast for a sunrise, three short paragraphs, lots of short sentences, four "..." pauses. Here it is:
Okay, story time. Last summer, four of us drove to the coast to catch the sunrise. One tiny car. Zero plan. We left at three in the morning... with snacks, a playlist, and no idea where the beach actually was.
Around four, the map just gave up. No signal. So we followed the seagulls. Seriously. Windows down... you could smell the salt before you could see a thing. And then the road just ended. Sand.
We ran the last bit barefoot. The sky went pink, then gold... and nobody said a word for a whole minute. For us? That's a record. Somebody's flip-flop floated away. Totally worth it. Best wrong turn ever... Okay. Your turn.
The voice sweep. The same text through bin/tts.py (the ElevenLabs with-timestamps endpoint, then single-pass ffmpeg loudnorm with a -16 LUFS target; the files measure -17.1 to -17.8) at five settings, all on the same voice id:
| Setting | Model | stability / similarity / style / speed |
|---|---|---|
| v3 | eleven_v3 | 0.5 / 0.75 / 0 / 1.0 |
| v4 cast | eleven_v4 | 0.45 / 0.75 / 0.3 / 1.0 (Daisy's cast settings) |
| v4 speed 0.9 | eleven_v4 | 0.45 / 0.75 / 0.3 / 0.9 |
| v4 calm | eleven_v4 | 0.7 / 0.75 / 0 / 0.9 |
| v2 + break | eleven_multilingual_v2 | 0.5 / 0.75 / 0 / 1.0, every "..." replaced by <break time="1.0s" /> |
For each file we counted pauses two ways: gaps between words longer than 0.35 s from the word timestamps (the same threshold our lip-sync score sheet uses), and silences from ffmpeg silencedetect at -35 dB lasting at least 0.3 s. A short script does both counts and writes them out per file, and the settings each file was made with are recorded beside it from the API request.
The video tests. All LTX 2.5 22B distilled image+audio (int8, two-stage, CFG 1.0, image strength 1.0) at 1280x704, 24 fps, seed 42, on ComfyUI 0.43 core nodes, run through our one GPU queue:
| Test | What | Audio | Face |
|---|---|---|---|
| 180 | Daisy story, 3 windows, each from the reference photo (15.67 + 15.67 + 19.67 s) | story on eleven_v3, 51.04 s | Daisy's on-set still |
| 183 | Window 1 of the story on eleven_v4 cast settings (14.33 s), against 180's window 1 on v3 (15.67 s) | story on eleven_v4 | same |
| 184, 185 | Window 1 again at seed 1234, on v3 (184) and v4 (185) | v3, v4 | same |
| 186 | Window 1 on multilingual_v2 with break tags (16.33 s), seed 42 | story on v2 | same |
| 187 | Window 1 on v3, seed 42, prompt asking for closed lips between sentences (no "warm smile") | story on v3 | same |
| 179 | Window 1 on v3, seed 42, our host tool's default prompt | story on v3 | same |
| 188, 189 | Window 1 on v3, seed 42, host tool prompt, on Daisy's front portrait (188) and three-quarter still (189) | story on v3 | portrait, three-quarter |
Same prompt and seed for every window of the chain. The chained take is assembled by tools/assemble-chain.py on the narration's clock (details in Setup), then scored like every other lip-sync test tonight: bin/score-sheet grabs the frame at the midpoint of every pause over 0.35 s (mouth should be closed) and every 4th word (mouth should be open or moving), and a reviewer counts them. bin/identity-strip lays 12 frames from first to last in a row for drift.
On top of the counts, the assembler measures each seam. It takes the face crop of every frame at 256 px wide and computes the mean absolute brightness difference (0 to 255) between consecutive frames. Inside a segment that number is small: the face moves a little. At a seam it's whatever jump the cut makes. We report each seam as a multiple of the within-segment median, and drift as the difference between the frame at 0.5 s and the last frame, plus the change in the face crop's average colour.
What the voice gives you to cut on
Measured on the one 115-word script (media/audio/*.pauses.sd.json, timestamps corrected with silencedetect as described in Setup):
| Setting | Length | Words/s | Pauses > 0.35 s | Pauses > 0.6 s |
|---|---|---|---|---|
| v3 | 51.04 s | 2.25 | 23 | 14 |
| v4 cast | 46.72 s | 2.46 | 23 | 1 |
| v4 speed 0.9 | 47.12 s | 2.44 | 22 | 2 |
| v4 calm (stab 0.7, style 0, speed 0.9) | 46.80 s | 2.46 | 23 | 1 |
| v2 + break tags | 49.04 s | 2.35 | 24 | 15 |
| Setting | Longest pause | Total pause time |
|---|---|---|
| v3 | 1.29 s | 17.0 s |
| v4 cast | 0.72 s | 10.5 s |
| v4 speed 0.9 | 0.80 s | 11.3 s |
| v4 calm (stab 0.7, style 0, speed 0.9) | 0.64 s | 10.9 s |
| v2 + break tags | 1.34 s | 18.5 s |
Here's the thing that table says. Every setting gives you about the same number of pauses. What changes is how long they are. On v4 at the cast settings, 22 of the 23 pauses are under 0.6 s, and the longest is 0.72 s. On v3, 14 are longer than 0.6 s. For lip-sync that matters twice: a short pause is a few frames for the mouth to close in, and a short pause is a bad place to cut, because the seam needs silence on both sides.
Two settings did less than I expected. speed 0.9 on v4 made the whole read 0.40 s longer (46.72 to 47.12 s), under 1%. With speed held at 0.9, raising stability to 0.7 and dropping style to 0 made the read 0.32 s shorter (47.12 to 46.80 s) and the longest pause shorter (0.80 to 0.64 s), not longer. If you want pauses on v4, these knobs don't give them to you. Use v3 with "...", v2 with break tags (the four 1.0 s tags came out as pauses of 1.25 to 1.34 s here), or add silence in the edit.
Loudness: all five went through the same single-pass loudnorm. It doesn't change timing (our TTS notes measured identical durations with and without it) and its effect on the lip-sync model was not measured tonight.
What the slower voices cost. Less than I expected. We ran all five reads back through ElevenLabs speech-to-text (scribe_v1) and diffed them against the script: every one came back word-perfect, 115 of 115, with no laughs or sighs slipped in After loudnorm they sit within 0.7 loudness units of each other (-17.1 to -17.8 LUFS integrated, ffmpeg ebur128). The real costs are length and wait. v3 is 9% longer for the same words (51.04 s against 46.72 s), which is 9% more of a 60-second on-camera budget and 9% more GPU, and the API took about twice as long (21.3 s against 10.3 s for 628 characters). Whether the read sounds livelier or flatter is not measured.
Does a longer pause close the mouth?
Not in our controlled test. Not for Daisy, not on LTX 2.5 image+audio, and nothing else we tried changed that either.
Going in, the takes we already had pointed that way. Another post tonight scored its four published host takes (eleven_v4 at the cast settings, seed 42): the mouth closed at about 6 to 7 of 19 pauses, every one of them 0.36 to 0.56 s, right at v4's cap ([Animated HTML vs generative video](/video-lab/animated-html-vs-generative-video-when-not-to-generate)). Our methodology's gate is 4 of 6. A third post had two of Daisy's lines on eleven_v3, and one of Daisy's and one of Rafael's (another AI host) on v4; I scored them with tight face crops: v3 about 3 or 4 of 11 closed, v4 about 1 or 2 of 7. Different lines, one seed each. A hint.
So we ran it controlled. One window (the story's first 40 words, cut at a silence), nine renders, one thing changed at a time:
| Test | Voice | Seed | Prompt | Image |
|---|---|---|---|---|
| 180 seg 1 | eleven_v3 | 42 | test prompt ("warm smile") | on-set |
| 184 | eleven_v3 | 1234 | test prompt | on-set |
| 183 | eleven_v4 cast | 42 | test prompt | on-set |
| 185 | eleven_v4 cast | 1234 | test prompt | on-set |
| 186 | multilingual_v2 + break tags | 42 | test prompt | on-set |
| 187 | eleven_v3 | 42 | asks for closed lips at pauses, no "smile" | on-set |
| 179 | eleven_v3 | 42 | our host tool's default (no "smile") | on-set |
| 188 | eleven_v3 | 42 | host tool default | portrait, mouth closed |
| 189 | eleven_v3 | 42 | host tool default | three-quarter |
| Test | Closed at pause | Open on speech | s/s |
|---|---|---|---|
| 180 seg 1 | 1/6 | 10/10 | 18.8 |
| 184 | 1/6 | 10/10 | 19.1 |
| 183 | 0/5 | 10/10 | 18.5 |
| 185 | 0/5 | 10/10 | 18.5 |
| 186 | 1/6 | 10/10 | 18.5 |
| 187 | 1/6 | 10/10 | 19.6 |
| 179 | 1/6 | 10/10 | 17.2 |
| 188 | 0/6 | 10/10 | 18.1 |
| 189 | 1/6 | 10/10 | 18.0 |
That's 6 of 52 pauses closed and 90 of 90 speech points open. The failure is one-sided: Daisy talks fine, she just never stops smiling with her mouth open. Here's what each lever did:
- Voice model. v3 closed 2 of 12 over two seeds, v4 0 of 10, v2 with break tags 1 of 6. No render closed at the window's longest pause (1.03 s on v3, 1.34 s on v2); she holds an open grin through both. The one pause most v3 takes close on is the same moment every time (10.88 s, a 0.49 s gap), so in this window closure seems to follow what she's saying more than how long the silence is.
- Prompt. Asking for closed lips and taking out "warm smile" (187), or using our host tool's default prompt (179), gave frames nearly identical to 180 at the same seed. With the image held at full strength, the prompt barely moves the face.
- Reference image. Her front portrait has the mouth closed, so it was the obvious suspect. It closed 0 of 6, and it brought a new problem: LTX pulled the camera back from the tight portrait framing to show her shirt mid-take, then pushed back in. The three-quarter still did the same and closed 1 of 6.
- Seed. 1 and 1 on v3, 0 and 0 on v4.
Every window took 17.2 to 19.6 s of compute per second of video, with the model already loaded.
Two takes point the other way, and they're worth knowing. This post's own instructional window DAISY-I3 (eleven_v3) closed at exactly its three longest pauses (1.43, 1.03 and 0.92 s) and stayed open at the three shorter ones. And a take in our lip-sync shootout (eleven_v3, seed 42) closed at all three of its pauses over 0.6 s and none of the shorter ones. One take each, not a controlled test, but it means long pauses may help some lines.
Two things this doesn't say. It doesn't say voice settings never matter: the same tables in our framework have Theo and Sloane passing the gate on some takes. And it doesn't cover InfiniteTalk, which closed 5 of 6 pauses on a cartoon host in our Nov 1 lab run (WORKFLOWS.md) but hasn't been run on Daisy yet. What it does say: for our lead host on LTX image+audio, pick the voice for how it sounds and where you need to cut, and handle the pauses in the edit.
The chained take
Test 180: the whole 51-second story in three windows, each rendered from Daisy's on-set still at seed 42, cut at 15.67 s and 31.33 s, assembled on the narration's clock.
| Test | Strategy | Length | s/s | Closed at pause |
|---|---|---|---|---|
| 180 | 3 windows, each from the photo | 51.0 s | 18.7 | 6/23 |
| Test | Open on speech | Seam jump (x within-window median) | Drift, 0.5 s to last frame |
|---|---|---|---|
| 180 | 26/28 | 10.3x and 10.9x | no visible identity drift; face colour moved under 2 of 255 |
Here's what that table means in frames. Inside a window, the face crop changes by a median of 1.73 brightness levels (out of 255) from one frame to the next, and never more than 8.33. At the two seams it jumps 17.9 and 18.8: about ten times the median, and about twice the biggest change anywhere inside a window. You can see why in the frames either side of each seam: the window ends with Daisy leaning in on a wide grin, and the next window starts from the reference photo's pose, upright with a smaller smile. Same framing, same light, so it reads as a jump cut.
The punch-in version scales the middle window to 1.15x. The pose still resets at both seams, but now the framing changes at the same moment, so it reads as a cut between two cameras, which is what an edited interview looks like anyway. It costs nothing to make, and it's the version in the short film.
No visible identity drift. Face, hair, the LEJ shirt and the necklace hold from the first frame to the last, and the face crop's average colour moved by -1.9, -0.3 and -0.6 (red, green, blue, out of 255) over 51 seconds. The plain difference between the frame at 0.5 s and the last frame is 23.5, bigger than either seam, but that's pose and expression (she's smiling wider and leaning), not identity. We can't yet say how much of the stability comes from going back to the photo: window 1 alone moved by up to 3.5 colour levels in its 15 seconds, and the last-frame and single-take comparisons didn't run.
The pause count matches the window tests: closed at 6 of 23, open on speech at 26 of 28. The longest pauses in the story (1.19 s and 1.29 s, near the end) both get an open grin.
What didn't run. Two comparisons were queued and never ran: the same story chained from each window's last frame instead of the photo (182), and one 48-second line as a single LTX take, as three windows, and through InfiniteTalk's own windows (323, 181, 313). So this post can't tell you whether last-frame chaining beats going back to the photo, or how a single long take drifts against a chain. Those are the next tests.
Setup and gotchas
- Cut on the waveform, not the timestamps. On eleven_v3 the character alignment sometimes hangs a pause on the start of the next word instead of the end of the last one. In the story, "Around" is timestamped 15.59 to 16.48 s, nine tenths of a second for a two-syllable word; silencedetect puts silence at 15.40 to 16.24 s. Uncorrected, the v3 file shows 12 pauses over 0.35 s; corrected, 23.
tools/words-sd.pymoves a word's start or end to the silence edge when a detected silence overlaps it (it moved 18 of 115 words on v3 and 4 on v4). Score sheets for these tests use the corrected file. This also matters for anyone scoring v3 audio with the raw timestamps: they'll miss pauses. - Snap every cut to a third of a second. LTX ia2v needs 8k+1 frames at 24 fps, so
tools/vlab.pyfloors each clip's length to a multiple of 1/3 s and trims the audio to match. Cut anywhere else and each segment loses up to a third of a second, which piles up across a chain. Our cuts sit inside measured silences on that grid: 15.667 and 31.333 s for the v3 story. - Cut to WAV, not MP3. An MP3 encoder adds a priming delay at the start of every file. Segments are cut from the MP3 with ffmpeg into 44.1 kHz PCM WAV, sample-accurate (
tools/make-segments.py). Each segment's job asks for its length plus 0.0001 s, so the floor lands on the grid and not one step below it (15.6667 s gives 377 frames; 15.66666 would give 369). - Drop one frame per seam. A segment of n thirds of a second is 8n+1 frames: one frame longer than its audio. The assembler drops the last frame of every segment but the final one, so the chain has 24 x length + 1 frames and frame k is exactly k/24 s into the full narration. Then the full, uncut MP3 is muxed over the top. Without this the picture slides one frame later at every seam.
- The punch-in trick. When each segment restarts from the photo, the host snaps back to the starting pose at every seam. The assembler also writes
chain_punchin.mp4, where every other segment is scaled up 1.15x and centre-cropped, so the snap reads as a cut to a tighter camera. Zero GPU. On test 180 the pose still resets, but the change of framing at the same instant makes it read as a cut to a second camera (the chained take section above). - Last-frame chaining (not tested here). The alternative to going back to the photo is starting each window from the last frame of the one before. The seam should be smoother, but each window then starts from a frame the model drew, so anything it got slightly wrong becomes the next window's reference. Test 182 would have measured that; it didn't run.
- InfiniteTalk chains internally. Its 9 motion frames carry pose across windows, which is why its seams don't show, and its maker says I2V colour shifts past about a minute (InfiniteTalk README). In ComfyUI core, longer runs mean stacking Video Extend groups, 81 frames each (Comfy tutorial). It also costs about 72 s of compute per second of video on this box (Nov 1 lab run), against about 17 for LTX ia2v.
Settings
- LTX 2.5 ia2v:
ltx-2.5-22b-distilled-transformer-comfy-int8-convrot+gemma4-12b-with-proj-ltx-2.5-comfy-int8-convrot, bf16 video and audio VAEs, two-stage (8 sigmas at half resolution, 2x latent upscale, 3 sigmas at 1280x704), CFG 1.0, image strength 1.0, narration encoded by the LTX audio VAE and frozen in both stages, seed 42, 24 fps. Prompt for Daisy: "A young woman with long honey-blonde wavy hair in a white crop top with pink LEJ letters sits at a desk in a dark studio and talks to the camera, warm smile, small natural head movements, static camera". Tests 179, 188 and 189 use our host tool's default prompt instead (bin/host-segments), and 187 the lip-closure prompt in its row. Exact graphs: each test'sworkflow_api.json. - ElevenLabs: with-timestamps endpoint,
mp3_44100_128, then ffmpegloudnorm=I=-16:TP=-1.5:LRA=11single pass. Settings per row in the table above. - Cuts: inside a silencedetect silence (-35 dB, at least 0.3 s), on the 1/3 s grid, every segment at most 20 s.
- Assembly: H.264 CRF 18, AAC 192k,
+faststart; seam metrics on a centre-top face crop (half the width, 60% of the height), 256 px wide.
Speed and memory
Every number here is from the test's timing.json: wall time from submit to ComfyUI reporting done, divided by the output's length. No model-load lines appear in any test's ComfyUI log excerpt, so all of these ran with LTX 2.5 already loaded.
| Test | Output | Wall | s/s | Min MemAvailable |
|---|---|---|---|---|
| 180 (3 windows) | 1280x704, 51.0 s | 294.6 + 279.5 + 379.5 = 953.6 s | 18.7 | 25 GiB |
| 179, 183-189 (one window each) | 1280x704, 14.4-16.4 s | 266-308 s | 17.2-19.6 | 26-35 GiB |
For scale, InfiniteTalk ran at about 72 s of compute per second of video on this box in the Nov 1 lab run, so the same 51-second take would be about an hour.
What I'd do
Go back to the reference photo for every window and punch in on every other one: the seams are visible (about ten times the normal frame-to-frame change) but read as cuts, and we saw no identity drift over 51 seconds. Cut long host takes inside real silences found on the waveform, on LTX's third-of-a-second grid, with no window over 20 s. Use eleven_v3 (or v2 with break tags) where you need a real silence to cut on, because v4 at our cast settings gave one pause over 0.6 s in 47 seconds of speech. Don't expect any of it to close Daisy's mouth at pauses on LTX image+audio: in nine controlled renders it didn't. Keep her on-set still, write her on-camera lines with fewer long pauses, cover the long ones with a cut or a panel, and say "sync is loose at pauses" when it is.