File size: 43,044 Bytes
e488ce7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# Tuning the Motion Lab pipeline

A working guide for dialing the pipeline to a specific user's content and
taste. Written for humans and for AI assistants doing the tuning on a
user's behalf. Everything here comes from same-seed comparisons on our
test clips; expect your content to move the numbers a little.

## First: pick the graph

The start-here index with diagrams is `examples/README.md`; the hardware
rows are `HARDWARE.md`. The short table:

| priority | graph | cost (vs one baseline render) |
|---|---|---|
| the normal starting point (since 2026-08-19): 12-step base pass 1, turbo 6-step pass 2, audio seeded | `examples/motion_pipeline_ref2va_audioinit.json` | ~1.3x; 192 frames at 1 MP in ~12 min on our card |
| best quality, base model throughout, audio dial available | `examples/motion_pipeline.json` | ~3.5x; the same clip at 25 steps both passes is ~3x the row above |
| a 16 to 24 GB card (12 reported working in the field, just slower) | `examples/motion_pipeline_lowvram.json` | as the first row; see `LOWVRAM.md` for the fenced measurements |
| prompt and seed scouting, not a final | `examples/motion_pipeline_fast_iterate.json` | ~1x or less (0.2 -> 0.4 MP, ~95 s) |
| user knows where the problem is (two-pass: review the oraclemap, type ranges, requeue) | `examples/motion_pipeline_targeted.json` | baseline + regen cost of YOUR spans only |
| GUI editing on the node (blocks, painting, automation) | `examples/motion_pipeline_editor.json` | as targeted |
| maximum speed for one burst in a longer clip (segment crop + splice) | `examples/motion_pipeline_editor_segment.json` | baseline + regen of window+handles only; the crop report states the ratio |
| cheap pass 1, de-rope at the delivery size | `examples/motion_pipeline_upscale_derope.json` | 89% of native detail in 83% of the time (0.4 -> 1.5 MP) |
| de-roping footage that already exists (no baseline render) | `examples/archive/motion_pipeline_v2v_source.json` [alpha] | regen only, ~2.5x one baseline-equivalent render; there is no baseline pass |
| the dilated pass does not fit your card (OOM, or steps balloon while weights stream) | `examples/motion_pipeline_rolling_window.json` [alpha] | peak memory scales with the biggest WINDOW, not the clip. At the budget that forces a split: ~1.4x the generated frames, and on a card at the offload cliff wall time comes back to parity because the DiT stays resident instead of streaming. Measured on a fenced 32 GB budget: 30.7 GB peak + layer streaming one-pass vs 24.6 GB resident windowed, 460 s vs 440 s |
| earlier recipes (turbo-in-the-old-graph, probe + expert, featherweight, i50, split LoRA) | `examples/archive/` | kept so old write-ups resolve; do not start from these |

On turbo, the rule moved on 2026-08-19 and it is worth stating exactly.
Pass 1 decides the choreography and wants the base model at 12 steps or
more. Pass 2 repairs bursts from a known init and is where the turbo LoRA
belongs, **provided the whole pass-2 recipe comes with it**: `gradient_estimation`
and `linear_quadratic` on pass 1, a 6-step `beta` schedule on pass 2, the
audio seeded through `H3 Audio Smear`. The turbo LoRA dropped into the
25-step `res_multistep` graph with nothing else changed renders jerky and
pixelated (measured 2026-08-22, seven arms, all scrapped). That is what the
old "no turbo in the pipeline" rule was actually measuring.

The working rhythm: iterate prompts and seeds on the fast-iterate graph to
learn what a prompt gives you globally, then run the keeper through the
starting-point graph. The base model on pass 2 (the 25-step graph) is
better quality still, at three times the wall; reach for it when a final
has earned the time.

## Second: ask the user what they actually care about

The dials trade four things against each other: sharpness in the bursts,
pose fidelity to the source, render time, and audio feel. Get a ranking
before touching anything. A user who says "the flip looks melted" wants a
different dial than one who says "it changed my character's pose."

## Symptom to dial

