Difforum · Shot Script (timeline from text)
Write your shot as text, or let an LLM write it — one beat per line and the timeline is built
- params
- timeline
- llm_instructions
- info
What it is
Difforum's Director is a timeline you draw with the mouse: scenes, camera moves, keys, energy. Great for one shot; painful at forty, or when the script came out of a chatbot, or when it lives in a client spreadsheet.
Shot Script (timeline from text) is the other way in. You give it text - typed into the box, read from a file, or fed in from any node that outputs a STRING - and it produces the Director's timeline as JSON. Wire timeline into the Director's timeline_in and the timeline appears, drawn for you, on the next run.
That third source is the interesting one. script_in takes a plain STRING, so an LLM node, a text-file loader, or a CSV export can direct the shot - the same text-through-the-graph pattern the ecosystem uses for prompt enhancement, applied to a whole clip instead of one prompt. And the job fits that pattern well: this is short structured rewriting, which is exactly what a small local model does better than a big one. A reasoning model will deliberate, then leak its thinking into your script lines.
The format, which is nearly English
0s | calm | dolly_in slow small | misty ancient forest at dawn, light shafts
2s | build | orbit_left | glowing moss and roots in the foreground
3.5s | key: the light breaks
4s | resolve | crane_up slow | the canopy opens to a pale sky
One beat per line, TIME | MOOD | CAMERA | PROMPT, and # starts a comment. A few behaviours carry it:
- TIME accepts
4s,4.5s,00:09,1:02.5orf96. Omit it and the line is spread evenly over the clip, so a bare list of prompts is a valid script. - Fields are recognised by content, not position. A camera move name (
dolly_in,push in,arc left…), a speed (slow,fast), an amount (small,large,x1.3), a lens (35mm), an easing, an audio reaction,energy 0.6,key: label- everything else becomes the prompt. 4s | key: labeldrops a Key marker at 4s, so you get the multikeyframing moment without touching the timeline.- A CSV with a header row (
time,mood,camera,prompt,key,energy) or a raw Director JSON is read as-is.
The inputs that matter
Two required, and the precedence between them is the trap everyone hits:
- script - the text box, prefilled with a working example.
- file - a
.txt,.csvor.jsoninComfyUI/input. It overrides the box, and it's re-read whenever the file changes (the node keys its cache on the file's mtime). Shipped scripts are addressed asexamples/01_infinite_zoom.txt- the repo'sexample_scripts/folder has ten recipes wired to specific templates. - script_in (optional) - text from another node. Overrides both the box and the file.
- params (optional) - from Setup. Converts seconds to frames and flags beats that fall past the end of the clip.
The precedence is what bites: file overrides the box, script_in overrides both.
Outputs
- timeline (STRING) → the Director's
timeline_in. Plain shot lines, CSV and Director JSON are all accepted there. - llm_instructions (STRING) → a ready-made prompt describing the format, with your clip length and the valid camera move names filled in. Feed it to a text model, append one line about the film, and wire the answer into
script_in. - info (STRING) → a readout: how many scenes, camera blocks and keys were parsed, each one with its timestamp, plus any warnings (a beat past the end, say). It also shows as text on the node, so a vague parse is visible before you render.
The Director also has an external setting for how incoming text meets what you drew - replace takes everything from the script, text only keeps the camera you drew and takes just the words. Start on replace; switch to text only once you're hand-tuning camera work.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/chillithebillis/Difforum.git difforum
Restart, or install from ComfyUI Manager by searching Difforum. The pack itself needs essentially nothing - numpy, which ComfyUI already ships - which is rare enough to be worth checking before you assume a dependency conflict. The LLM node is your own choice; this node doesn't care what produced the string. If you do add one, remember that any custom node is arbitrary Python with full OS access - read it before you run it.
Common problems
- "not found in ComfyUI/input". The
filebox only resolves inside the input directory, or against the shippedexamples/…scripts. Absolute paths are out. - The timeline didn't change.
fileorscript_inis feeding it. Conversely, if an LLM wrote something the parser half-understood, check info - the notes at the bottom tell you which lines it didn't like. - Beats land in the wrong place. Without a
paramswire there's no clip length to check against, so nothing warns you that a beat is past the end. - Keys didn't appear. They need the
key: labelform on their own line or as a field;key:buried inside prompt text gets treated as words.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| script | STRING | # TIME | MOOD | CAMERA | PROMPT (one beat per line, # comments) 0s | calm | dolly_in slow small | misty ancient forest at dawn, light shafts through the canopy 2s | build | orbit_left | glowing moss and roots in the foreground 3.5s | key: the light breaks 4s | resolve | crane_up slow | the canopy opens to a pale sky | One beat per line: TIME | MOOD | CAMERA | PROMPT. TIME 0s, 4.5s, 00:09 or f96. CAMERA a move (dolly_in, orbit_left, crane_up...) plus slow / fast, small / large, 35mm, an easing. '4s | key: label' adds a key. Lines without a time are spread evenly. |
| file | STRING | A .txt / .csv / .json in ComfyUI/input (e.g. shots/scene01.txt), or a shipped example: examples/01_infinite_zoom.txt. Re-read whenever it changes. Overrides the box. | |
| paramsopt | DIFFORUM_PARAMS | From Setup: converts seconds to frames and flags beats past the end. | |
| script_inopt | STRING | Text from another node (an LLM, a text file loader, a spreadsheet export). Overrides the box and the file. Feed the llm_instructions output to the LLM as its prompt. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| timeline | STRING | — |
| llm_instructions | STRING | — |
| info | STRING | — |