Writing for Hand Tie Clips
Everything in this file was confirmed against renders, not reasoned from the code. Where a rule exists because of something the model actually did, that is said out loud β the reasons are more useful than the rules.
If you only read one section, read The three laws.
What you are actually writing
Two JSON documents, both living in widgets on the node, both editable as cards in the panel or as raw JSON under the JSON tab.
shot_plan |
An ordered list of shots. One shot per hop β the shot count is the hop count. |
ref_plan |
The reference register: which pictures exist, what each one is for, who they are photographs of, and which hops each belongs on. |
The node writes a great deal that you do not: the identity lock, the citation of
the live frame carried over from the previous hop, the join sentence, and the
per-hop <Picture N> numbering. Your job is the beat β what happens this
hop β plus the register that tells the node who is in it.
The three laws
1. The prompt is additive
Sampling runs at cfg 1.0 with no negative branch. There is nothing to push against. Every concept you name is added to the conditioning and nothing can be removed by mentioning it.
BAD No cut, no camera shake, don't change the lighting.
GOOD One unbroken take. The camera holds its position. The light stays as it is.
The bad version puts cut, camera shake and change the lighting in front of
the encoder, which is precisely the opposite of the intent. Write what the shot
is doing. This is also why the directive vocabulary is phrased affirmatively
throughout, and why an unset axis emits nothing at all rather than asserting a
default β an unset axis costs zero tokens.
Whether this bites inside quoted dialogue has not been tested. A line like 'I did not think you would come' is speech, not a stage direction, and the model may well treat it as such β but nothing here was confirmed against a render, so the shipped templates avoid negation in dialogue rather than assume either answer. Your own lines are your call.
2. Never name the thing you want to end
BAD The cook stops talking and stands still.
GOOD The cook leans back against the counter with her lips closed, and lets her
eyes move slowly across the room. The kitchen is quiet apart from the low
hum of the refrigerator.
stops talking keeps her talking. Describe the state you want as a pose plus
a sound. This follows directly from law 1 but it is the single most common
mistake, so it gets its own rule.
Silence has to be written as a sound. H3 generates audio for the whole hop whether or not you asked for any. Written as an absence, you get speech β the model fills the track with the most likely thing. Written as room tone, a refrigerator, a distant car, you get room tone.
3. Put state changes at the end of the previous shot
Every hop after the first opens by holding the frames it was handed, and the audio pin carries the tail of the previous hop across the join. If shot 2 ends mid-sentence, no wording in shot 3 will make shot 3 start quiet.
shot 2: The cook sets the knife down, turns toward the window, and finishes her
last word as she looks out.
shot 3: The cook leans back against the counter with her lips closed...
To be silent, still, or somewhere else in a shot, arrive there before the previous shot ends.
The other half of law 3: a beat must survive an over-delivered hop
Law 3 says put a state change at the end of the previous shot. Its mirror is just as load-bearing: never write a beat that assumes where the previous hop stopped. A hop routinely does more than you asked. Give it a walk that was meant to be a first step and it will finish the whole walk β seven steps of turbo sampling has no reason to stop halfway.
If the next beat then instructs an action its own live frame has already carried
out, there is exactly one way for the model to satisfy it: reset the scene and
do it again. That reads as a hard cut about a second and a half into the hop,
not at the seam, because _assemble_next holds the incoming frames for a short
beat first.
Observed on a six-hop chain. Shot 3 asked for "takes a first slow step along the counter" and delivered the entire walk to the window in a tight close. Shot 4 was then handed "carries the bowl the length of the counter to the window and sets it down on the sill" β already true. It cut at 21.8 s.
Two fixes, and use both:
- Give one hop the whole movement. Splitting a walk across two hops is what creates the impossible second instruction.
- Write the next beat so it is true from either ending. "She reaches the window and sets the bowl down" is satisfiable whether she arrived last hop or is still arriving. "She takes up the bowl again" asserts no location for the bowl, so a hop that never saw it set down still has something legal to do.
The rewritten chain rendered 39 s with no cut detectable at a 0.08 scene threshold.
Dialogue propagates, and tail: ongoing compounds it
Observed on a six-hop chain: shot 1 ended on its spoken line, and the character went on talking through all five remaining hops, none of which had any dialogue written.
Two things caused it together. The audio pin carries the previous hop's tail, so
the last second of hop 1 β speech β became the opening of hop 2. And
tail: ongoing closes the prompt with action is still underway, which the
model satisfied with the action it could hear.
The fix is not to write "she is silent" in the later shots. It is to land the line early in the hop and leave a non-verbal action running into the seam:
BAD ...looks up from the chopping board and says, 'You are early.'
tail: ongoing
GOOD ...looks up from the chopping board and says, 'You are early. I have
barely started.' She turns back to the board and goes on slicing, the
knife tapping steadily against the wood.
tail: ongoing
The knife is what is still underway when the frames stop, so that is what the pin carries. Then give every dialogue-free hop a sound bed of its own β footsteps, a refrigerator, rain on glass β or the audio has nowhere to go but back to speech.
Templates
The Templates button in the SCRIPT header appends ready-made patterns β Dialogue, held, Cross the room, Leave for an unseen space, Return to the room, Quiet close. They append rather than replace, so a chain is built by stacking them.
Each one exists to demonstrate a rule on this page in a form you can edit rather
than read, and each carries a note about how to schedule references around it.
None of them contains an @tag: a template that named a reference you had not
created would turn one click into a run-time error.
Writing a beat
Shot 1 is the whole opening. Establish the person, the place, the light, what they are doing. Every later shot is only the new beat β do not re-describe the face, the clothes, or the room. The photographs, the register and the pin already carry all three, and re-asserting them competes with the pin rather than reinforcing it.
shot 1: @hero_face stands at the counter in @kitchen, an apron over a grey
t-shirt. She looks up from the chopping board and says one short line,
then turns back to the board and goes on slicing, the knife tapping
against the wood.
shot 2: She sets the knife down and walks the length of the counter to the
window, looking out at the street. Her steps are soft on the tiles and
the refrigerator hums behind her.
Shot 2 says nothing about her face, her apron or the kitchen. It does not need to. It does name a sound, because shot 2 has no dialogue and the audio needs somewhere to go that is not the previous hop's speech.
Point the action line at the reference you want used
Describing a reference in the register is not enough. The beat is what drives the frame. Write the tag into the action:
The cook stands at the counter in @kitchen, looks up from the chopping board,
and speaks one short line to someone off-frame.
@kitchen resolves to whatever <Picture N> that reference happens to be on
this hop, so adding, removing or rescheduling references never breaks your text.
A person tag behaves differently on each side of the first hop, and the node
handles the difference for you. On hop 1 @hero_face becomes <Subject 1>,
bound by the subject_definitions: block that only hop 1 carries. From hop 2 on
that block is gone, so an ordinal would dangle β and the node substitutes the
subject's name instead:
hop 1: <Subject 1> stands at the counter in <Picture 2>...
hop 4: The cook walks down a narrow hallway...
"The cook" binds to the identity sentence continuity_line puts on every
continuation hop. So give every subject a name β without one the node
falls back to <Subject N>, which points at nothing on that hop. Capitalisation
is handled at sentence starts.
Phrase a place as a place that is depicted, not as a container to be placed inside β "in @kitchen", not "inside the image of @kitchen". The latter can produce a literal composite of the photograph.
Dialogue
Write the line, in quotes, inside the beat:
@hero_face looks up from the chopping board and says, "You are early. I have
barely started." She turns back to the board and goes on slicing.
Land the line in the middle of the hop, never at its end β see Dialogue propagates.
Keep it to roughly one line per 5β7 seconds of hop. A long speech in a short hop gets truncated mid-word, and that truncation is then pinned into the next hop's audio.
If you are pasting a plan into the JSON tab rather than typing into the cards, single quotes around dialogue survive the round trip more reliably than escaped double quotes, and read identically to the encoder.
Ambience: narrowband and specific
After shot 1 the official soundscape fields are stripped and ambience is plain prose, so the exact words carry all the weight.
faint street noise through the window |
broadband β renders as a five-second hiss |
the low hum of the refrigerator |
narrowband β renders as a refrigerator |
a single click from the refrigerator, then stillness |
a discrete event β the fallback when a continuous bed still misbehaves |
Directives
Five axes, all optional. Set on a shot as "directives": {...}.
| axis | values | notes |
|---|---|---|
join |
continuous, match_cut, hard_cut |
Ignored on shot 1, which has nothing to join to. |
camera |
hold, pan_follow, push_in, pull_back, orbit, handheld |
|
framing |
keep, wide, medium, close |
|
pace |
slow, steady, brisk |
|
tail |
ongoing, settle, hold |
Defaults to ongoing. The only axis with a default. |
They compile in that order, join first, because join describes how this hop
meets the previous one.
tail is not decoration β set it on your last shot
This axis exists because of an observed failure. A beat whose action completes before the frames run out leaves the model with nothing to render, and it settles onto its strongest remaining conditioning β the identity photograph. The last few seconds of a chain would cut to the reference still.
ongoing is the default for exactly that reason: it gives the tail somewhere to
go that is not the reference. But on your final shot, ongoing tells the
model action is still underway at the final frame, and it will invent something
to satisfy that β a stray gesture, or a stray line of dialogue in the closing
second. Use settle or hold there.
Two combinations the node will warn about
join: continuous + a framing change + camera: hold. A framing change asks
the audience to be somewhere new. With the camera still, the only way to get
there is a cut β so the join and the framing are asking for opposite things and
the model picks one. Earn the framing on the move (push_in, pull_back,
pan_follow) or set framing: keep.
push_in + wide, or pull_back + close. The move points the opposite way
from the destination. Physically contradictory at any join value.
anchor: restart is a cut, not a hop interval
A restart re-reads the start image and relays nothing. That bounds the decay every hop otherwise inherits from its predecessor's most settled frames. It is also a cut: the hop opens on the photograph's pose, not where the previous hop ended.
Put one where a cut is motivated β a real pause, a change of thought, a new
beat β and not on a fixed hop interval. Pair it with join: hard_cut or
match_cut; continuous is refused on the queue. Name the room in that
beat: a pin-less hop has nothing else telling it where it is. Never on shot 1.
Needs a start image.
On a pin-less restart, identity stills that would otherwise ride the hop can
win the middle of it (the identity photograph as a mid-hop attractor). Set
"refs": [] on that shot to drop them; omit the field and you get the register
default.
tail: settle or hold, then a beat that opens mid-action
This is the over-delivery defect of law 3, caught mechanically. settle brings
the subject to rest and hold freezes the frame β so a next beat that opens
with "She continuesβ¦", "Walking to the windowβ¦", "Mid-sentenceβ¦" is asking
the model to carry on something the hop before it was told to stop. One of the
two instructions loses, and which one is a coin flip.
This is the class of defect that produced the single hard cut in the
114-second reference chain. Both shots were individually well-formed; every
other check passed. Only the join between them was wrong. Fix it at either end:
set the previous shot's tail to ongoing, or rewrite the opening so it also
reads from a standstill.
The check only catches beats that say they are continuing. A beat can open mid-action without any of those words and it will not fire β the rule in your head still has to be the real one.
All three are warnings, not errors β they are legitimate things to want. They just rarely read the way you meant.
When
join: continuousand the camera is moving, the framing sentence compiles as a landing ("The move settles into a close shotβ¦") rather than as the shot's opening state, so it does not fight the pin that still holds the previous framing. You do not have to do anything to get this; it is worth knowing it happens.
The reference register
{
"refs": [
{"tag": "hero_face", "file": "cook_face.jpg", "subject": 1,
"retention": "fully_preserved", "shots": [1, 4],
"desc": "head-and-shoulders, even light"},
{"tag": "hero_outfit", "file": "cook_apron.jpg", "subject": 1,
"retention": "partially_copy", "shots": [1]},
{"tag": "kitchen", "file": "kitchen_wide.jpg",
"retention": "reference", "shots": [1, 2, 3, 6]}
],
"subjects": {
"1": {
"name": "the cook",
"locked": "the same face, the same short dark hair, the same silver stud earrings",
"context": "the apron stays tied over the grey t-shirt"
}
}
}
Fields
| ref field | |
|---|---|
tag |
Required. Short, stable, what you write in beats as @tag. Must be unique. |
file |
A basename in ComfyUI/input/h3_refs. Set for you when you drop a picture on the rail. Never a path. |
subject |
Integer β₯ 1. Groups pictures of the same person. |
retention |
fully_preserved, partially_copy, reference. Defaults to fully_preserved when subject is set, reference otherwise. |
desc |
Free text about the picture. |
shots |
1-based hop numbers this picture rides on. See below β this is the important one. |
| retention | what carries over |
|---|---|
fully_preserved |
face, bone structure and hairstyle, exactly |
partially_copy |
the garment and its cut, moving naturally with the body |
reference |
the layout, surfaces and light, i.e. a place |
| subject field | |
|---|---|
name |
What to call them in prose. |
locked |
What must not change. This is what survives when the photograph is off the hop. |
context |
Situational state that should persist β wardrobe, what they are holding. |
At most 9 pictures on any one hop.
subject is not optional decoration
Declaring two photographs of two different people as the same subject makes the model render the average of two faces. Group photos per person, always.
Conversely, a subject block that no ref claims is an error, not a warning: it would ride every hop describing someone who never appears, and at cfg 1.0 that is purely additive noise.
Give every subject a locked. Pictures alone put a face in front of the encoder;
locked is what carries the identity across a seam where the picture is absent.
This was not true before 2026-08-29.
lockedandcontextreached hop 1 only, and a hop scheduled with no references carried no identity text at all β which is exactly how a six-hop test lost its character on the one hop that had no pictures. They now ride every hop, phrased with no<Picture N>or<Subject N>citation so they cannot send the encoder back to the plates.
Name a colour, or it drifts
locked and context ride every hop, but they only protect what they actually
describe. A noun with no adjective is exactly as unanchored as a <Subject N>
with no antecedent β each hop is an independent encode, and "the bowl" on
hop 4 gives that encode nothing to bind to.
Two renders of the same chain settled it. In the first, three hops said only
"the bowl"; it came back stainless steel on hop 4 and a different ceramic one
on hop 6. Adding the colour to every mention and stating it as a property in
context β "the bowl she is using is white porcelain" β held one white bowl
through all six hops of the re-run.
The second render then made the rule sharper than a designed test would have.
This context line ran on every hop:
the apron stays tied over the grey t-shirt
The t-shirt has its colour named and is grey in all six hops. The apron is one clause away in the same sentence, has no colour, and had turned denim blue by hop 6. One sentence, one variable.
So:
- Put persistent properties in
contextas properties, never locations. "the bowl is white porcelain" is true wherever she puts it; "the bowl stays in her hands" becomes additive text fighting the beat the moment she sets it down. - Repeat the adjective in the beats too. "the white bowl", every time.
- This applies to anything the register does not photograph on that hop. A ref
with
shots: [1]stops defending its garment at hop 2; from there the only thing holding it is the words.
shots is how you schedule references β and the default will surprise you
On a continuation chain, a reference with no
shotslist rides hop 1 only.
That is deliberate for place plates: a room still riding a hop set somewhere else beats the pin, because the model holds a crisp well-lit picture of one room and a noisy carried-over frame of another, and it follows the still. List every hop a picture should appear on, explicitly.
For a face plate, schedule it on every hop. This page used to warn that a face plate riding a later hop beats the pin the same way. A six-hop render contradicted it flatly: hop 4 carried the face plate through a walking medium shot in a space the plate had never seen β it was photographed in a different kitchen β and held cleanly, while hop 5, scheduled with no refs at all, came back a different person, and hop 6 never recovered.
Identity drift is permanent. The room came back the instant a place plate rode hop 6; nothing brought the face back, because no face plate rode anything.
lockedholds a face that is already right. Only a plate rebuilds one that is gone, so do not let it go. Putting the face on[1,2,3,4,5,6]fixed it and cost nothing visible.
The useful consequence: you can take references away deliberately. A hop with no room reference is a hop the model must invent a room for, which is exactly what you want when the character walks somewhere the register has never seen.
A worked six-hop plan
This is workflows/HandTieClips_Showcase.json, and it is built to be hard.
| hop | beat | refs active | what it tests |
|---|---|---|---|
| 1 | kitchen, dialogue | face, outfit, kitchen | establishment |
| 2 | walks to the window | kitchen | lateral movement, held identity |
| 3 | exits through the doorway | kitchen | leaving the referenced space |
| 4 | hallway | face | an unseen space β no room reference exists for it |
| 5 | dialogue at the far window | none | identity, wardrobe and voice on the pin + locked alone |
| 6 | returns to the kitchen | kitchen | re-entry after three hops away, on a match_cut |
Hop 6 joins on match_cut, not continuous. Asking for one unbroken take
between two different rooms is asking the impossible, and the first run of this
plan got what it deserved: the hallway morphed into the kitchen mid-turn. A walk
through a doorway is a cut on a matched movement.
Hop 5 is the point of the exercise. If the cook is the same person, in the same apron, with the same voice, with no picture in front of the encoder at all, the register is doing its job. Hop 6 then asks whether the room survived three hops of absence.
The face plate returns on hop 4 and not on 5 on purpose: hop 4 is where the character enters a space the register cannot describe, which is where identity drift is most likely to start, and re-asserting the face there is cheaper than recovering from drift on 5 and 6.
Duration
The label on the widget and the frames the model actually renders agree to a
tenth of a second, because every offered value satisfies the model's own frame
alignment (n % 17 == 5 at 24 fps).
| label | frames |
|---|---|
5 s |
124 |
7 s |
175 |
8 s |
192 |
10 s |
243 |
15 s |
362 |
A shot may override the chain with "duration": "7 s", using the same labels.
Short hops cut, long hops flow
Overlap is chain-wide β 0.9 s by default β and it comes out of every hop that joins
continuously. A 5 s hop asking for join: continuous therefore spends a fifth of
itself on the airlock, and the node prints a note saying so. A lucky seed still
joins, but 8 s and 15 s are the canvases the join was validated on.
This is not a limitation to write around. It is the same instinct you would edit
with: a quick beat takes a cut, a flowing take wants length. Give the short shots
join: cut and spend the continuous joins on the long ones.
Read it before you render it
Set dry_run=on and queue. Every hop's prompt compiles and the node stops β
no model, no sampler, a few seconds. The text comes out on info, and the same
thing comes out on contact_sheet as one readable page.
Do this before any long chain. What you wrote in a beat is not what the encoder
receives: the directive layer, the continuation scaffolding, the identity lock
and the <Picture N> citations are all assembled at render time. A dry run is
the only way to read the real thing before paying for it, and it is where the
warnings above show up too β the whole plan is checked on the way through.
Two related dials:
render_through=Nstops after N hops. Withcache_hops=on, 3 β 5 β 8 builds a chain up in stages and only ever renders the new hops. The plan is not truncated; shot 4 still keys exactly as it will in the full run.quality=draftforces the 448p tier and 6 steps. Enough to read blocking, camera and whether a join lands. Both values are in the cache key, so a draft never overwrites the final it stands in for.
And after the render, contact_sheet=on gives you one row per hop β first and
last delivered frame, beat, directives, seed, cache hit, what the tone
correction did. On an eight-hop chain it is the fastest way to find the hop
that broke.
Re-rolling one hop
Eight hops in, hop 6 is wrong and the other seven are fine. cache_hops is how
you fix 6 without re-rendering 1 through 5.
Turn it on before the first run of a plan -- it is off by default, and a
hop that was never cached cannot be reused. It needs nothing installed --
the lossless FFV1 encode runs in process through PyAV, which ComfyUI already
ships.
Editing a hop invalidates that hop and everything after it
Every hop is rendered from the previous hop's tail, so the cache key chains: hop N's key mixes in hop N-1's key. Rewrite shot 5's beat and hops 5, 6, 7 and 8 re-render, while 1 to 4 come straight off disk.
That is correct rather than a limitation. Hop 6 was rendered from hop 5. If hop 5 changes, the old hop 6 is a continuation of a frame that no longer exists.
The working rule: edit the earliest shot you are unhappy with, and expect everything after it to re-render. Fix front to back and you pay for each hop once. Fix back to front and you pay repeatedly.
The cache is lossless 16-bit, chosen so that a resumed chain is not a different render from an uninterrupted one -- a cached hop's last frame becomes the next hop's pin, so an 8-bit round trip would have let the cache change the output.
What re-renders the whole chain
Some inputs are not per-hop. Touching any of these invalidates every cached hop at once:
- the canvas --
resolution,aspect,overlap - sampling --
sampler_name,scheduler,shift_video,shift_audio ref_image_size,pin_to_qwen- any reference picture, the voice, the reference clip, the first-frame pin. These are keyed on actual pixels, so re-saving a file with a different crop counts even when the filename is identical
- the model wire -- a different LoRA stack or attention path is a different fingerprint, because a hop rendered under different LoRAs is not that hop
So swapping one reference photo re-renders everything. That is the right answer, since every hop carried that photo -- but it does not look right from the outside, and it is the most common reason the cache appears to have stopped working.
locked freezes a hop you are happy with
A shot with "locked": true reuses its last render even when its inputs
changed. The ordinary cache says the same inputs give you the same pixels
back; locked says give me those pixels regardless.
Use it when a hop came out better than its prompt deserves and you want to hold that exact take while you rewrite the hops around it:
{"id": "s3", "beat": "...", "locked": true, "directives": {"join": "continuous"}}
Give a locked shot an id. The lock is stored against that name rather than
against the content key -- that is what lets it survive the edit that would
otherwise have invalidated it. Without an id it falls back to shot3, which
moves if you reorder the plan.
Locking a hop does not stop the hops after it re-rendering. They are still continuations of it, and if you edited them they still change.
Two unrelated fields are called
locked.subjects.N.lockedin the reference register is identity text that rides every hop -- the face that must not drift.shots[].lockedin the shot plan is this cache pin. Same word, different blocks, unrelated jobs.
When it goes wrong
| symptom | cause | fix |
|---|---|---|
| The clip cuts to the reference photo in its last seconds | The beat finished before the frames did, and the model settled onto its strongest remaining conditioning | Set tail, and give the beat enough to do |
| A stray gesture or line in the closing second | tail: ongoing on the final shot |
settle or hold |
| She keeps talking after you asked for quiet | You named the ending (stops talking) β law 2 |
Pose plus a sound |
| Dialogue continues into hops that have none written | The hop before ended on speech, and the audio pin carried it | Land the line early; leave a non-verbal action running into the seam; give every quiet hop its own sound bed |
| A character walks between two rooms and one morphs into the other | join: continuous across a real location change |
match_cut |
| The film gets steadily dimmer over a long chain, but no single join looks wrong | Each hop darkens across its own frames and hands the darker tail on; seam correction cannot see this | tone_compensate=anchor. Measure it first with H3 Seam Report β a large one-signed sum of steps is the signature |
| The chain holds hop 1's colour, which already missed the still | Hop 1 is the first casualty: a 9-hop study read the still at chroma 33.6 / b* 26.6 against hop 1 at 30 / 22, before any relay | tone_anchor_ref=still (needs a start image). Under Motion-Context the pull still does not reach the next hop's pin β pin_mech=addguide closes that |
| A hop ignores what you told it and carries on the previous action | The previous shot's tail promised a stop your beat then overrode |
Over-delivery: change the tail, or rewrite the opening |
| A deliberately dark scene keeps getting brightened back up | tone_compensate=anchor cannot tell intent from drift |
"tone": "rebase" on the first shot of the darker scene |
| The camera keeps cutting in and out, even on a close-up | last_frame_guide=still plants the photograph at the end of every hop. A framing: close hop plays close for ~6 s then snaps wide in ~0.6 s |
before_restart β only the hop before an anchor: restart. still is safe only when no shot authors a framing |
| A restart is an obvious jump (tight/smiling into wide/neutral) | A pin-less hop opens on the still's pose while the previous hop ended somewhere else | last_frame_guide=before_restart so hop N arrives at the still. Pair the restart with hard_cut or match_cut, and name the room in the beat |
| Silence renders as speech | Silence written as an absence | Name room tone, a fridge, a distant car |
| Ambience is a five-second hiss | Broadband wording | Narrowband, or a single discrete event |
| Two characters' faces merge | Both declared as the same subject |
One subject number per person |
| The face becomes a different person partway through | A hop scheduled with no face plate. locked holds a face that is still right; only a plate rebuilds one that is gone, and the drift never self-corrects |
Put the face ref on every hop |
| A hard cut ~1.5 s into a hop, mid-scene | The previous hop over-delivered, so this beat instructs something its own live frame already did. The only way to obey is to reset the scene | Give one hop the whole movement; write the next beat so it is true from either ending |
| An animated or stylised plan renders photoreal, and hop 1 most of all | The node prepends an establishing line to hop 1 and its default asserts live action, so "Live-action, natural light" was added ahead of your style. Fixed in 0.3.3: the default is dropped when shot 1 names a medium | Name the medium in shot 1's first sentence, or clear the establish widget |
| The film gets steadily darker hop after hop | Luminance drifts like colour, in one direction, and nothing pushes back. Measured at 72 -> 11 mean luma across six hops of a night chain | Restate the light as a positive property in every beat: "the clearing reads bright under the full moon, pale stone, open detail in the shadows" |
| A location introduced mid-plan drifts | It has no place plate. Tightening a plate to its own shots does not mean the next location goes without one | Add a plate for it, riding the hop it arrives and every hop after. check_place_handoff warns when the plan ends with no plate riding |
| A prop or garment changes material or colour | It is named with no adjective, so each hop's encode is free to invent one | Repeat the adjective in every beat, and state it as a property in context |
| A continuous join reads as a cut | Framing change with camera: hold |
Earn it on the move, or framing: keep |
| The run stops naming a reference | That row's picture is not in h3_refs. A named file that is not on disk is fatal -- rendering the chain without it is never what was meant |
Drop the file on the row in the REFERENCES rail, or clear its picture to run without it |
| A reference has no effect on some hop | Its shots list leaves that hop out. This is silent, not an error -- it is a legitimate schedule |
List every hop the picture should ride |
| Every hop re-renders after a tiny edit | You changed something chain-wide -- a reference picture, the sampler, the resolution, the LoRA stack. Those are not per-hop | Expected. See Re-rolling one hop |
| A re-roll of hop 6 also re-rendered 7 and 8 | The cache key chains; 7 and 8 were continuations of the old 6 | Expected. Edit the earliest hop you dislike and work forward |
| A hop you liked changed anyway | Its inputs moved and it was not locked | "locked": true on that shot, plus a stable id |
| A pasted plan is rejected as invalid JSON | Usually escaped double quotes mangled in transit | Use single quotes around dialogue |
Having a model write the plan
The whole vocabulary, schema and rule set fits in one prompt. prompt_pack/
holds a copy-paste system prompt that turns a plain description of a scene into
a shot_plan and ref_plan this node accepts:
prompt_pack/AUTHORING_PROMPT.mdβ paste into any chat model, describe your scene, paste the two JSON blocks back into the node.prompt_pack/SCHEMA.jsonβ the machine-readable version, for anyone wiring this into their own tooling.
The node still validates everything, so a model that gets it wrong is caught rather than obeyed.
Treat a written plan as a strong draft, not a finished one. It will get the structure right -- tags, retention tiers, schedules, directives, a beat per hop that reads as one continuous take -- and that is the part that is tedious to write by hand and easy to get subtly wrong. What it will not reliably get right is judgement about your particular pictures and your particular scene. Two things to read every time before you queue:
- The
descon each ref. It is written from the photograph and it goes into the prompt verbatim as what to carry over, so a wrong one is actively harmful -- a wardrobe plate described as "a dark top" when the garment is white will fight the picture it came from. Vision quality varies a lot between models here. - The spoken words in each beat. Speech runs about 2.5 words a second, so a 10 s hop of someone talking needs roughly 25 words inside the quotes. Models write one short line and stop. The panel now warns with the arithmetic, and the fix is to write the extra sentences yourself.
Everything the panel flags is a warning rather than a rejection, because all of it is legitimate to want on purpose. Read the warnings, fix what you meant differently, and queue.