| user says | do this |
|---|---|
| the reference image stops influencing pass 2 (or an upscale pass) after a graph change | before touching any reference dial, put `BasicScheduler` / stock sigmas back. One field report (2026-08-23) lost all reference influence to a third-party scheduler node and got it back with `BasicScheduler`; not a same-sigma comparison, so it is a diagnostic first step, not a theorem about that scheduler. Only then: reference strength, prompt roles, `ref_image_size` |
| dialogue or audio sounds wrong: mangled words, tags read aloud, audio that copies the audio reference instead of borrowing its voice | check the core first: `H3 Capability Probe` prints `tokenizer_special_tokens`; before ComfyUI PR #15808 (2026-08-22) `<d>` and six other H3 tags tokenized as two tokens each, and every dialogue reading on such a core is suspect. Update, re-render, then tune steps, turbo and audio refs |
| motion still smears inside a burst | raise `d_max` toward 4 if lowered; lower `q` toward 0.70; check `bridge` is not 0 |
| brief stutter or soft frames mid-burst | `bridge: 8` (fills the dip between burst peaks) |
| poses drift from the source (head angle, hand position) | `bridge: 0` first; if not enough, lower `inject` toward 0.5 |
| output ignores the source choreography, invents moves | `inject` is too high; come down toward 0.6 |
| source artifacts leak into the output | `inject` is too low; go up toward 0.7 |
| too slow / too expensive | raise `q` toward 0.85 (tighter spans); switch to the turbo graph; then the probe graph |
| coarse mosaic / tiling in a turbo pass 2 when pass 1 was ALSO turbo (the seed-hunt iterate-then-finish flow) | a turbo init only tolerates a SHALLOW turbo pass 2. Judged in playback, one fight clip, fl2va, 0.7 to 1.5 MP, rank-21 at 1.0: 1 step at inject 0.20 clean and good; 3 steps at 0.50 and 6 steps at 0.50 both out of quality bounds. A later playback check on a second content class found 1 step at 0.30 already visibly griddy, so 0.20 is the measured ceiling, not a conservative pick, and 0.30 to 0.50 is progressive tiling onset. The same 3-at-0.50 recipe is clean over a bare 12-step pass 1, so the init is the variable, not the recipe. For keepers from turbo seed picks: stay at 1 step / 0.20, or hand pass 2 to the base model. Do NOT stack a second shallow tap through pixel space: each extra tap pays a VAE encode/decode roundtrip that costs more than a 0.20 solve returns (judged: 2 taps a step below 1) | split the pass with the rolling-window graph: `H3 Window Plan` + `H3 Window Collect`. Set `max_dilated_frames` below the dilated count your card last survived, read the plan report before rendering, queue one window per item. Leave `coverage` on `full clip` on upscale graphs or the calm spans stay at baseline resolution |
| turbo output changes appearance (adds ornament, shifts style) | lower LoRA strength 0.8 toward 0.65; or raise `base_head` in the expert schedule so more structure forms on the base model |
| audio feels thin | raise `reference_mix` (needs the non-probe graphs); it is happiest near 0 or 1, mid values can double misaligned impacts |
| audio impacts feel soft | known vocoder trait; try `reference_mix: 1` for baseline foley, or accept lean |
| unprompted speech or vocal sounds appear | any voice your prompt describes without verbatim words gets gap-filled: sometimes a coherent invented line, sometimes non-language vocalizing. Script the words in the dialogue format, declare the voice indistinct by design, or cut the voice mention from the prompt |
| dialog sounds processed | check whether the speech overlaps a burst; unheld spans pass through untouched, so only speech during bursts is affected. `reference_mix: 1` restores the original line |
| probe init loses choreography | raise `probe_steps` from 6 toward 10 |
| background details change in regenerated spans (a flag recolors, props swap) | known limitation: detailed backgrounds re-roll during dilation. Try `inject` toward 0.5 (closer init tracking); simple backgrounds barely show it. A subject-only "foveated" mode that never regenerates the background is on the roadmap |
| smooth or slowly panning footage gets dilated spans much wider than the action | measured cause: on smooth content the latent jerk profile is nearly flat (max/min about 1.7 where the pixel jerk spans 20+), so a quantile threshold marks a fixed fraction of tokens hot by construction and the ramp+bridge fuse them into one wide window. Camera motion itself explains only a few percent of the jerk, so camera compensation does not help. Raise `q` toward 0.85, or gate the oracle through `H3 Manual Hold Map` with the ranges where the real action is |
| the oracle dilates spans the user does not care about / the pass is too expensive for one burst | `H3 Manual Hold Map` in gate mode: wire the oracle's hold_map, type the ranges that matter, everything else recovers to hold 1. The report output shows the effective length before the expensive pass runs |
| an action repeats or doubles in regenerated spans (two backflips become four) | the model fills dilated time with extra beats instead of slowing the existing ones; its prior for action density wins over the init. Try `inject` toward 0.5 (closer init tracking), and state exact counts positively in the prompt ("performs exactly two backflips in total"). On the roadmap as beat-anchored recovery |
| the clip contains a mirror or a big reflection and the beats double | do NOT blame the oracle first. Synthetic probe, 2026-08-09: a reflection showing the SAME motion at the SAME time leaves the oracle's output bit-identical (its threshold is a quantile of the profile, so duplicating the motion scales every token equally and selects exactly the same ones; profile shape correlation 0.9999). What does bite is a reflection prominent at a DIFFERENT time than the body, which a camera tilt produces (mirror early, subject late): that leaves two separated hot clusters and `bridge` welds them into one long plateau, +26% dilated time in the probe, of which `bridge` alone is +18%. So on mirror content try `bridge: 0` first, then `inject` toward 0.5, then explicit counts in the prompt. Everything left over after that is the model's action-density prior, not the oracle |
| background elements (birds, crowds, traffic) speed up during bursts | the AUTOMATIC remedies stay rejected (oracle-mask compositing popped at the boundary, blanket freeze degraded other artifacts). The manual path is new and promising: draw the keep-baseline region yourself (`show_drift` on the heatmap shows what to lasso in blue), wire it into `H3 Motion Composite` `mask` with `invert_mask` on, feather 48-64 smoothstep, and put the seam on a real edge (horizon, rooftop) where nothing moves. Use the `H3 V2V Init` `mask` freeze instead when background and subject share lighting or contact. First internal A/B (demo clip, invented flock + pagoda both removed, static seam) awaits playback ratification |
| half a second of an otherwise good clip is wrong (a face, a prop, one bad beat) | do not re-render the clip. `H3 Repair Plan` + `H3 Repair Splice`: type the bad frame range, let the plan snap it outward to whole tokens, and splice the rest back as original pixels. Type any nearby shot cut into `shot_cuts`; the exit belongs ON a cut. AUDIO IS STILL A WORK IN PROGRESS here, the splice is video-exact and passes audio through untouched |
| the de-roped span still plays accelerated: background agents run fast inside the held region | that is the stock clock, not the oracle. `H3 DyRoPE` gives the true-duration grid to some blocks or some steps. Two settings worth trying, `physical_blocks` 30-49 and 40-49; see the dose section below, and expect to judge it in playback because the meters cannot rank the two |
| a de-rope with DyRoPE on shows flicker at the span boundary | flash is an early-block effect: keep the physical grid out of blocks 0-9. Any `*_blocks` range starting at 25 or above meters at the stock control's flash level. If flicker still bothers you more than jitter does, switch to `fade_physical_to_compact` at `fade_end 0.7`, which is flash-free in playback |
| the sound restarts, jumps level or changes room at a segment join | wire the previous segment's audio as `audio_latent` and set `H3 V2V Init`'s `audio_prefix_ticks` to the handle (a 39-frame handle is exactly 65 ticks). Add `audio_prefix_release_ticks 8` for a soft handover, and then the ASSEMBLY must let the continuation own the overlap tail or the trim discards the ramp. It carries level, timbre and beat, not phrase structure |
| contrast and texture wash out further with every link of a chain | `H3 Drift Control` on the pass-1 model: schedule-matched noise on the carried video prefix so the model stops treating it as impossibly clean. `prefix_frames` must equal the handle and `matched_steps` + `taper_steps` must equal its latent step count (39 f = 12 = 8 + 4). replicated across two chains and new seeds; joins hold 0.85-0.89 through link 4 (previously 0.65-class at link 2 without it).4% and handles accumulate it. `H3 Delta Color Carry` cancels the encode bias by construction. Active path exercised on a real 4-link render 2026-08-24: it fires, corrects in the anchor direction, and stays sub-visible under its clamps on mildly-drifted content; on the deepest tested link it fires on both channels at or near its clamp. A strong-drift bench (where the 2.4% per-round-trip signature actually accrues) is still owed. |
| a window edit renders garbage, and the damage sits where the window USED to be | the conditioning length was not moved with the crops and the hold map. All four numbers move together: see `docs/WINDOW_MODE.md` |

