Convert a written script
Give it a written script, any format. You get script.json, script.md and
script.fountain in the format in references/script-json.md: scenes from the
script's sections, shots from its own shots (their names kept as label), and
elements in order (dialogue, action, beats, sound and music cues), each on an
estimated timeline. You do the reading yourself. This is text work, so there is
no model call. A small script then times your reading, checks every line word
for word against the text, and writes the files. When the source is a video,
use gemini-script-from-video.
Inputs
- The script as text. Save it in the job folder (
<job>/script.md); convert a .docx or doc to plain text first. - Optional: an earlier
script.jsonabout the same people, so their labels, looks and voices carry over (--cast-from). - Optional context from the person: what the video is, and who each character is when the script doesn't make it obvious.
Run it
- Read the script into
parsed.json(shape and a worked example inreferences/parsed-json.md), following the rules below. - Write
references/written-script.md's script into the project folder aswritten_script.py, unchanged, and run it:exec python written_script.py --parsed <job>/dialogue/parsed.json --text <job>/script.md \ --out-dir <job>/dialogue [--cast-from <earlier job>/dialogue/script.json] [--words-per-second 2.8] - Exit status 2 lists the lines that aren't word for word, or labels used
but missing from the cast. Fix those lines in
parsed.jsonfrom the text, never the other way round, and run it again. Nothing is written until it passes.
Reading rules
- Scenes are the script's own sections (OPENING, ROUND 1, a scene heading). A scene is a sequence in one place or one segment. Keep cutaways and inserts inside the scene they serve. Each gets a heading (INT./EXT. form when the place is known, else the section's name with the place) and one summary sentence.
- Shots are the script's own, in order, with the script's name as
labelexactly as written ("0", "5b", "6–10"). A script shot that holds several camera set-ups may stay one shot. Never merge two of the script's shots.framingis camera and subject (MEDIUM - HOST AT PODIUM).descriptionis what we see, with the set or angle the script names. - Dialogue is copied WORD FOR WORD: never paraphrased, shortened or
completed. Drop only stage-direction parentheses and formatting (bold,
italics, quotes). Split a long speech at sentence or clause boundaries where
the delivery or the person spoken to changes, never mid-clause. A line spoken
across a cut is split at the cut, and its second part has
continuedtrue. - Extension: "O.S." for a speaker in the scene but off screen (a voice on a phone or a big screen while we see someone else), "V.O." for narration to the audience, else "".
- Parenthetical and emotion on every line, never empty. The parenthetical is how it is said: the script's direction, or what the moment calls for, in a few words. The emotion is what the character feels.
- Action: one element per action. On-screen text and graphics become an action starting "ON SCREEN:". Beat: a pause or a reaction (a look, a gasp, a held moment). Sound, music, and transition only where the script calls for one.
- Seconds on every element, at a natural pace. Dialogue: the time to speak it, about 2.8 words a second, slower when the direction says so. A look 0.5–1 s, a gesture 1–2 s, a walk-off 2–3 s. Sound and music under speech: 0.
Cast labels
- One character per speaking or on-screen person, with an UPPER-CASE label made
from their name or role.
namedis true when the script names them. Give agender, adescriptionand avoice(how they should sound). Seton_screento false when we only ever hear them. - With
--cast-from, reuse the earlier labels for the same people exactly. The script fills in their description and voice when you leave those empty, and prints the labels that are new, so you can confirm each new label is a new person. - Two different people never share a label, even when one imitates the other: an impostor and the real person they fake are separate characters when both appear (the Impostor and REAL RAVI).
What you get
script.json, script.md and script.fountain in --out-dir, plus a line
with the scene, shot and line counts, how many lines lack a parenthetical or
emotion, and the estimated length.
Tips and traps
- The estimate is a planning number. A 6-scene, 22-shot, 100-line quiz-show script estimated 267 s, and the voiced track came out at 227 s. Time from the voiced track, not from this.
- Sentence-by-sentence splits of long speeches are fine for direction. The voicing step groups lines by scene anyway.
- Shot labels like "6–10" stay as written. Shots are still numbered from 1 in order, and the label rides along.
Check it
The run passes the word check with no label errors. Read script.md against
the written script: the same scenes and shots in the same order, every line
present, every line with a delivery and an emotion.


