File size: 82,532 Bytes
c2471a3 | 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 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 | ---
license: apache-2.0
tags:
- comfyui
- minimax-h3
- video-generation
- text-to-video
- workflow
- audio-video
pipeline_tag: text-to-video
---
# MiniMax-H3 Seamless Chain
A ComfyUI node pack and two workflows that render a multi-shot MiniMax-H3 scene
as **one continuous take**: no visible cut at the shot boundaries, no colour
shift between shots, and continuous audio across the whole piece.
MiniMax-H3 natively generates blocks of roughly 10-15 seconds. This pack chains
those blocks into arbitrarily long scenes and hands back a single master video
with a single master audio track.
Released as **v2.7.0** of the ComfyUI-H3-Multishot pack.
## v2.7.0 - per-subject voices, flf_chain fixed, chain leveller, ComfyUI 0.34
- **Per-subject voice refs** (`voice_ref_2` / `voice_ref_3`, both samplers):
each character keeps their own voice across a chained scene - verified blind
across a full chain, no cross-speaker bleed.
- **flf_chain fix:** the memory bank stood in flf chains and pinned a clip of
shot 1 (which opens on boundary plate 0) into every later shot as an unbound
reference - PLATE0 bled back in from shot 2 onward. The bank now stands down
automatically in flf_chain; plates alone carry the continuity there.
- **H3ChainNormalize:** post-chain texture + colour leveller - the 2.6.0
long-take ratchet limit is addressed.
- **refresh_pin splice alignment**, **x0 clamp dial** (`x0_clamp_window`,
dose cap 0.30), in-loop latent upscale + `refresh_renoise` +
`pin_noise_ramp` + `auto_chunk_ffn`.
- **ComfyUI 0.34 supported:** native interior keyframe anchors detected; the
pack's layout patch stands down automatically on 0.34, unchanged on older
cores.
- **Engine-aware writer:** separate H3 (`<d>[English] ...</d>`) and LTX
(quoted dialogue) system prompts for every mode via an appended `engine`
widget.
## v2.6.0 adds the extend take: one prompt, one continuous speech, as long as you want
Set **`take_seconds`** on MASTER CONTROLS (or open `H3_Extend_Take`, now the
main workflow, shipped at 1280x736 with a 30-second take) and give the writer
one premise. The panel sizes a window for your
card and the number of windows that fills the time; the writer's new
**extend take** join style writes ONE continuous speech and cuts it across
the windows at sentence boundaries; the chain continues it under
`context_pin` in H3's own voice β no TTS, no airlock, no per-shot dialogue
budgeting. Verified before shipping: seven writer-driven renders including a
65-second, 7-window take, every join continued the speech, reviewed blind as
one uninterrupted take. Also in 2.6.0: reference photographs now win over the
writer's prose (the writer points at the photos instead of describing a face -
same-seed measured), auto refs reach the sampler on their own (a second gate
downstream used to discard them silently), three
reserve-planning fixes from the 24 GB test lab and a writer fix for silent
shots echoing the voice anchor. Full notes in the changelog below.
**Known limit (2.6.0):** the chain's texture ratchet is not fully solved for long takes - measured about +13% fine texture per join at 736x1280 with the anti-drift set on. Under ~4 windows (~30-40 s) it is slight; at 7 windows it is visible sharpening. Keep extend takes to ~4 windows for now; a pin-side fix is in progress for 2.6.1.
## v2.5 is the memory release
Four new memory systems, all measured, two of them fully automatic:
- **The driver-headroom rule (automatic).** High-resolution renders used to
take anywhere from 27 minutes to 3 hours for identical work β a lottery
caused by the Windows driver demoting GPU memory when the card fills past
roughly 95%. The pack now detects that zone and deliberately streams a few
GB of weights instead of riding the ceiling. The lottery render became 15
minutes, every time. Nothing to configure.
- **`low_ram_master`.** Long chains used to hold every finished shot in
system RAM until the final join β tens of GB at the very last step. Switch
it on and shots stream to lossless disk staging as they finish; peak RAM
becomes about two shots regardless of chain length, with verified-identical
output (42.8 dB against the RAM path β codec noise).
- **Remote text encoder.** The text encoder runs for seconds per shot and
holds 15+ GB the rest of the time. Point the new node at any second PC
running ComfyUI with this pack and that memory leaves your render card β
identical results, verified across machines, with a local cache so repeated
text never touches the network. One flag on the switches panel turns it
on; it ships off.
- **`H3 TAE Decode`.** 2-second full-resolution draft previews from a 9 MB
tiny decoder, versus about a minute per shot through the real VAE. For seed
hunts and batch triage, never finals.
Plus a Speed Boosters panel (Spectrum, TeaCache, block cache, EasyCache β
each measured and eye-tested, with honest notes about which distort people),
and new defaults tuned for 16-24 GB cards. Details in the changelog below.
## What this repository contains
- `ComfyUI-H3-Multishot/` - the ComfyUI custom-node pack (samplers, loaders,
studio controls, LoRA stack, GGUF architecture patch).
- `workflows/` - three ready-to-load ComfyUI graphs:
- `H3_Seamless_Chain_v2.json` - the full workflow: master controls, LLM
prompt writer, speed boosters, remote encoder lane, anchors.
- `H3_Seamless_Chain_CORE.json` - the same chain with **zero third-party
dependencies**.
- `H3_Keyframes.json` - a single clip with anchors at chosen frame
positions.
- `INSTALL.md`, `SETTINGS.md`, `PROMPTING.md` - install steps, the full
settings reference, and the boundary/prompt rules.
## What this repository does not contain
**No model weights.** Nothing here is a checkpoint, a text encoder, a VAE or a
LoRA. Download the weights separately:
| Component | Where |
|---|---|
| MiniMax-H3 checkpoint (`ref2va` shipped, `fl2va` also chains), GGUF quants | [joeygambino/MiniMax-H3-GGUF](https://huggingface.co/joeygambino/MiniMax-H3-GGUF) |
| Text encoder, video VAE, audio VAE | [Comfy-Org/MiniMax-H3](https://huggingface.co/Comfy-Org/MiniMax-H3) |
GGUF sizing guide from the quant repo: `Q8_0` for 32 GB cards, `Q5_1` for
24-32 GB, `Q4_0` for 16 GB. The `curve` variants are pruned-form requants.
The `ref2va` checkpoint is only needed for the reference/bank workflows. It is
**not** required for seamless chaining.
### Nodes added in 2.1
| Node | What it does |
|---|---|
| `RiftPromptSource` | One dropdown over LPFF-style `.txt` briefs and passthrough `.json` scripts. Emits `story_idea` / `character` / `count`. Reads `input/rift_prompts/`, and still reads the older `input/joyecho_prompts/` so existing folders keep working. |
| `RiftScriptPicker` | JSON script dropdown, and the speaker/voice stash `RiftPromptSource` feeds. |
| *(not a node)* `unload_model_after` | A switch **added to the LLM prompt writer** (`JoyEcho_LLMEnhance`). On, the writer frees its own model from Ollama once the script is written, so the video model gets the card. Uses the writer's existing `base_url` and `model_name` β nothing to keep in sync. Added in memory at startup by this pack, so the writer's own package is not modified; the switch simply appears on the node. Off by default. Ollama's OpenAI-compatible endpoint has no `keep_alive` field and its shim never sets one, so without this the model sits for the server default of five minutes β your whole first shot. |
`JoyEcho_PromptSource` and `JoyEcho_ScriptPicker` still resolve as deprecated
aliases, so graphs saved against 2.0 open unchanged. They were never published
under those names β that was the 2.0 bug.
### The prompt writer needs a model you actually have
The full workflow points at a local Ollama with `model_name = qwen3:14b`.
**Pull it before the first queue** or the run stops with
`LLM API error 404: model 'qwen3:14b' not found`:
```
ollama pull qwen3:14b
```
Any OpenAI-compatible endpoint works β its URL in `base_url`, its exact tag in
`model_name` (`ollama list` prints the tags you have). A remote endpoint is
often better: a local writer large enough to be good competes with H3 for the
same card and on under 32 GB will evict the model mid-render. When you do run
local, turn on `unload_model_after` on the writer β it frees the model as soon
as the script is written.
No LLM at all? Set `use_file_prompts` to manual entry, delete the writer, and
feed your own `---`-separated shot script into the sampler's `script` input.
The CORE workflow already works this way.
**Try a render with every switch off and the reserve at `0` before touching
any of this.** The activation reserve measures each shape and conditioning
payload as it renders and sizes the pool itself, and it holds on 24 GB cards
as well as 32 GB. A hand-set reserve *overrides* that measurement. These
switches are for digging out of a spill the console has already named.
## Requirements
Always required:
- **ComfyUI v0.30.0 or newer** (native MiniMax-H3 support).
The **CORE** workflow needs nothing else. It uses only this pack plus ComfyUI
built-ins (`LoadImage`, `LoadAudio`, `SaveVideo`, `SaveAudio`, `CreateVideo`,
`VAELoader`, `PrimitiveFloat`, `Note`).
The **FULL** workflow additionally uses:
| Pack | Needed for |
|---|---|
| ComfyUI_JoyAI_Echo_GGUF_Nodes | the LLM prompt writer β **ships in the release zip**, modified with attribution; use that copy, not upstream |
| [ComfyUI-H3-Motion-Context](https://github.com/NikoDemon80/ComfyUI-H3-Motion-Context) | `continuity=context_pin`, the shipped default |
| [RES4LYF](https://github.com/ClownsharkBatwing/RES4LYF) | the `beta57` scheduler the full workflow ships with |
| ComfyUI-sol-attn + comfyui-minimax-h3-blockcache-T8 | the VRAM/SPEED patch switches |
| [ComfyUI-Custom-Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) | the on-canvas script preview |
ComfyUI validates **every** node class in a graph before it will queue, so a
missing pack stops the whole workflow β even for switches that ship OFF. Each
pack can be removed instead (one-widget change or node deletion, documented in
`INSTALL.md`); e.g. no RES4LYF β `scheduler=beta` (measured cost: lip-sync
8/10 vs 10/10, all else equal), no Motion-Context β
`continuity=first_frame`.
GGUF users also need [ComfyUI-GGUF](https://github.com/city96/ComfyUI-GGUF),
then run `apply_gguf_arch_patch.py` once to teach it the `minimax_h3`
architecture.
## Install
1. Search **H3 Multishot** in ComfyUI-Manager and install it - the pack is on
the Comfy Registry as `comfyui-h3-multishot`. You can also copy
`ComfyUI-H3-Multishot/` into `ComfyUI/custom_nodes/` by hand, or use
Manager's Install via Git URL with
`https://github.com/jlucasmcrell/ComfyUI-H3-Multishot`.
The Registry carries the node pack only; the LLM prompt writer the full
workflow uses ships in this repo under `nodes/`.
2. Place the MiniMax-H3 checkpoint, text encoder and VAEs in the usual
ComfyUI model folders.
3. Restart ComfyUI.
4. Load `workflows/H3_Seamless_Chain_CORE.json` (no extra packs) or
`workflows/H3_Seamless_Chain_v2.json` (full).
5. GGUF only: install ComfyUI-GGUF and run `apply_gguf_arch_patch.py` once.
Full detail is in `INSTALL.md`.
## Usage
1. Open a Seamless Chain workflow.
2. Set the **MASTER CONTROLS** panel (`H3StudioControls`): resolution, frames
per shot, steps. One node drives both the sampler and the prompt writer's
dialogue pacing, so the writer knows how much dialogue fits a shot.
3. Write your script into the sampler: **one prompt per shot, `---` between
shots**. JSON of the form `{"prompts": [...]}` is also accepted. In the FULL
workflow you can instead let the LLM lane write the shots for you.
4. Leave `continuity` on the shipped default and hit Queue.
5. The master video lands in `output/video/H3CHAIN/` with a paired audio file.
`preview_first_shot` is ON by default so you can judge shot 1 before paying for
the whole chain.
### Shipped defaults
| Setting | Default |
|---|---|
| Resolution | 736x1280 portrait (the model distorts faces below ~1 MP) |
| Frames per shot | 192 (8 s at 24 fps) |
| Steps | 14 |
| Sampler / scheduler | `euler` / `beta57` (full; RES4LYF) β CORE ships stock `beta` |
| Continuity | `context_pin` |
| Checkpoint | `ref2va` `curve-Q5_1` GGUF (~14 GB) β `fl2va` also chains, see below |
| Anti-drift | `chain_gain_control=flatten`, `master_normalize=luma+contrast`, `pin_renorm` ON, `memory_frames` 0 |
| Bank | OFF |
| Speed boosters | all OFF. Spectrum/TeaCache/EasyCache are real speed (-14 to -29%) but can distort people; block cache is inert at 14 steps (0 hits measured) |
| `low_ram_master` | OFF β turn ON for long chains or under 32 GB system RAM |
| `preview_first_shot` | ON |
| Mux | 24 fps |
| Output | `output/video/H3CHAIN/` + paired audio |
These defaults target a 16-24 GB card at the best speed/quality mix. Every
booster except block cache changes what people look like β leave them off
unless you have compared for yourself.
## How the chaining works
Two mechanisms ship in the pack. Both produce a single master; they differ in
what crosses the boundary.
### 1. `first_frame` chain - `H3MultishotSampler`
Each shot's **last frame** is handed to the next shot as its **first frame**,
through `fl2va`'s trained continuation task. That is the model doing what it
was trained to do, not a hand-rolled trick. The duplicated boundary frame is
trimmed, and the seam audio gets a 40 ms equal-power weld so the join does not
click.
No third-party dependency. This is what the CORE workflow uses.
### 2. `context_pin` - `H3MultishotMemorySampler`, `continuity=context_pin`
The previous shot's **last 22 frames ride into the next shot as raw latents** -
bit-identical, with no VAE round trip - placed at interior keyframe
coordinates, alongside a timeline-placed audio reference. The model regenerates
that overlap as its own head; the regenerated 0.92 s is trimmed on decode, so
what survives is the original tail followed by continuous new material.
Requires the third-party ComfyUI-H3-Motion-Context pack. As of v2.1.1 the two
packs coexist cleanly β this pack's payload wrapper declares Motion-Context's
compatibility marker, so load order does not matter. `seamless` and
`seamless_tail` are legacy comparison modes: `seamless` is a soft latent-only
pin that often reads as a cut, and `seamless_tail` conflicts with
Motion-Context and stops up front, before sampling, when that pack is
installed.
### Why identity holds with no reference images
Two mechanisms stack:
1. **The frame relay.** Every shot after the first begins from an actual
rendered picture of the character. Faces and wardrobe propagate as *pixels*,
not as a re-imagining from text.
2. **Byte-identical text.** The prompt writer repeats each character's
appearance block **verbatim** in every shot.
The frame pins the specific instance; the repeated text pins the category so
the model cannot drift the description out from under the pixels. This was
verified on a 40-second two-character scene with **zero reference images
supplied**.
## Settings reference
`SETTINGS.md` in the release carries the complete list. The dials that matter
most:
| Dial | Notes |
|---|---|
| `shot_count` | `0` = one shot per prompt in the script. |
| `seed_per_shot` | **Leave ON.** Measured: per-shot seeds hold the face; a single seed shared across all shots drifted both face and voice. |
| `continuity` | `first_frame` or `context_pin` (see above). |
| `chain_gain_control` | Set to `flatten` on chains past roughly 5 shots. Texture ratchets about 1.3x per join otherwise, so late shots come out visibly over-sharpened. |
| `color_level` | `off` / `mvgd` / `scene`. Levels each shot's colour and exposure statistics to shot 1's settled tail (a fixed reference - matching neighbour-to-neighbour re-accumulates drift). Usually unnecessary; for long chains that drift warm or cool. |
| `self_anchor_voice`, `voice_ref` | Voice identity across the chain (the v1.5 headline feature). |
| `output_scale` | Lanczos resize of each shot AFTER decode - resolution, not detail. Works with every continuity mode including `context_pin`; applied per shot and after the bank takes its clip, so conditioning and VRAM are unchanged. Measured 1.78x faster than native for the same output size, and visibly softer. |
| `upscale_model` | Optional `UPSCALE_MODEL` link (ESRGAN and friends via ComfyUI's own loader). Synthesizes detail rather than resizing. Its invented texture never reaches the memory bank, so it cannot feed the sharpening ratchet. |
| `reference_image_size` | `match` or `max`. |
| `preview_first_shot` | Renders shot 1 alone first so you can abort early. |
### MASTER CONTROLS (`H3StudioControls`)
One node sets resolution, frames per shot and steps for **both** the sampler
and the prompt writer, so the writer's dialogue budget always matches the shot
length actually being rendered.
### VRAM/SPEED panel (`H3StudioSwitches` + reserve control)
Three lazily gated switches: memory-efficient attention, chunked feed-forward,
and the remote text encoder (speed boosters have their own node). The gates
are lazy, so a switch left OFF never executes its path - and all OFF is
exactly the verified recipe. The panel keeps its 2.5.x eight output slots so
saved graphs keep working; the removed ones always emit False.
The activation-reserve heuristic decides how much VRAM to hold back. Its cache
keys include a conditioning-**payload** signature (keyframes / audio refs /
two-pass), so a bare shot 1 and a reference-laden shot 2 are measured
separately instead of sharing one wrong number. Measured pools are no longer
overridden by a fixed floor, the first run of a new payload variant estimates
from a measured sibling, and a VRAM spill into system RAM is now detected and
named in the console - previously it only showed up as an unexplained ~5x
slowdown.
## Prompt and boundary rules
These are render-verified. The FULL workflow's prompt writer applies them
automatically through its `join_style` control, which appends them to the
system prompt. **Hand-written scripts must follow them manually.**
- **AIRLOCK.** Every shot after the first opens holding the previous shot's
exact closing arrangement, and gives about **2 quiet seconds** before anyone
speaks - a breath, a weight shift, real micro-motion, not a freeze.
- **The first ~1 second of every chained shot is discarded replay.** Dialogue
starting at frame 0 loses its opening syllables.
- **LAND SETTLED.** End each shot back in a stable arrangement, dialogue
finished, about 2 seconds spare.
- **A spoken line never straddles two shots.** Budget: dialogue plus 4 seconds
of quiet must fit inside the shot. 243 frames fits one long line; 124 does
not.
- **Repeat verbatim.** Each character's appearance description *and* the
room/lighting description, word for word, in every shot.
- **Keep fps at 24.** Other rates audibly shift voice accents.
`PROMPTING.md` has worked examples.
## New in v2.6.0 - the extend take
### One prompt, one continuous speech, as long as you want
Type a length into MASTER CONTROLS `take_seconds`. The panel picks the
largest window whose activation pool fits with most of the weights resident
on your card (`window = auto`; wire the loader's MODEL into the panel for a
real weight size) and the count that fills the time; the writer's new
**extend take** join style writes ONE speech and cuts it across the windows
only at sentence boundaries; the memory sampler chains them under
`context_pin`. H3 continues the speech across every join in its own voice.
Verified: seven writer-driven renders at 141/192/243-frame windows plus a
65 s / 7-window take β every join continued, zero repeats, zero clipped
words. `take_seconds = 0` is the old behaviour exactly.
Also new: `audio_pin_frames` on the memory sampler (audio reference window
independent of the picture pin; 96 = the JoyEcho 4 s audio memory; neutral
at n=1, ships 0), the standalone `H3ExtendTake` node, the `H3_Extend_Take`
workflow.
### Fixed: reserve planning (from the 24 GB test lab)
The bare-to-payload x1.6 no longer fires between the two samplers'
differently-named payload signatures; a first run at a new shape borrows
measurements only from the same quant family (GGUF figures do not transfer
to w4a8/int8 β a cross-family borrow under-reserved a first run into driver
paging); the streamed master's metadata is the API graph again.
### Fixed: silent shots echoing the voice anchor
A silent shot whose framing boilerplate said "visible lip movement clearly
readable", with shot 1's voice riding along as `<Audio 1>`, re-spoke shot
1's line word for word. The few-shot example no longer mentions lip
movement, the rule cites the render, and the writer warns by shot number.
## New in v2.5.5 - the memory release
### New: the driver-headroom rule (the random-slowdown fix)
The single biggest fix this pack has shipped. High-resolution renders would
randomly take anywhere from 27 minutes to 3 hours for identical work β same
seed, same settings. The cause: when model weights plus working memory fill
the card past roughly 95%, the Windows driver starts demoting GPU memory
unpredictably, and whether your render crawled was luck.
The auto-reserve now detects that zone before sampling and deliberately
streams a few GB of weights instead β streamed weights are nearly free on
modern ComfyUI, the last few percent of VRAM are not. The lottery render
became 15 minutes, every time, verified on both the clean-shot and
payload-shot branches. Automatic; the console prints a "driver headroom"
line when it engages.
### New: `low_ram_master` streams the master to disk
Off (the default), every finished shot is held in system RAM until the final
join β historically the point where long renders killed whole machines. On,
each shot streams to lossless disk staging as it finishes and the master is
assembled from disk through the exact same levelling math: verified at
42.8 dB against the in-RAM path, which is codec noise. Peak RAM stays near
two shots regardless of chain length. The finished file's path now comes out
of a new `master_path` output either way.
### New: remote text encoder
The text encoder works for a few seconds per shot and occupies 15+ GB for
the whole render. Install this pack on any second PC with ComfyUI, point the
new **H3 Remote Text Encoder** node at it, turn `remote_encoder` ON in the
VRAM / SPEED SWITCHES panel, and prompts are encoded over there while your render card keeps the memory. Results are
identical β verified across two different machines to float precision β and
repeated scene text is answered from a local cache with no network call at
all. Ships wired into the full
workflow; the panel flag defaults OFF, so single-PC setups see zero change.
### New: `H3 TAE Decode` draft previews
A 9 MB tiny decoder (Kijai's taeh3, downloaded separately into
`models/vae_approx/`) turns latents into full-resolution draft frames in
about 2 seconds, versus roughly a minute per shot through the real VAE.
Drafts smear fine texture but composition, framing and motion read clearly.
For seed hunts and batch triage β never finals.
### New: the Speed Boosters panel
Spectrum, TeaCache, block cache and ComfyUI's own EasyCache, each behind a
switch on one node, each measured on the same seed and eye-tested on the
same masters:
| Booster | Time saved | Eye verdict |
|---|---|---|
| block cache | none at 14 steps | 0 cache hits on every run, both cards β inert; ships OFF (only fires at 30+ steps) |
| Spectrum | -29% | visible distortion on people; rooms fine |
| TeaCache 0.15-0.30 | -14 to -21% | visible distortion on people; rooms fine |
| EasyCache (built-in) | -21% | same family of people distortion |
| Spectrum + TeaCache stacked | no faster than Spectrum alone | severely damaged β never stack |
A booster whose pack is missing prints an install link and passes the model
through unchanged instead of breaking the graph.
### Changed: defaults tuned for 16-24 GB cards
736x1280 at 192 frames and 14 steps, `euler`/`beta57`, `curve-Q5_1`
checkpoint, the full anti-drift set on (`context_pin`,
`chain_gain_control=flatten`, `master_normalize=luma+contrast`,
`pin_renorm`, `memory_frames=0`). The resolution floor is deliberate: the
base model distorts faces below roughly 1 MP.
### Changed: the writer knows how long a shot really is
The prompt writer now receives each shot's actual speakable span β clip
length minus the replay, airlock and settle a chained shot spends β and
sizes dialogue against it. Overruns and silent scripts are flagged at
generation time with the numbers. Measured on renders: lines over budget
garble, lines under budget drag, matched budgets reviewed as natural and
fully intelligible.
### Changed: writer craft rules from failed renders
Silent shots with visible people now state what mouths are doing β stops the
model inventing mumbling. Revealed objects are written as already present β
stops mid-shot pop-ins. Shots that open a mouth without dialogue must name
the sound it makes.
### New: clickable title blocks in every workflow
Each workflow carries its version number and live links to the GitHub repo,
this page and the Civitai page, plus a plain-language pass over every
on-canvas note.
## New in v2.2.5
### Fixed: chained shots could stall before the first sampling step
The auto-reserve learns how much activation pool a given shape needs by
measuring it. On a chained render the later shots often load *partially*
- the weights are already resident, so only part of them streams in. The
old code discarded every partial measurement as untrustworthy, which meant a
chained shot could never contribute what it learned. If the very first shot was
also partial, the cache stayed empty forever, the fallback reserve was too small
for the shape, and every subsequent attempt failed the same way. A render could
sit there refusing to start with no error to point at.
Partial measurements are now kept when the pool is genuinely large, and
clamped by a named floor (`_auto_cache_floor`) that only ever ratchets
upward, never down. Verified live on a six-shot chain: offload dropped from
4308Β MB to 638Β MB once the cache started learning.
The warning that fires when a shot asks for far more pool than is available
now distinguishes the two cases it was conflating. If this shape has been
measured before, it says so and gives you the number. If it is the first run at
this shape, it says *that* instead of implying something is broken.
### Fixed: `output_scale` was documented backwards
With an upscale model selected, `output_scale` is not a multiplier
on top of it - the model applies its own fixed factor (4x for the
ESRGAN family) and `output_scale` is the **final** size
you want, not an extra step. The tooltip said otherwise, in both samplers. Both
are corrected, and `upscale_model_name` now explains the interaction
rather than leaving you to discover it.
### New: the upscale says what it will cost before it runs
Decoded frames accumulate in host RAM until the master is joined, so the bill
is per-shot multiplied by shot count. A 1344x768 chain through a 4x model is
5376x3072 per frame, and an `output_scale` left at a value that
looked harmless has taken machines down at the join - after all the
sampling was already paid for.
Before sampling starts you now get the projected dimensions, MB per frame, GB
per shot and GB total, measured against actual free RAM:
```
[H3Memory] upscale will produce 5376x3072 (4.0x): 198 MB per frame
[H3Memory] WARNING: that will not fit. Frames are held in host RAM
until the master is joined - lower output_scale to 1.60
or lower, or turn the upscaler off.
```
### New: the folder walk prints which index is which file
Queueing a folder told you nothing about the mapping between
`EPISODE INDEX` and the file it would pick, so a cancelled run left
you doing off-by-one arithmetic against a single log line to work out where to
restart. The ordered list now prints once per folder per session, and each job
reports its own position and the index to set to resume there. It re-prints if
the folder's file count changes, which is the one case where a remembered
listing would mislead.
### Fixed: garbled speech - the writer was never told how long a shot is
`num_frames` on the LLM Enhance node defaults to `0`,
and it shipped as `0` in the bundled workflow. At zero, the writer is
told **nothing** about clip length, so it sizes every spoken line
for the "~10 second clip" assumed in the system prompt regardless of what you
actually render. Too long for a short shot is crammed, garbled speech; too short
for a long one is dead air.
Three things now close that:
- `num_frames = 0` prints a warning naming the widget and telling
you to match it to the sampler's `frames_per_shot`. It used to pass
in complete silence.
- The bundled workflow ships with `num_frames` set to
**243**, the same value its `frames_per_shot` already
uses, so a fresh install is correct out of the box.
- Shots whose dialogue overruns the budget are reported at generation time
with the numbers, instead of being discovered forty minutes later by ear. The
check is symmetric: a script that comes back with **no dialogue at
all** is also flagged, because that renders as a silent slideshow.
### Fixed: `revise` asked for lines that could not fit
Chained styles spend about five seconds of every block on the replay, airlock
and settle, so the speakable span is shorter than the clip. The main writer
subtracted that. `revise` did not - it sized lines against the
raw clip length, from its own separate copy of the arithmetic.
On a 243-frame chained shot that meant `revise` asked for
**12-20 words where only 6-10 fit**: double the budget,
every line, which is exactly the overrun that renders as garble. It also never
checked its own output, because it returns long before the check the main path
runs.
Both paths now call one `_speakable_budget()` and both check their
result, so they cannot drift apart again. The duplicate arithmetic is why they
had.
### Changed: the story writer stops prescribing creativity
The system prompts had accumulated a five-beat dramatic arc, a banned-cliche
list, a "land near seven shots" target and a dialogue quota. None of those are
properties of the model - they are one person's taste, hardcoded, and they
were flattening every brief toward the same shape.
They are gone. What remains is what the renderer actually needs: valid JSON,
identity restated verbatim per shot so the room does not drift, literal physical
description over mood language because abstract adjectives have nothing to
render, and the note that audio is half the model. The story, its length, its
tone and whether any given shot speaks are the model's call now.
### Changed: the node menu no longer says "Rift"
Four nodes sat in a category called `Rift`, which means nothing to
anyone who is not me. They are in `H3/script` now, the labels drop the
prefix, and ten console messages say `[H3 Multishot]` - the name
you actually installed - instead of `[Rift]`.
> **Saved workflows are unaffected.** Categories are
never written into a graph and display names resolve fresh on load. The
underlying class names are deliberately *unchanged*, because those strings
*are* written into every saved `.json` and renaming them would
turn your nodes into red missing-node boxes.
### New: installable from the ComfyUI Registry
The pack is published as `comfyui-h3-multishot`, so ComfyUI-Manager
can find and update it without a manual clone. The zip on this page stays the
complete bundle - both node packs, the workflows and the docs - and
remains the right choice if you want everything in one drop.
The dependency list is deliberately empty. Every import this pack uses
(`torch`, `torchaudio`, `psutil`,
`av`, `numpy`, `PIL`) is already guaranteed by
ComfyUI itself, and a custom node that declares `torch` can talk pip
into replacing a working CUDA build with a CPU wheel.
### Documentation: drift on long chains, and the dials that fight it
`SETTINGS.md` gains a measured section on why chained shots accrete
detail - each conditions on the previous shot's own output, so invented
texture compounds - and which controls actually counter it. Measured over
ten shots at 960x544:
- `memory_frames = 0` is the big one. The bank's RECENT slots feed
accreted output forward on top of the pin.
- `master_normalize = luma+contrast` levels brightness
**and** contrast of the finished chain.
- `pin_renorm = on` holds each pinned latent at shot 1's sigma.
- `chain_gain_control = flatten` on chains longer than about five
shots - texture ratchets roughly 1.3x per join.
Residual with all of those on is about **1.02 per hop**. Not
zero. Long chains still drift, slowly. `pin_noise` is small,
scene-dependent and gets worse above `0.10`; it is not the fix it was
described as in 2.1.5.
## New in v2.2.4
Four things people asked for or tripped over, plus the documentation that
should have prevented two of the questions.
### Fixed: reference images of different people blended into one face
If you gave the sampler reference pictures of two characters, it rendered
something that resembled neither. That was not you holding it wrong. Internally
every reference picture was declared to the model as
`<Picture N> is a reference photograph of <Subject 1>` -
*the same* Subject 1, every time. With one character that is correct and
it is why single-character references work well. With several you were telling
the model that photographs of different people all showed one individual, and
it produced the average.
There is a new `reference_subjects` field on the sampler. Leave it
empty and nothing changes - every picture is one person, exactly as before.
Fill in comma counts in picture order to group them:
```
reference_subjects: 3,3 pictures 1-3 are person A, 4-6 are person B
reference_subjects: 2,2,2 three people, two pictures each
```
Each group is then declared its own subject, with an explicit instruction that
it is never blended with the others. Only Subject 1 is described as speaking,
because H3's voice conditioning is single-speaker.
### New: point the prompt source at a folder and queue the whole thing
`start_index` has always selected a block *inside* one file.
That works for LPFF-style prompt batches, where one file holds many prompts. It
does not work for H3 scripts, because in an H3 script `---` separates
**shots within one scene** - concatenating scenes into one file
would fuse them into a single enormous take.
Turn on the new `walk_folder` switch and `start_index`
selects **which file** instead, walking the chosen folder in sorted
order and emitting the whole script. Wire it to a Primitive set to
`increment`, queue thirty times, and thirty finished scenes render
unattended.
### New: hand H3 an existing clip as a video reference
There is now a `V2V REFERENCE` lane in the workflow - Load
Video into Get Video Components into the new **H3 Reference Video**
node, then into two new sampler inputs. It ships muted; un-mute the three nodes
to use it.
**Read what this is before you wire it.** H3 is told a video
reference is "a clip from an earlier moment of this same continuous
scene" and asked to keep its framing, camera distance, room contents and
colour temperature. It is **scene and appearance conditioning**. It
is **not motion control** - there is no pose, depth or optical
flow path in H3, so your subject will not copy the movement in the clip. It
borrows the place and the look, not the action.
Keep the window short. References are subsampled to 2 fps and then ride
through *every* sampling step, so a 25-second clip is roughly 50 reference
frames of permanent per-step cost. The H3 Reference Video node trims to a window
and prints what that window will cost before you commit to it. Requires ref2va;
fl2va has no reference rows and ignores video references entirely.
### Fixed: an empty prompt dropdown that blamed the wrong thing
The prompt source finds `.txt` files through the
`inspire_prompts` folder path, which is registered by
**comfyui-inspire-pack**. Without that pack installed the path does
not exist, so the dropdown came back empty however many prompt files you had,
and the error blamed the prompts folder. It now tells you which of the three
fixes applies: install the pack, register the path yourself in
`extra_model_paths.yaml`, or set `manual_path` and ignore
the dropdown. The dependency is written into INSTALL.md as well.
### Documentation: what has to change between shots
Several people have hit chains where shot 3 comes back as a near-copy of shot
2. The prompting guide is partly responsible: rule 5 says to repeat descriptions
word-for-word, and never said what must *differ*. Rule 5 is only about
appearance and the room. It is not an instruction to restate the action, and
when it gets read that way most of shot 3 is byte-identical to shot 2 - and
the model is separately instructed to preserve the subject, the room and the
colour temperature, so "keep everything the same" wins.
There is now a sixth rule covering it. The short version: each shot's action
must leave the world in a state the previous shot's world was not in, physical
and irreversible, not a mood or a camera move. If you can swap two shots' action
lines and the script still reads correctly, the model cannot tell them apart
either.
### Documentation: fl2va is not a smaller file
Both this guide and INSTALL.md described fl2va as "lighter and
faster", which everyone reasonably read as a size claim while choosing a
file for a 24Β GB card. It is not. **fl2va and ref2va are exactly the
same size at every quant level.** "Lighter" only ever meant
fewer tokens per sampling step, because there are no reference rows riding
along.
What actually separates them was never written down: **fl2va lands on a
supplied frame and ref2va only nudges toward one.** Measured against the
same frame, 26.35Β dB on fl2va versus 16.15Β dB on ref2va with a keyframe
at 6 turbo steps - and 16.81Β dB at 20 stock steps, which rules out the
sampler and leaves the checkpoint. fl2va can also take a first *and* a
last frame and plan a camera move between them, which ref2va cannot do at all.
So: ref2va when identity or voice must persist, fl2va when a shot must start
exactly where the last one ended.
### Improved: the story writer, for thin briefs and long shot counts
Give the writer a couple of characters and a genre with no plot, ask for
fifteen shots, and it tended to return atmospheric moments rather than a story.
That was the system prompt's fault in four specific ways, all now fixed.
It was **calibrated to about seven shots and argued against more**
- "most scenes land between 4 and 10", "go higher only when
the story clearly has that many real beats", plus a padding test. Asked for
fifteen, the model was simultaneously told to produce exactly that count and that
this many was probably padding, and it resolved the contradiction by rationing one
thin premise across the shots. It is now told the opposite: **a high count
is an instruction to invent more story** - another location, a second
complication, a character who arrives partway through, a reversal that changes
what the earlier shots meant. The no-padding rule is unchanged and is exactly the
point; the way to satisfy a high count is to invent enough real plot that no shot
is padding.
When the brief names **only characters and a genre**, the writer is
now told plainly that it is the author: decide who wants what, the incident that
starts it, the complication, the turn and the ending *before* writing any
shot. Returning a series of moods involving the named characters is called out as
the most common failure on a thin brief.
**Dialogue density** now has a floor. Speech was marked optional
per shot and non-speaking shots were actively encouraged, with nothing on the
other side, so long pieces came back nearly silent. It now aims for two thirds or
more of shots to carry speech, with silent shots as punctuation.
And a new **FIT THE SHOT** section covering *both* dialogue
and action. Overrunning the clip is what produces crammed, garbled speech and
distorted motion, so the writer now budgets each shot - settle, action, line
at an unhurried pace, a beat to land on - and cuts the action rather than
speeding up the speech. One clear physical action per shot; if more than one is
written, split the shot.
### Note on saved workflows
Every new control in this release is appended at the *end* of the
node's input list. Widget values are stored positionally, so inserting a control
anywhere else silently shifts every value after it in workflows you have already
saved. Your existing graphs load unchanged.
## New in v2.2.3
Everything here is a fix. Nothing changed about how you use the workflow.
### Fixed: long multi-shot renders died at the very last step
A long job would sample every shot, upscale every shot, finish master
normalize - and then die while assembling the result, with nothing
written. Every expensive stage had already succeeded.
```
RuntimeError: DefaultCPUAllocator: not enough memory:
you tried to allocate 33791016960 bytes.
```
That is **host RAM, not VRAM**, and it is one contiguous request.
Upscaled frames were held on the host as fp32 for the whole run, and the final
assembly then allocated a *second* complete timeline while the first was
still alive. Peak was twice the timeline, and the timeline grows as
`shots Γ frames Γ height Γ width Γ 3 Γ 4 bytes`
with the upscale factor squared. Six shots of 243 frames upscaled is a 36.5Β GB
timeline and a ~70Β GB peak, which no 64Β GB machine can satisfy.
Three changes: the timeline is now assembled into one preallocated buffer,
releasing each shot as it is copied (bit-exact - the same bytes as before);
frames are parked as fp16, which is well clear of the 8-bit the encoder writes
anyway; and master normalize upcasts to fp32 for its arithmetic and writes back,
so the memory saving costs no precision.
**Effect:** six shots of 243 frames upscaled drops from ~70Β GB
peak to ~21Β GB. Twelve shots of 192 frames drops from ~58Β GB to ~31Β GB.
Both now fit in 64Β GB with the per-shot upscale unchanged. Diagnosed by a
second operator on a 3090 box; thank you.
### Fixed: the auto-reserve inflated its own estimate until the render spilled
If your chains got slower as they went - shot 1 fine, shot 3 sluggish,
shot 4 crawling - this is why. After each shot the pack measures how much
activation memory that shape needed. It subtracted the weight bytes
*currently resident* rather than the model's full size, so whenever the
model was only partly loaded the measurement came out too large by exactly the
amount that had been offloaded. That number then raised the next shot's reserve,
which left less room for the weights, which offloaded more.
```
shot 1 recorded 6.4 GB true 6.4 (full load - correct)
shot 2 recorded 8.0 GB true 5.3 (2749 MB offloaded)
shot 3 recorded 8.9 GB true 4.3 (4784 MB offloaded)
shot 4 asked for 19.9 GB -> clamped -> 262 s/step
```
The overstatement equals the offload every time. The measurement now uses the
model's full size, and a shot that offloaded weights records nothing at all
- it measured a spill, not an activation pool.
The cache also only ever grew, so a single bad shot poisoned a shape
permanently. Entries written by earlier versions are **dropped once**
on first load; you will see a line saying so, and those shapes re-measure on
their next run.
This does not make an over-committed render fit. If a shot
honestly needs more memory than the card has you still have to lower
`frames_per_shot` or resolution - the difference is that the
warning now fires on true numbers instead of the reserve quietly climbing.
### Fixed: the sol_attn and chunk_ffn switches did nothing
Reported by **sdktertiaire2**. Those switches shipped ON, but the
nodes they gate ship *bypassed* - they need third-party packs that
cannot be bundled (`ComfyUI-sol-attn`,
`comfyui-minimax-h3-blockcache-T8`). A toggle routing into a disabled
node changes nothing and warns about nothing. That contradictory default was
mine.
Both now ship OFF so the panel states what the canvas actually does, and the
panel is labelled with the fix: **install the pack, then select the node
and press Ctrl+B to un-bypass it.** The switch only routes; it cannot
enable a bypassed node. If you do not want those packs, leave the switches off
and lose nothing - they are speed and memory optimisations, not quality
features. Everything renders identically without them, just slower.
The nodes stay bypassed on purpose. Enabling them by default would hard-fail
every install that lacks the optimizer packs.
## New in v2.2.2
One fix, reported by a user against 2.2.1. If you render with reference
images, take this one.
### Fixed: reference renders forced glasses onto the subject
Reported on Civitai: a reference image produced a character wearing thick
black frames in every shot, and prompting to remove them changed nothing.
The pack injects a `retention_analysis` block alongside reference
images so identity and voice stop drifting across a chain. That block was
hardcoded to say the subject *"retains the same face, skin, hair,
**glasses** and wardrobe"* - so every ref2va render was told to
keep glasses whatever the prompt said. And it could not be argued with, because
the sampler runs on a `BasicGuider`: **cfg 1.0, no negative
branch**. There is nothing for a negative statement to subtract from, so
"remove the glasses" only put the word into the conditioning a second time.
The block now says "the same face, skin and hair" and names no accessories.
Eyewear, hats and jewellery belong to your prompt.
**The rule this exposes, and it applies to every prompt you
write for this graph:** anything hardcoded into unconditional conditioning
is permanent from your side. At cfg 1.0 you can add but never subtract, so
phrase everything positively - "clear unobstructed eyes", never "no glasses".
Negations do not work anywhere in this pack.
## New in v2.2.1
A bug-fix release. Four defects, three reported from real renders on real
machines rather than found in review. If you have been unable to finish a long
chain, or a high-resolution one died partway with the whole server going down
with it, this is the release for you.
### Fixed: two crashes that only appear on long or high-resolution chains
Both were `DefaultCPUAllocator: not enough memory` - system
RAM, not VRAM - and both killed the job *after* it had already done
the sampling.
- **`master_normalize` built the entire finished timeline four
times over.** A 12-shot 1088x1920 chain asked for a single
**31.2 GB** block and died on a 24 GB machine after 81 minutes of
work. One of those four copies was pure waste: the code rebuilt a tensor
byte-for-byte identical to one already in memory, purely to compute two numbers
for a log line, then threw it away. Every statistic it needs is
one-dimensional. It now measures per shot and releases each shot as it is
consumed, so peak memory is one finished timeline plus one shot instead of four
timelines. Output is **bit-identical** - verified against the
old implementation across every colour mode and median width, on the pixels and
on the log strings.
- **The upscaler was handed every frame at once.** 243 frames at
1472x2560 is an **11.0 GB** float32 allocation on top of the input.
It now runs in chunks into a preallocated buffer, so peak is one chunk rather
than the whole batch. Bit-identical, verified at two chunk sizes. Set
`H3_UPSCALE_CHUNK` if you want to trade memory for a little speed;
default 16.
### Fixed: auto-reserve could clamp itself into a hard crash
On a 12-shot 1280x736 run, shot 1 loaded completely and rendered fine. Shot 2
carries the context pin **and** the reference rows, so its
activation-pool requirement roughly doubles - the node correctly costed it
at 18.2 GB. It then clamped the pool to 9.4 GB *"so the weights still load
completely"*, and the weights loaded **partially anyway**,
401 MB offloaded. Neither constraint was met and the render aborted inside a CUDA
kernel, taking the whole ComfyUI process with it.
Two causes, both fixed. The keepout was **384 MB**, which did not
cover ComfyUI's own buffer plus the difference between what the driver reports
free and what ComfyUI treats as usable - about 400 MB short. It is now
**1 GB**. And the "this is tight" warning compared the clamped
reserve against a *previous shot's* measurement rather than what this shot
had just asked for, so a 9.4 GB clamp looked fine against shot 1's 9.1 GB while
the payload needed 18.2. It now compares against the real request and says
plainly that this usually dies inside a kernel, and that raising the reserve
cannot help because the memory is not there.
**Worth knowing regardless of this fix: shot 1 succeeding
tells you nothing about shot 2.** Shot 1 has no pin and no reference rows.
If shot 2 will not fit, lower `frames_per_shot` or the resolution, or
load a smaller quantisation of the DiT.
### Fixed: `start_image` on the memory sampler silently did nothing
Reported by a user. `start_image` on
`H3MultishotMemorySampler` is an identity **reference row**,
not a first frame - and on an fl2va checkpoint, which has no reference rows,
it is built and then ignored entirely. Meanwhile the sibling node
`H3MultishotSampler` has an input with the **same name**
that really is an I2V first frame, which is where the perfectly reasonable
expectation comes from. The combination produced no warning at all.
It now prints one, naming both ways to actually open shot 1 on a picture: use
`H3MultishotSampler`, whose `start_image` is a true first
frame, or set `continuity=flf_chain` here and feed
`keyframe_images` (N+1 stills for N shots). Note that
`continuity=first_frame` is not about your image either - it
hands over the *previous shot's* last frame, and does nothing on shot 1.
### Verified
Three chained shots rendered end to end on ComfyUI 0.32.0 with the new
auto-reserve: every shot `full load: True`, which is exactly where the
crashing run went partial, and the new warning fired on shot 2 with real numbers
and did not stop the render. `master_normalize` ran on that same
685-frame chain. The chunked upscaler is proven bit-identical by test rather than
by render - that graph had upscaling off, so I will not claim end-to-end
coverage it did not get.
## New in v2.2.0
Everything since **2.1.2**, which is where most people still are. Seven point
releases in one: five separate defects that stopped the workflow running, a
measured campaign against chain drift that changed the shipped defaults, and a
whole ComfyUI version this pack could not previously run on.
*(2.1.9 on GitHub and HuggingFace is this same content under a smaller number,
tagged an hour earlier.)*
**Run-blocking, all user-reported or found by finally testing what we ship:**
| | |
|---|---|
| the Audio Spine produced static on ComfyUI 0.32.0 | fixed in 2.1.7 |
| naming an mmproj file broke GGUF text encoders | fixed in 2.1.7 |
| `The value 1 for reference_image_size is not available` | fixed in 2.1.6 |
| the full workflow needed a node from a pack not in the zip | fixed in 2.1.8 |
| **`H3_Seamless_Chain_CORE` could not be queued at all** | fixed in 2.1.9 |
**New controls:** `pin_frames`, `pin_noise`, `pin_renorm`, and
`master_normalize`'s `luma+contrast` mode.
**Changed defaults that change your output:** `memory_frames` 2 β **0**, and
`join_anchor_noise` / `handoff_release` to 0 because they are inert under
`context_pin`.
**New runtime:** ComfyUI **0.32.0**, which needed real work β its
`ModelSamplingAV` carries the audio half of the latent on a different scale.
0.30.0 is unchanged and still supported.
Every section below is the original release note for each of those versions,
newest first.
---
## New in v2.1.9
### Fixed: `H3_Seamless_Chain_CORE` could not be queued at all
```
value_smaller_than_min: Value 0.0 smaller than min of 1.0 - output_scale
```
CORE's sampler still carried the widget array it was saved with **before 2.1.3
removed the four `two_pass_upscale` dials**. Nineteen saved values against the
class's fourteen live widgets, so the frontend put `false` into `output_scale`
(minimum 1.0) and `1.5` into `save_every_shot`, and the server rejected the whole
prompt. The workflow advertised as the one with no third-party dependencies β the
safest thing for a new user to open β has been un-runnable since 2.1.3.
It was never caught because CORE had **never been rendered end to end**. Our own
release checklist said "one CORE render before posting" and that step had been
carried, unticked, through six releases.
The array is now generated from a nameβvalue map resolved against the server's
schema, and the map is stored on the node so the pack's JS can re-apply it by
name β the same treatment the full workflow's sampler got in 2.1.6. CORE has now
rendered: three chained shots at 960x544, 370 frames, picture and audio, holding
framing, wardrobe and lighting across both joins.
All three bundled workflows are now submit-tested against a live server as part
of the release routine, not read.
### Bypassed nodes now say what they need
Four nodes ship bypassed because their packs are not in the zip, and a missing
class fails the entire prompt. That is the right default β but a bypassed node
sitting on the canvas invites you to un-bypass it, and doing that without the
pack installed breaks the workflow with no explanation. Joe's point, and he is
right.
Now it says so in four places, in order of how hard they are to miss:
| | |
|---|---|
| the group title | `VRAM PATCHES - bypassed: install the pack named in each title BEFORE Ctrl+B` |
| each node title | `attention patch (bypassed - needs ComfyUI-sol-attn)`, and so on |
| a note above the cluster | install first, then `Ctrl+B`; without it the whole workflow stops queueing |
| the VRAM / SPEED note | the full table with repository URLs, and the two-step rule |
The two-step rule is the part people lose: un-bypassing a patch node changes
nothing on its own, and flipping its switch while the node is still bypassed
changes nothing either. Both, in that order, after installing the pack.
`SCRIPT PREVIEW` is the odd one out β it is a leaf, so leaving it bypassed costs
you the on-canvas preview and nothing else.
**Why not just bundle the packs?** The writer pack is bundled because it is
modified and pinned. These three are not: shipping a second copy of
Custom-Scripts, sol-attn or blockcache-T8 inside the zip would shadow whatever
the user already has installed and freeze it at the version we happened to
vendor. Naming them and linking them is the honest version.
---
## New in v2.1.8
### Fixed: one node in the workflow came from a pack that is not in the zip
`SCRIPT PREVIEW` is `ShowText` from **ComfyUI-Custom-Scripts**, and it shipped
*active*. A node whose class is missing serialises as `class_type: null` and the
server rejects the **whole prompt**, so without that pack installed the workflow
could not be queued β for the sake of an on-canvas text box. INSTALL.md said to
delete the node, which only helps someone who reads it before pressing Queue.
It now ships **bypassed**, the same remedy the three accelerator nodes got in
2.1.4. A bypassed node is dropped from the prompt entirely. Install
Custom-Scripts and `Ctrl+B` the node if you want the preview; the writer feeds
the sampler either way.
Found by mapping every node type in all three bundled workflows to its owning
Python module against a running server. That check is now part of the release
routine instead of something I do after a user tells me. The other two workflows
were already clean, and everything else in the full one is either core ComfyUI,
this pack, the writer pack **that is in the zip**, or one of the three bypassed
accelerators.
### Verified end-to-end on ComfyUI 0.32.0
Not by reading the graph β by loading the shipped file in a browser on a 0.32.0
install and pressing Queue. Twice: once through the prompt-writer lane exactly
as shipped, and once with a hand-written script.
- loads with no missing node types, and every sampler widget lands on its own
name (`memory_frames` 0, `master_normalize` luma+contrast, `pin_renorm` on) β
the shift that caused `The value 1 for reference_image_size is not available`
cannot reproduce
- no null `class_type`: the bypassed preview and the three bypassed
accelerators are all dropped cleanly
- three chained shots at 960x544, 124 frames each β **328 frames**, exactly 372
minus the two 22-frame `context_pin` head trims
- a reviewer given the clip cold, with no idea how it was made, read it as
**one continuous static take**, found no cut or jump anywhere, transcribed all
three lines of dialogue, reported the lip-sync as matching, and found no shift
in framing, colour, brightness or wardrobe and no hiss, dropout or click in
the audio
0.30.0 remains the version everything else here was measured on; both are now
tested before release.
---
## New in v2.1.7
Three things that stopped the workflow running. All user-reported, all
reproduced, all fixed and verified by render rather than by reading the code.
**Everything below was verified on ComfyUI 0.32.0**, not just 0.30.0. Two of
these three only ever appear on 0.32, which is why they survived several
releases.
### Fixed: the Audio Spine produced static on ComfyUI 0.32.0
`guide_audio` came out as hiss while the same file through `voice_ref` or the
native node was perfect. Reported against 2.1.2, still present through 2.1.6.
ComfyUI 0.32.0 introduced `ModelSamplingAV`, which carries the **audio half of
the packed AV latent scaled onto the video schedule** β `process_latent_in`
multiplies it by `shift / audio_shift` (12/3 = **4** for H3) and
`process_latent_out` divides it back. Everything this pack injects into the
sampler's latent is in the stream's *native* domain, so on 0.32 it landed 4x
too small and decoded as broadband noise. On 0.30.0 there is no such scaling,
so the same code was correct β which is why it never reproduced here until a
0.32 rig existed.
Three injection sites had it, not one:
| source | used by |
|---|---|
| the encoded spine | Audio Spine |
| the previous shot's audio tail | `audio_lock`, latent handoff |
| the encoded room tone | onset guard |
All three now scale at the point of use, reading the factor off the live
`model_sampling` object so a changed sigma shift stays correct. `getattr`'s 1.0
default leaves 0.30.0 byte-identical.
Verified on 0.32.0 with a real 44.1 kHz stereo voice track: loudness-envelope
correlation against the guide **+0.965**, speech-band energy 34.2% against the
guide's 39.3%, and a blind listener transcribing the guide's words with "clean,
no background hiss". Before the fix the same render was hiss.
### Fixed: naming an mmproj file broke GGUF encoders
mat1 and mat2 shapes cannot be multiplied (3680x1152 and 3456x1152)
thrown at the handoff into shot 2, GGUF encoders only, unaffected by resolution
or by turning every image input off.
Setting `mmproj_name` explicitly took a different code path than `(auto)` and
skipped the vision key-renaming step entirely β so the vision tower loaded
under raw llama.cpp names (`v.blk.*`, `mm.*`) that nothing downstream reads,
the merger was never populated, and the first matmul touching vision features
had the wrong width. Same file, two loaders, **19 key names in common out of
351**.
The bitter part: `mmproj_name` is documented as the reliable escape hatch for
when filename pairing fails, and it was the broken path.
It now runs the same post-processing as `(auto)`, using ComfyUI-GGUF's own key
map rather than a copy, so it follows their changes. Verified: an explicitly
named file now yields a state dict identical to `(auto)` β 351 tensors, every
shape matching. A new guard also fails by name if a chosen mmproj produces no
`visual.*` tensors, instead of dying in a matmul twenty minutes later.
### Fixed: `The value 1 for reference_image_size is not available`
The shipped workflow's saved widget values were written for a layout that did
not present `sampler_override` and `scheduler_override` as widgets. The current
schema does, so everything from index 27 read two slots early and
`output_scale`'s `1.0` landed in `reference_image_size`, a combo of
`match`/`max`. This affected **2.1.3, 2.1.4 and 2.1.5**.
The array is now generated from a nameβvalue map resolved against
`/object_info`, and the same map is stored in the node's properties so the
pack's own JS can re-apply by name if a future schema change shifts anything.
### Also: `memory_frames` now defaults to 0
The bank's *recent* slots hand each shot's accreted output forward as reference
images on top of the latent pin, so invented detail compounds. Measured over ten
shots at 960x544, moving 2 β 0:
| | 2 | 0 |
|---|---|---|
| texture per hop | 1.055 | **1.022** |
| chroma per hop | 1.086 | **1.039** |
| framing correlation at shot 10 | 0.976 | **0.995** |
| drift acceleration | 4.2% β 6.7%/hop | **2.3% β 2.0%/hop** |
The last row matters most: at 2 the drift *accelerates*, which is what a runaway
loop looks like. At 0 it holds flat.
The obvious worry was motion continuity, since the recency slots exist to carry
it. Tested on a scene with continuous large-amplitude movement: anchor-only
retained motion **slightly better** (β5.9% vs β6.5% over four shots) with better
framing (0.983 vs 0.971). The cost does not exist. If a busy scene ever does
lose continuity between shots, raise it to 1.
`join_anchor_noise` and `handoff_release` now ship at 0 β both are **inert**
under `context_pin` (one noises keyframes the mode never creates, the other
belongs to `latent_handoff`) and non-zero values read as tuned settings while
doing nothing.
---
## New in v2.1.6
**Chained shots stop getting brighter-edged every hop.** Not by the route
2.1.5 claimed - see the correction below.
`master_normalize` matched every frame's MEAN to one global target, which is
why a chain shows no brightness step. It was also masking a second drift it
never touched. Measured on a 3-shot chain at 960x544: mean held flat, 27.31 ->
27.43, while the DISTRIBUTION stretched - p25 fell 6 -> 2 and p95 rose 85 -> 96.
Contrast climbing every hop, re-centred each time, and handed to the next shot
as a higher-contrast starting point.
**New: `master_normalize=luma+contrast`** (now the default) matches the spread
as well as the mean. Rescaling amplitude about each frame's own mean is an
affine remap: it moves no edges, so it is not the blur this pack has always
ruled out for texture drift.
Texture growth per hop, from a log fit across all shots. 1.000 is no accretion:
| where | `luma` | `luma+contrast` | contrast spread |
|---|---|---|---|
| 960x544, in-render, 124f x 4 | 1.126 | **1.047** | 11.2% -> 0.3% |
| 960x544, 243f x 3 | 1.199 | **1.064** | 12.2% -> 0.4% |
| 640x352, 243f x 3 | 1.130 | **1.055** | 7.0% -> 0.2% |
The baseline ratchet scales with canvas (1.130 at 640x352, 1.199 at 960x544);
after normalising it stops caring (1.055 vs 1.064). There is nothing in it to
tune per resolution - it works on decoded frames with per-frame statistics
against one global target.
The target anchors to **shot 1**, not the timeline median. Contrast only
ratchets upward, so shot 1 is the one frame-set with nothing accreted onto it;
a median target pulls shot 1 UP to meet the drift (+11.7% texture, for no
benefit) where anchoring to shot 1 leaves it untouched (+0.1%) and only ever
pulls later shots down.
1:1 crops of the last shot show no loss of real detail - lamp vent slots, hinge
rivets, hair strands and knit weave all survive. What leaves is the invented
crispness.
### What is left, honestly
About **1.05 per hop**. That residual is spatial accretion, and this pass
cannot reach it: `master_normalize` runs on the finished master, outside the
feedback loop, so it cleans what you see while the next shot is still handed
the inflated pin. Four shots is slight. Ten shots is roughly +50%. If you are
chaining long, expect it.
### Correction to v2.1.5
v2.1.5 said `pin_noise=0.05` fixed this. **It does not.** That was measured on
two seeds of a single scene at 640x352 - a scene whose background was nearly
black and which barely ratcheted to begin with - and the control that would
have caught it, `pin_noise=0.00` at the reporting user's own resolution, had
never been run. With it run:
| canvas | 0.00 | 0.05 | change |
|---|---|---|---|
| 640x352 | 1.131 | 1.111 | -1.8% |
| 960x544 | 1.211 | 1.201 | -0.9% |
Both on a detail-heavy scene. The dial is small and scene-dependent, it cannot
touch the dominant drift in a busy frame, and above 0.10 it gets **worse** (0.20
measured 1.228 against a 1.211 control). Its range now stops at 0.10 and its
tooltip says all of this. It stays in the pack because it costs nothing and
does help where the ratchet is already small; it is not the fix.
### Not tested
Portrait canvases. Everything above is landscape - 640x352 and 960x544. The
mechanism is resolution-independent by construction and the two landscape sizes
agree, but 768x1344 and 736x1280 have not been measured and are not claimed.
### Measuring this yourself
Texture comparisons are only meaningful **while the framing holds**. If the
model cuts to a different setup, texture reflects content and the number is
meaningless - one portrait run here scored a flattering 0.878 per hop purely
because shot 3 cut to a close-up of a film reel. Correlate each shot's mean
frame against shot 1's before trusting any of it; a held framing sits above
0.95.
That cut is worth a writing rule of its own: **do not name a nearby object in a
shot's closing beat.** *"She glances down at the reel"* reads as a request for
a shot of the reel. Keep closing beats on the speaker's own body.
---
## Fixed in v2.1.4
- **The main workflow would not queue without two third-party packs.**
`H3_Seamless_Chain_v2.json` ships three optional accelerator nodes - `sol_attn`
and `chunk_ffn` (**ComfyUI-sol-attn**) and `block_cache`
(**comfyui-minimax-h3-blockcache-T8**) - and they were saved *active*, so a
clean install hit `missing_node_type: Node 'attention patch (gated)' has no
class_type` and nothing ran, even though the shipped recipe keeps all three
gates off. They now ship **bypassed**: dropped from the prompt entirely, with
the model passing straight through. To use one, install its pack, select the
node, `Ctrl+B` to un-bypass, **then** turn its gate on.
- **`Value 4 bigger than max of 3: memory_frames` on a workflow you never
edited.** v1.2 inserted `seed_per_shot` into the middle of the sampler's input
list. Widget values are positional, so every dial after it shifted by one in
workflows saved on v1.0/v1.1 - your old `anchor_frames` arrived as
`memory_frames`. Pre-v1.2 workflows are now repaired on load (save to make it
permanent), the sampler also stores values **by name** so no future change can
shift anything, and the validation error explains the shift instead of naming
the wrong dial.
---
## New in v2.1.3
> **Correction, 2026-08-12 β read this before turning any drift dial on.**
> A render with `chain_gain_control=flatten`, `color_level=mvgd` and the
> per-shot audio leveller all ON came out **+142% texture and +18% brighter**
> over three shots, with a visible brightness step at each join. The per-shot
> approach cannot work, for a reason the code already knew: under
> `context_pin` the drift is carried by the **raw latent pin**, and every one
> of those dials operates on decoded frames *after* the pin has been stored.
> They correct what you see and not what feeds forward. `audio_tone_control`
> has been **removed**. `color_level=mvgd` is **deprecated** β its own source
> comment records a 29% warmth step at every join. Use `color_level=scene`
> (one target for the whole piece, applied per frame at the end) and the new
> `master_normalize=luma`, both of which run outside the feedback loop and
> land every frame on the same number, so they cannot create a seam.
> Texture drift is **not** fixable after the fact: the only lever is blur, and
> blur removes real detail along with the invented kind.
- **Picture darkening over chains: measured, and the existing dial verified.**
User-reported (β1.5 luma/shot, monotonic). Same autoregressive mechanism as
the audio dulling; the raw-latent pin carries it directly. `color_level=mvgd`
- shipped since 2.1, never verified - holds an 8-shot chain to β1.0 total
luma where uncorrected loses β10.5 (both seeds). On long chains turn on all
three drift dials:
- **Audio dulling over long chains: measured, mechanism found, countered.**
Five-arm A/B on 8-shot chains: with `bank_pinned=0` (pure recency
conditioning) the voice band collapses - 84-92% of 4-10 kHz energy gone by
shot 8; with the default pinned slot, 8-50% depending on seed. It is the audio twin of the
seam sharpening ratchet, running the other way, and continuity mode is
irrelevant - the bank decides. Two counters ship: a console warning when
`bank_pinned=0` on a chain past 4 shots (there is no true "bank off" - 0/0
leaves one recency slot, the worst configuration), and
**`audio_tone_control=flatten`** - the audio twin of
`chain_gain_control=flatten`, EQ-matching every shot's long-term spectral
envelope to shot 1's before the weld. Constant per-shot gains, clamped
+/-9 dB, half-strength in the top band so it cannot manufacture hiss.
Paired A/B on the worst seed: HF loss halved (-49.5% -> -23.7%), rolloff
drift cut to a third. It reduces the drift rather than eliminating it (the
context_pin replay carries raw latents the EQ cannot reach), and it ships
OFF until ears, not spectra, have judged it.
- **The Audio Spine produced static with real-world audio files (ref2va,
user-reported).** The spine encoded `guide_audio` at whatever sample rate the
file arrived in, while the audio VAE expects its own rate (32 kHz) - the
native node resamples, the spine path did not. Nearly every real voice or
music file is 44.1/48 kHz, so the encoded latent was garbage, and because
the spine LOCKS the audio stream to that latent at every sampling step, the
render came out as noise. The same file worked through the native
`MiniMaxH3ReferenceToVideo` node, which is exactly what the reporter
observed. The spine now resamples to the VAE's rate and upmixes mono to
stereo, and the console says so. Measured: guide-to-output correlation went
from **0.06** (unrelated noise) to **0.97** on a 48 kHz voice track -
identical to a native-rate control. Also verified at 44.1 kHz mono.
- **The spine's tooltip claimed `latent_handoff` only - wrong.** It works with
every continuity mode; the per-shot stride table has carried each mode's
seam trim all along, and the fix above was render-verified on `context_pin`.
This is the locked-audio music-video path, now documented as such.
- **`two_pass_upscale` is removed.** It spatially interpolated the raw latent
between passes. H3's latent is not a spatially smooth representation, so the
interpolated values landed off-manifold and pass 2, running at low sigma, had
no room to pull them back. Every arm tested came back as colour-noise mush
against clean single-pass controls - including at 14 steps / `beta57`, the
recipe `SETTINGS.md` previously called render-verified, and including shot 1,
which carries no pin at all. It was never a `context_pin` incompatibility;
it did not work in any mode. The guard around it is gone with it.
- **`output_scale`** replaces it: a lanczos resize of each shot's finished
frames, after decode, so it cannot leave the latent manifold and works with
every continuity mode. It adds resolution, not detail - measured at
**1.78x faster** than rendering the same output size natively (45.5s vs
80.9s at 672x384) and visibly softer. Applied **per shot**, so a long chain
never holds a full upscaled master in memory at once.
- **`upscale_model`**: optional `UPSCALE_MODEL` input for real detail synthesis
(ESRGAN and friends via ComfyUI's own loader), per shot, at the model's own
factor. Render-verified with RealESRGAN x2plus: 448x256 -> 896x512, and
combined with `output_scale` it lands exactly on the requested size.
- **`video_latents` / `audio_latents` / `head_frames` outputs** on the memory
sampler (issue #12). Every shot's latent exactly as sampled, batched along
dim 0, untrimmed. Shots after the first open with `head_frames` of replayed
material that is only removed at decode, so they do not line up with the
master until you trim it - the outputs are deliberately raw rather than
trimmed on your behalf, because the pin material cannot be recovered later.
Verified: 124-frame shots return 37 latent rows (`5*((F-5)//17)+2`), and
124 + 124 - 22 is exactly the 226-frame master.
Both upscales are applied after the memory bank has taken its base-resolution
reference clip, so conditioning and VRAM are unchanged from an un-upscaled run,
and the returned latents stay base-resolution.
**If you saved your own copy of a v2.1.2 graph**, reload the shipped workflow:
removing four widgets shifts the saved widget order on that node.
---
## Fixed in v2.1.2
Writer-half only (`ComfyUI_JoyAI_Echo_GGUF_Nodes`). If you paste your own shot
list instead of letting the LLM write it, nothing here changes for you.
- **Every shot can now be saved as it renders.** A chain only became a file at
the very end, so anything that failed after the last shot destroyed the whole
run β one report was three hours lost to an OOM at the mux, *after* every shot
had rendered successfully. `save_every_shot` (both samplers) writes each shot
to `output/video/H3_SHOTS/` the moment it decodes. Written before the seam
trim, so consecutive files overlap ~1s and the master is still the clean join.
Requested in issue #13.
- **Custom sigma schedules.** The samplers built the schedule themselves from
`steps` + `scheduler` with no way to supply your own, so a turbo LoRA that
ships the curve it needs simply ran wrong rather than refusing. Both samplers
now take an optional `SIGMAS` input; connect one and it replaces the schedule,
`steps` rebinds to `len(sigmas)-1` so the two-pass split rides your curve, and
the console says the widgets are being ignored instead of silently overriding
you. It is a link-only input, so saved graphs are unaffected. Issue #14.
- **`---` separators were ignored in passthrough mode.** `example_script.txt`
ships `---` separated and every doc tells you to write scripts that way, but
the writer's passthrough path returned the whole file as ONE shot β which the
sampler then repeated to fill `shot_count`. Pasting a finished multi-shot
script rendered the entire text as shot 1, four times. It now splits on the
same rule the sampler uses. A single paragraph is still one shot, so `.txt`
batches are unaffected.
- **Reference images had no way in.** The sampler's `reference_images`
input has always existed, and `SETTINGS.md` documented it β as `unwired`,
because nothing in the workflow was connected to it. There is now a
**REFERENCE** lane in the anchors column (two image loaders β `ImageBatch` β
a gate), shipped with the gate **off** so nothing changes until you turn it
on. This is the one item here that is a new capability rather than a repair.
- **A stale prompt-set filename blocked the whole queue.** ComfyUI validates
every combo value in a graph before it will run anything, so if
`RiftPromptSource`'s saved `source_file` no longer existed β a renamed
folder, a workflow shared from another machine, or simply the prompt lane
switched to manual β the run died with `Value not in list` and *nothing*
executed, including the lanes that were fine. The node now declares
`VALIDATE_INPUTS`, so the filename is only resolved if the node actually
runs; switching to manual genuinely disables it. If it does run and the file
is missing, the error names the file.
- **Every story came out 15 shots.** The system prompt ordered exactly 15 when
the brief gave no count. It now counts the story's beats β measured 4β7 on
ordinary briefs, ~7 with no length signal (~65 s at 243 frames, past the
1-minute mark). An explicit count is still honoured exactly.
- **The `mode` dropdown did nothing.** The shipped workflow's `system_prompt`
box held a frozen copy of the long prompt, and a filled box overrides the
per-mode file β so every mode ran the long prompt and pack prompt updates
never reached anyone. It ships empty now. **If you saved your own copy of the
v2.0/v2.1 workflow, clear that box by hand.**
- **`short_story` is 1β3 shots**, not always exactly 1.
- **Messy LLM JSON no longer kills the render.** A markdown fence sharing a line
with the payload used to destroy it; truncated replies now have their complete
shots salvaged; and parsing moved *inside* the retry loop, where it should
always have been. Order: clean β parse β retry Γ3 β salvage β fail.
## New in v2.0
- Complete single-purpose workflow **H3 Seamless Chain v2** (42 nodes, 9
grouped lanes, 5 on-canvas notes), plus a **CORE** variant with zero
third-party dependencies.
- **MASTER CONTROLS** panel (`H3StudioControls`): one node drives resolution,
frames per shot and steps for the sampler *and* the prompt writer's dialogue
pacing.
- **VRAM/SPEED** panel (`H3StudioSwitches` + reserve control): three lazily
gated model patches, all OFF by default.
- **Energy-aware seam audio ("smart weld").** The boundary audio cut now lands
in the quietest gap within the incoming shot's first 0.75 s instead of
blindly at sample 0, so a word placed at a shot head is no longer clipped.
- **Rewritten activation-reserve heuristic** (payload-aware cache keys,
measured pools no longer floored, sibling-based first-run estimates, VRAM
spill detected and named).
- Prompt writer gained a **`join_style`** control that appends the
render-verified boundary rules to the system prompt.
- `flf_chain` with no boundary plates now raises a clear error instead of
silently rendering an unanchored chain.
Versions v1.0 through v1.5 shipped the same pack; v1.5's headline was voice
identity (`voice_ref` + `self_anchor_voice`).
## Verified and not verified
**Verified**
- A 3-shot `context_pin` chain and multi-shot `first_frame` chains were
reviewed **blind** by two independent video-understanding models. One
described the result as one continuous unedited take, colour consistent,
with nothing broken.
- Verified on both static talking-head content and dynamic moving content.
- Blind-reviewed recipe (differs from the shipped defaults, which add
`ref2va` reference rows for explicit voice/identity): `fl2va` checkpoint,
`euler` sampler, `beta57` scheduler,
14 steps, 362 frames per shot (about 15.1 s at 24 fps).
- Identity retention across a 40-second two-character scene with no reference
images supplied.
**Not yet verified**
- **Very long chains.** Audio dulls slightly at each hop. Restart the chain on
scene cuts rather than running one chain indefinitely.
- The `ref2va` + bank + `context_pin` combination.
- Hard-FFLF boundary-plate mode.
Nothing above is a benchmark. These are render observations from the recipe as
shipped; results will vary with content, resolution and quantisation.
## Release contents
`MiniMax-H3_Seamless_Chain_v2.0.zip`
```
ComfyUI-H3-Multishot/LICENSE
ComfyUI-H3-Multishot/README.md
ComfyUI-H3-Multishot/__init__.py defensive loader
ComfyUI-H3-Multishot/apply_gguf_arch_patch.py on-disk GGUF arch fallback
ComfyUI-H3-Multishot/h3_advanced.py advanced sampling helpers
ComfyUI-H3-Multishot/h3_avbank_probe.py AV bank diagnostics
ComfyUI-H3-Multishot/h3_cartridge.py portable character cartridges
ComfyUI-H3-Multishot/h3_episode_tools.py StudioControls, StudioSwitches, AnySwitch
ComfyUI-H3-Multishot/h3_gguf_arch.py teaches ComfyUI-GGUF the minimax_h3 arch
ComfyUI-H3-Multishot/h3_interior_patch.py interior anchors (stands down for Motion Context)
ComfyUI-H3-Multishot/h3_keyframes.py keyframe anchor nodes
ComfyUI-H3-Multishot/h3_lora_stack.py H3LoraStack
ComfyUI-H3-Multishot/h3_multishot_utils.py samplers, loaders, gates
ComfyUI-H3-Multishot/h3_ref_folder.py reference-folder picker
INSTALL.md
PROMPTING.md
SETTINGS.md
example_script.txt worked four-shot two-hander
workflows/H3_Keyframes.json single-clip keyframe anchoring
workflows/H3_Seamless_Chain_CORE.json same job, zero third-party packs
workflows/H3_Seamless_Chain_v2.json everything, optional lanes gated off
```
**Three workflows, one reason each.** `v2` is everything with the optional
lanes gated off. `CORE` does the same job with zero third-party packs β start
there if you want a render before installing anything else. `Keyframes` is a
different job: a hand-built sampling graph for anchoring a single clip at
chosen frame positions with per-anchor condition strength, not multishot.
`H3_Multishot_AIO` and `H3_Multishot_MEMORY` from earlier versions are retired
β every lane they had is in v2 (the AIO's episode source, plate chain and audio
spine were folded in; MEMORY had nothing v2 lacks). Existing copies keep
working.
## Links
- Guides: [the 5-minute guide](https://civitai.com/articles/34047/make-talking-videos-with-minimax-h3-the-5-minute-guide-26) and [every setting explained](https://civitai.com/articles/34046/every-setting-explained-the-seamless-chain-deep-manual)
- Source: <https://github.com/jlucasmcrell/ComfyUI-H3-Multishot>
- Civitai listing: <https://civitai.com/models/2833322>
- GGUF quants: <https://huggingface.co/joeygambino/MiniMax-H3-GGUF>
- Encoder and VAEs: <https://huggingface.co/Comfy-Org/MiniMax-H3>
## License
Apache-2.0 for the node pack and workflows in this repository. Model weights
are covered by their own licenses at their respective repositories.
|