## VRAM expectations (measured)

An honesty note on the peak numbers in this section: they were read
from `nvidia-smi` on a large card, where the reading includes whatever
the caching allocator felt like keeping, so treat them as upper bounds
rather than requirements (we have since measured a smaller model
peaking HIGHER than a bigger one this way). The portable units are
latent token count for activations, and the `loaded partially` /
`lowvram patches` lines in the ComfyUI log for whether your card is
actually streaming weights. When the dilated pass is what does not
fit, the rolling-window graph caps the token count per pass: see the
table above.

Weights: the int8 DiT is 20 GB, the text encoder 15 GB (offloads after
encoding), the video VAE 4.9 GB. Activations at 1.0 MP run about 0.1 GB
per latent token: a 5 s clip (37 tokens) needs ~5 GB, and its dilated
regeneration pass (87 tokens) ~9 GB. Roughly half that at 0.5 MP.

- 96 GB: everything stays resident, ~11.5 s/step.
- 32 GB (5090 class): the DiT fits, but the dilated pass of a 5 s clip
  lands around 30 GB total, right at the cliff, so ComfyUI starts
  offloading and steps balloon (~20 s/it reports). What helps, in order:
  start with 2 to 3 second clips (the dilated span is what hurts), drop
  the regeneration to 0.5 MP, or use the probe graph so there are far
  fewer of those expensive steps. Or switch stacks entirely: see the
  featherweight numbers below.
- 24 GB: featherweight plus 0.5 MP plus short clips territory. Untested
  by us so far.

Cost scales with the burst spans, not the clip length, so a long clip
with one short burst is far cheaper than these worst-case numbers.

### Featherweight stack, measured (ComfyUI 0.31+)

The smallest community-published variant of each piece: w4a8 DiT
(12.5 GB, Kijai/MiniMax-H3-experimental), int8_convrot video VAE
(3.2 GB), nvfp4 AWQ text encoder (15.7 GB, offloads after encoding).
Needs ComfyUI 0.31 with comfy-kitchen 0.2.28. Full de-roping pipeline
measured on one card (peaks are torch-allocated for the whole process):

