Nodes/Difforum/Difforum · Shot Script (timeline from text)
ComfyUI Node

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

By chillithebillis·Created 4 months ago·Updated 17 days ago· 10
Difforum · Shot Script (timeline from text)
  • params
  • timeline
  • llm_instructions
  • info
◄script# 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►
◄file►
◄script_in—►

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.5 or f96. 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: label drops 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, .csv or .json in ComfyUI/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 as examples/01_infinite_zoom.txt - the repo's example_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 file box only resolves inside the input directory, or against the shipped examples/… scripts. Absolute paths are out.
  • The timeline didn't change. file or script_in is 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 params wire 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: label form on their own line or as a field; key: buried inside prompt text gets treated as words.
CategoryDifforum/2 · Direction

Inputs (4)

NameTypeDefaultDescription
scriptSTRING# 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 skyOne 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.
fileSTRINGA .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.
paramsoptDIFFORUM_PARAMSFrom Setup: converts seconds to frames and flags beats past the end.
script_inoptSTRINGText 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)

NameTypeDescription
timelineSTRING—
llm_instructionsSTRING—
infoSTRING—