| run | wall time | peak, TE resident | peak with TE offloaded |
|---|---|---|---|
| 3 s at 0.4 MP | 4.0 min | 34.4 GB | ~19 GB |
| 3 s at 0.7 MP | 6.3 min | 35.2 GB | ~20 GB |
| 5 s at 1.0 MP | 29 min | 43.8 GB | ~28 GB peak, ~20 GB sustained |

The last column is what consumer cards see: without `--gpu-only` the
text encoder leaves VRAM after encoding, which is default behavior.
The first column is the one **H3 Conditioning Bank** attacks: it banks
the encoded conditioning to disk and takes its `conditioning` input
lazily, so a queue item that hits the bank never executes the encode
node and the text encoder is not loaded at all. Wire it on any flow
that re-runs one prompt: rolling windows, seed hunts, extension chains.
Requeueing an unchanged graph does not need it (ComfyUI's node cache
already serves the conditioning); queueing a second workflow in
between, restarting, or editing anything upstream of the encode does,
and that is the normal shape of a session. **H3 Latent Bank** does the
same for a sampled pass: on the rolling-window graphs the pass-1
baseline is lost to the same evictions and re-renders in full before
window item 2 can start, so bank its LATENT (4.8 MB for a 107-frame
480x832 clip; the decoded frames would be 513 MB) and wire the noise
seed into the node's `seed` input so a new seed misses instead of
serving the old take. So
a 5 s, 1.0 MP de-rope fits a 32 GB card with a few GB of headroom (the
28 GB figure is a brief spike, under a minute total), and 3 s clips run
comfortably. Resolution barely moves the peak (0.4 to 0.7 MP added less
than 1 GB); frame count is what costs.

Two honest notes. Quality: the featherweight output is coherent and
sharp in our runs, but a given seed renders a DIFFERENT take of the
scene than the int8 stack; quantization changes the trajectory, so
compare quality, not pixels. Speed: on our card the w4a8 pipeline ran
somewhat slower per clip than int8 (29 vs low-20s minutes for the 5 s
case); on a 32 GB card that is the wrong comparison, because int8 does
not fit and offload-thrash costs far more.

## Chained clips and the audio clock

H3's video runs at 24 fps but its audio latent runs a 40 Hz clock, and
the two only agree on durations that are multiples of 1/8 s. Of the
legal `17k+5` frame counts, only k = 2, 5, 8, 11 ... (39, 90, 141, 192
frames) land exactly; every other length ships an audio stream up to
12.5 ms longer or shorter than its video (124 frames: audio 8.3 ms
long; 107: 8.3 ms short). One clip: harmless. Concatenate segments for
a long continuation and the error compounds into audible A/V drift --
we have watched a 10-minute extension from another tool visibly
separate by the end.

What to do about it:
- for anything destined for chaining, prefer the aligned lengths
  (90 / 141 / 192 frames);
- when assembling segments, trim or pad each segment's audio to exactly
  `frames / 24` seconds before concatenation, so no segment exports its
  rounding error to the next;
- inside this pack you are already covered: H3 Audio Recover, H3
  Segment Splice, and H3 Window Collect place and size audio on the
  absolute world clock (sample-exact per segment), so seams do not
  accumulate error.

`tests/test_audio_recover.py` is the regression guard for all of this:
run it bare for the synthetic sample-exact checks (no GPU), or hand it
a `baseline.mp4 recovered.mp4` pair from any `reference_mix: 1` render
and it verifies the recovered audio really is the baseline track --
duration within one AAC frame, zero lag, full correlation. It caught a
pre-fix render 64 ms short on its first outing.

### Known-good environment (this works for me)

Every number in this document was measured on this exact stack, on
Blackwell silicon, so it is a safe reference point for 5090-class cards:

```
GPU:      RTX PRO 6000 Blackwell / driver 610.43.02
Python:   3.12.13
PyTorch:  2.14.0.dev20260801+cu132 (nightly; Blackwell wants the cu13x builds)
CUDA:     13.2, cuDNN 9.24, Triton 3.8.0
Attention: sageattention (no flash-attn, no xformers)
ComfyUI:  0.31.1 with comfy-kitchen 0.2.28
```

If a 5090 runs far slower than the table above, check the torch build
first: a stable cu126/cu128 wheel on Blackwell can cost you more than
any dial in this document.

### Conditioning modes (I2VA, FL2VA, Ref2VA)

The pipeline works with all of them, unmodified, at the shipped
defaults. Tested end to end on the featherweight stack: wire your
`first_frame` / `last_frame` / reference inputs into BOTH conditioning
nodes (the baseline's and the regeneration's) and run.

- I2VA: the first-frame pin lands at t=0 on any clock, so the
  regeneration honors it exactly. Recovered frame 0 matched the
  reference in our test.
- FL2VA: the last-frame pin names a timestamp that lands mid-clip on
  the regeneration's dilated clock, and it does not matter: at inject
  0.70 the injected trajectory owns the timing and the pins act as
  pose guidance. The recovered clip hit the anchor pose at the true
  end. At much lower inject values this could in principle fight; if
  your FLF de-rope stalls mid-clip, raise inject back toward 0.70.
- Ref2VA (the ref2va checkpoint, w4a8 available): reference tokens
  carry no timeline, so they pass through dilation untouched. Subject
  identity held through the full de-rope in our test.

## Manual targeting (time ranges and spatial masks)

When the user can point at the problem, let them. Two tools, one per
axis:

- Time: `H3 Manual Hold Map` converts typed ranges (`36-60,
  1.5s-2.4s:3`; frames or seconds, ends inclusive) into an
  oracle-format hold map. Gate mode (oracle hold_map wired in) is the
  default recommendation: the oracle decides hold strength, the user
  decides where it is allowed to spend. Ranges snap outward to the
  token grid, so trust the segments output over the typed numbers, and
  show the user the report output: it states world length vs effective
  regeneration length, and estimates minutes when given a measured
  s/step. Cost scales with held spans, so this is also the speed
  answer for long clips with one burst.
- Space: `H3 Motion Composite` `mask` (pixel space, full feather
  control: size, linear/smoothstep/gaussian profile, in/out/centered
  direction) pastes baseline timing back outside the mask after
  recovery. `H3 V2V Init` `mask` freezes the region during generation
  instead; its default is HARD latent cells (mask_feather 0): every
  ~16 px cell fully frozen or fully live, no fractional blend cells.
  Rationale: half-frozen cells denoise as frozen/live mixtures that can
  themselves mush the seam, while the decode's receptive-field overlap
  smooths a hard cell edge for free. mask_feather above 0 restores the
  pooled fractional ramp if a hard seam ever shows in playback (report
  it; that reading is one A/B from ratified). Either way this path
  cannot do fine pixel feathering; the composite is the fine-feather
  tool.

The GUI for both axes at once is the `H3 Motion Editor` node
(`examples/motion_pipeline_editor.json`): timeline blocks, per-frame
painting, per-block dials, and automation envelopes (hold, feather,
strength) compiled into the same hold_map/mask wires. Queue once to
load the filmstrip, edit on the node, queue again; the baseline stays
cached. Its mask output is already feathered, so the composite runs
with `mask_is_soft` on and its own feather at 0. When tuning for a
user who cannot or will not paint, author `editor_state` JSON for
them; the contract is in the node docstring.

Compute honesty when a user asks where the speed comes from: TIME
targeting is the lever. Sampler cost scales with the dilated frame
count, so gating holds to the user's window helps, and `H3 Segment
Crop` multiplies that by dropping the un-held world from the regen
pass entirely (its report states the ratio). SPATIAL masks do not
reduce FLOPs; the DiT still processes every token and the mask only
controls blending. Sell the mask as quality control and the window as
the speedup.

A mask authored without frame-by-frame attention WILL cut off props
that swing with the subject (a sword mid-cartwheel leaves a static
blob and gets truncated at the boundary). Fixes, in order: paint
per-frame in the editor following the prop, enlarge the static region
to the prop's full arc, or feather outward. This is exactly the
static-vs-per-frame trade; a human on the timeline beats any static
region for rotating props.

Method rules for masks: static union masks cannot pop (the boundary
never moves); route the seam along a real image edge, not through sky
mid-gradient; lasso generously and let feather work; per-frame mask
batches reintroduce the moving-boundary pop and need harder feather
and a playback check. The two-pass rhythm for the targeted graph is
queue once, read `mainodes_targeted_oraclemap`, type ranges, requeue.

## When the expansion stops before the clip does (expand_to_end)

A hold map that expands a burst and then drops back to rate 1 for the
last few world frames reads as a small jump at the end of the shot: the
picture is in slow motion and then, a beat before the cut, it is not.
`H3 Time Smear` and `H3 Temporal Insert` both carry an `expand_to_end`
toggle, default ON, for exactly that shape.

What it does when it fires: the trailing rate-1 run is lifted to the
rate of the span in front of it, and the resulting length is put back
on the 17k+5 grid inside that same span, spending the deficit on the
LAST frames so rates only rise toward the end. The t2c fight window
`[1]*17+[2]*34+[1]*5` (56 world frames, 90 dilated) becomes
`[1]*17+[2]*27+[3]*12` (107 dilated); the same shape with the burst
later, `[1]*34+[2]*17+[1]*5`, becomes `[1]*34+[2]*10+[3]*12` (90
dilated). Mixed rates are the normal outcome, not a fallback: the added
frame count has to be a multiple of 17 and a single rate rarely lands
there.

What it never touches: uniform dilation, a map that already ends inside
an expansion, a map that is rate 1 all the way, and any rate-1 tail
longer than 17 world frames. That last one is the tail guard: one whole
group of frames or more of real time at the end of a shot is intended
rest, not an end jump, and adaptive oracle maps produce it constantly
(a 124-frame map ending in 39 quiet frames would otherwise go from 250
to 294 dilated frames to slow down a section nobody asked to slow
down). Those come back bit-identical, so existing graphs keep their
results. It also never
lowers a hold, so the user never loses expansion they asked for, and it
does not move where the expansion begins: only the tail is rewritten.
A rewrite prints one line to the console with the before and after map
and repeats it in the node's report, so nobody has to guess whether it
fired. `expand_to_end` off is the old behaviour exactly.

Cost warning worth saying out loud before turning it on: running the
span to the end usually buys frames. 90 to 107 dilated frames is about
19% more frame data and more than that in time, because per-step cost
is superlinear in tokens. The report output prices it.

Two traps that cost a round each while this was being built:

1. `H3 Inject Schedule`'s `preset` overrides the `inject` knob unless
   it is set to `custom`. Typing 0.45 into `inject` and leaving the
   preset at "balanced 0.70" renders 0.70, silently.
2. `H3 Time Smear` pads any segment under 39 frames. `_legal_ceil`
   floors at 39, so a 20-frame window smeared at rate 2 comes out at 39
   frames with the whole pad folded into the final hold, which is a
   long freeze on the last frame. With `expand_to_end` on, that pad is
   absorbed by the expansion instead (`[1]*5+[2]*10+[1]*5` becomes
   `[1]*5+[2]*11+[3]*4`, still 39 frames, no freeze). Either way, do
   not hand a sub-39-frame window to the smear and expect the length
   you asked for.

## Source footage instead of a baseline render (alpha)

`examples/archive/motion_pipeline_v2v_source.json` swaps the first pass for a file
on disk: `LoadVideo -> GetVideoComponents -> H3 Video Fit -> ImageScale ->
VAEEncode`, and from there the graph is the standard one. The oracle reads
the source's own encoded latent; nothing else in the chain changes.

Two things to get right, in this order.

1. Frame count. H3's legal lengths are 5, 22, 39 ... 17k+5. An arbitrary
   file lands between them, and the VAE does NOT complain: `encode_temporal`
   pads the last chunk by repeating the final frame, so a 312-frame clip is
   encoded as 323 with 11 frozen frames on the end. The oracle then reads
   those tokens as calm and under-dilates the real ending. `H3 Video Fit`
   trims to 311 instead and hands the count straight to the oracle's
   `length`. Read its report; it states which end lost frames.
2. Canvas. The source is resized to the `ResolutionSelector` dimensions with
   a centre crop, and the same width/height feed the conditioning. Pick the
   aspect ratio that matches your footage or you will crop the subject out.

`max_frames` on the fit node is the cost lever. Per-step time goes as
tokens**1.7, so capping a long source before fitting saves more than it
looks: 192 frames capped to 150 fits to 141, which is 42 tokens instead of
57, about 1.7x faster per step.

Audio: the graph keeps YOUR track. Exact recovery restores the world clock
frame for frame, so the source audio still lines up with the regenerated
video; it is wired into `H3 Audio Recover`'s `reference` at `reference_mix
1.0`. Lower the mix to hear the model's own foley for the slowed
performance instead, or blend. If your source has no audio stream, the fit
node's audio output carries nothing and that link must be removed.

## Repairing a few frames instead of the clip

The repair verb is for a finished clip that is good except for a moment of
it. Two nodes: `H3 Repair Plan` does integer arithmetic, `H3 Repair Splice`
does frame selection.

The tuning is almost all in the plan's inputs.

- `bad_start` / `bad_end` are 0-BASED and inclusive. A file-numbered frame
  46 is 45 here, and getting this wrong by one is the most common way to
  repair the wrong token.
- `hold` is the dilation on the repaired span only; everything else stays
  real time. Set `H3 Time Smear`'s `dilation` to the same number, and turn
  its `expand_to_end` OFF: an end-expansion rewrite moves every dilated
  coordinate the plan just computed, and the plan's report says so when it
  detects the case.
- `shot_cuts` is where the quality is. Type the 0-based first frame of any
  new shot near the span. A cut inside the span, or within `cut_reach`
  frames of its end, becomes the exit, and the splice hands back to the
  original AT the cut so the seam is an edit the shot already had. On the
  validation clip, not doing this left the exit seam popping at 2.6x the
  clip's median frame delta while the model reinvented the next shot's
  opening frames; extending the mask through the cut did not fix it (2.3x);
  exiting on the cut did.
- Wire `images` if you have the frames handy. It costs nothing and the
  report then tells you whether your entry frame is quiet, and names a
  quieter frame within four if it is not. The plan never moves the entry
  for you: change `bad_start` yourself if you want it.

Read both reports rather than eyeballing the result. The plan states the
snapped token span, the splice bounds and the cost; the splice states the
entry and exit seam deltas next to the ORIGINAL clip's own delta at the same
frames, because at a hard cut a big number is the right answer. The measured
cell (90 frames, bad 45 to 47, hold 3, cut at 53) read entry 3.99/255 =
0.26x median, exit 138.04 against the original's own 135.57 (1.8% apart),
and maximum absolute difference 0 outside the splice, which the node
asserts rather than reports.

**Audio is still a work in progress for repair.** The video splice is
frame-exact; audio handling across the splice is not finished. The node
passes an AUDIO input through untouched, same length and same clock, which
is correct when the repair did not change the performance and is not a
general answer. If the bad frames carried dialogue, plan on repairing the
picture and keeping your original track.

## DyRoPE: choosing the dose

`H3 DyRoPE` is a dial on one question: how much of the run sees the clip's
true world duration instead of the stock uniform grid. That dose is what
buys the timing correction on a de-roped span, and it is also what costs
shimmer, so tuning it is picking a point on one curve rather than finding a
setting that is good at everything.

Two findings shape the whole dial. **Flash is an early-block effect**: the
physical grid in blocks 0 to 9 reproduces the entire flash penalty (7.84x
against a stock control's 2.09x), while every arm that keeps it out of those
blocks meters at the control's level. **Jitter is a dose effect**: once the
early blocks are excluded, it scales with total physical-time exposure
(blocks 25-49 2.61x, 30-49 2.33x, 40-49 1.39x; fading by sigma 0.3 2.72x,
0.5 2.07x, 0.7 2.06x).

The two settings worth starting from, with no winner between them:

| setting | what you get | what it costs |
|---|---|---|
| `physical_blocks`, blocks 30-49 | world speed held neutrally through the span | slight flicker or hitch inside the span |
| `physical_blocks`, blocks 40-49 | the smoothest span, and the cleanest meters measured (1.17x to 1.39x jitter across three takes) | on some content it slightly overshoots and reads over-smooth or slowed |
| `fade_physical_to_compact`, `fade_end 0.7` | flash-free in playback, timing held | visible jitter (2.06x to 2.87x across takes) |

Below those doses the world-time correction is gone entirely: `fade_end 0.8`
loses it because the window is too short, and stacking a block restriction
on top of a sigma window loses it too, even though both parents keep it.
Above them, shimmer rises. There is no quieter setting that is still on
time, so a clip whose timing slips wants MORE dose, not a cleverer one.

Three method notes.

- **The meters cannot pick for you.** They rank flash and jitter, and they
  say nothing about whether the timing benefit survived. Every ranking
  above came from a meter; every benefit boundary came from playback.
- **Match the arms.** `physical_all` reproduces `H3 True Clock` exactly and
  `compact_all` reproduces having no node in the graph, so an A/B is one
  widget rather than two graphs. Keep the seed, the hold map and the
  schedule fixed across arms.
- **Watch out for the patch collision.** The `*_blocks` modes and
  `H3 Streamed Blocks` both own the double-block replacement slot and
  ComfyUI keeps one per block; the capability probe reports it. The fade
  modes use no block patches and compose fine.

## Audio across a join (alpha)

`audio_prefix_ticks` on `H3 V2V Init` freezes the first n audio-latent ticks
to the seeded `audio_latent` and leaves every later tick at
`audio_strength`. It is the audio twin of the video prefix freeze, and it is
how a continuation carries the previous segment's sound instead of starting
a new performance.

Four constraints, all of which have bitten:

1. **It needs `audio_latent` wired.** The ticks are the audio latent's clock,
   not the video's: a 39-frame carried handle is exactly 65 ticks.
2. **An AddGuide AUDIO guide and `ref_audios` cannot coexist** in the same
   graph; they collide in conditioning-audio packing. (Measured on core 82f839f5; treat as a current-core rule, not a model property.) Pick one route.
3. **A release changes assembly.** With `audio_prefix_release_ticks` above 0
   the last n ticks of the frozen prefix ramp up on a half cosine (8 ticks,
   0.2 s, is the upstream lineage's tested recipe), and the assembly step must let the
   CONTINUATION own the overlap tail. Trim the other way and the ramp is
   discarded, which is worse than never asking for it.
4. **Scope.** It carries local continuity: level, timbre, beat. It does not
   carry global phrase structure. A longer handle helps at the seam (90
   frames measured 0.953 overlap correlation vs 0.92 at 39) and gives the
   model a full musical phrase of context, but only a pinned master track
   GUARANTEES the structure; that guarantee is by construction.

## Chained segments: drift and colour (alpha)

Two separate decays, two separate nodes, and neither is a substitute for the
other.

`H3 Drift Control` is for contrast and texture washing out down a chain. The
carried prefix is bit-clean conditioning, and repeated clean conditioning
accumulates error: the seam probe reads join one around 0.9 and join two
around 0.65 across every variant tested. The node gives the carried VIDEO
prefix a small schedule-matched amount of the sampler's own noise at every
model evaluation, tapering to exact at the seam end. Audio is untouched by
design; the frozen per-tick audio prefix stays hard.

Wiring and limits, in the order they trip people:

- Wire the `H3 V2V Init` latent in for shape (it must already carry the
  noise mask; the node rewrites that mask, it does not create one), feed it
  the SAME base model your pass-1 guider uses, and take the guider's model
  from this node's output.
- `prefix_frames` must equal the plan's handle and sit on the 17k+5 grid.
  39 frames is 12 latent steps, and `matched_steps` + `taper_steps` must sum
  to exactly that: 8 + 4 is the field-validated recipe.
- It **refuses to stack** on another dynamic denoise-mask patch, and it does
  not support sigma-split samplers.
- **Replicated across two chains and new seeds.** Joins hold 0.85-0.89
  through link 4 on the newer chain; the earlier chain fell to the 0.65
  class at its second join before the recipe existed. Cross-chain
  comparison, not a paired drift-off arm. The recipe numbers are still
  one recipe, not a swept space.

`H3 Delta Color Carry` (fed by two `H3 Scene Color Stats`) is for the other
decay: each carried handle is a VAE round trip, each round trip darkens
about 2.4%, and a chain accumulates it. It decodes the prefix once, applies
a weak scene-one exposure and saturation correction in RGB, encodes both the
original and the corrected frames, and adds only E(corrected) - E(original)
to the latent, so the encode bias cancels and only the grade survives. The
delta is low-passed spatially and tapered from zero at the old edge to full
strength beside the generated future; audio is never touched. Anchor is
scene one's delivered tail, current is the current predecessor's tail, and
on link one they are the same tail, so the transform is the identity.

Status, so nobody tunes against a number that does not exist yet:
Active path exercised on a real 4-link render 2026-08-24: it fires, corrects in the anchor direction, and stays sub-visible under its clamps on mildly-drifted content; on the deepest tested link it fires on both channels at or near its clamp. A strong-drift bench (where the 2.4% per-round-trip signature actually accrues) is still owed.

Both are adapted from ComfyUI-MiniMaxH3-Contex-Loop (GPL-3.0, the same
licence as this pack); the per-module credit lines in `h3_drift.py` and
`h3_color_carry.py` state what was taken and what was deliberately not
ported.

## Window mode (retiming an excerpt)

When only part of a long clip needs retiming, the window lives in four
coupled places: the prefix crop, the excerpt crop, the hold map, and the
regeneration's conditioning length. Move three of them and the render breaks
with the damage parked at the OLD boundary, because a latent longer than its
conditioning is garbage and the downstream masks resample to the old
proportions. Window starts also quantize to a 17-frame lattice (0.708 s at
24 fps), so a request to start at a given second lands on the nearest rung.
The arithmetic and the procedure are in `docs/WINDOW_MODE.md`.

## Method

- Change one dial at a time, same seed, and compare in playback. Still
  frames lie about temporal quality in both directions; we have been
  burned by this repeatedly.
- The oracle heatmap (`H3 Jerk Heatmap`) shows where the pipeline will
  spend its budget. If the hot region misses the artifact the user is
  pointing at, fix `q` before touching anything else.
- Sharpness metrics rank frames usefully within one clip but do not
  decide between settings. The user's eyes decide.
- Cost scales with the burst span, not the clip length. A long clip with
  one short burst is cheap to fix.

## Trying the indecision oracle (experimental)

`H3 Indecision Oracle` is a second signal for the same hold map: the
model's own x0 jitter instead of the clip's jerk. It is not on by
default and nothing else moved for it.

- Cost: free if pass 1 already ran through `X0 Tap` with two steps
  dumped. It reads files, it does not sample.
- Compare, do not switch. Put it where the jerk oracle sits, flip `mode`
  between `indecision`, `jerk passthrough` and `blend max` with
  everything else fixed, and judge in playback. `jerk passthrough` is
  the same numbers the jerk oracle produces, so the only variable is
  the signal.
- Keep `q`, `d_max`, `ramp` and `bridge` matched across the arms. They
  feed the same compiler, and an unmatched knob turns the A/B into a
  comparison of two hold-map compilers.
- The `comparison` output is the measurement, not the vibe: whole-map
  Spearman, top-decile IoU, and the token-times where the two sources
  disagree most. Look at those token-times in playback first, since
  that is where the choice actually shows.
- If the map looks like a rectangle rather than like the picture, check
  the report for the degeneracy line. Masked and repaint runs pin token
  rows to exactly zero jitter and the oracle then draws the mask.
- Expected failure mode: fast small props. Jitter under-ranks them
  relative to frame-diff, which is the argument for `blend max` over
  `indecision` alone.

## Defaults, and why

`q 0.75, d_max 4, ramp on, bridge 8, inject 0.70` is the playback-ratified
starting point. The shipped default is now `inject 0.48`: at 0.50 and above some
scenes measurably loosen reference adherence, and 0.48 keeps the sharpness
without that risk. `inject 0.50` measured sharper with closer motion tracking
on our clips and is one preset away; some of us prefer it. The expert
schedule defaults (`total 8, inject 0.70, base_head 2`) give the turbo
tail its native 4 steps.

## Identity vs strength: the reference rule

Whether a higher inject or denoise is safe depends on what is anchoring
the subject, not on the route.

- No substantial references in the graph: keep the repaint strength
  under 0.5 when the subject's identity must match the source. Identity
  drift is measurable from about 0.30 and structural past 0.5; below
  that line the init still owns the subject, above it the model's prior
  does, and no prompt wording buys it back.
- With substantial references (identity refs, FLF pins, a strong anchor
  frame): higher strength is fine, and on the high-step routes (the
  full de-rope at 25+ steps) it often helps more, because the
  references carry identity so the extra strength buys motion quality
  instead of drift. The pinned FLF de-rope holding identity at inject
  0.70 above is the worked example.

## Refining this guide

When a tuning session finds a symptom/dial pair this table lacks, add it.
That is the point of the file.