Making step 3 clickable was not enough: render clamped VIEW back down to the
project's stage on every draw, so the click set VIEW = 3 and the redraw
immediately put it back to 2. The Filming panel stayed unreachable.
- "off the clock" fired on ANY shot not within 1.2s of the clip length, so a
7.0s line in a 10s shot was flagged alongside a genuine 11.4s overrun. Under
is fine - the engine finishes early and waits. Only an overrun gets cut off
mid-word, and only that is now flagged, as "runs long".
- A cutaway could be made or remade but never removed, and the subject is the
writer's guess so it is often wrong. There is now a Remove button.
- The Filming step was disabled until the project was already filming, so the
only way to start was a button at the bottom of the Layout page. Approved now
reaches step 3.
- "Cutting to the clock ... rewrites how the lines are divided" is no longer
true - it measures and no longer moves boundaries, because Setup shots does
the division locally. Copy corrected.
- "speaking the words costs about a hundredth of a cent" was wrong by three
orders of magnitude; a measured run is about 16 cents.
The one button did two unrelated jobs and showed the result of neither.
- Setup shots is now local division only: Python at the engine's own measured
speaking rate, ~500ms, free, no LLM and no TTS. Answers "divide with local
code, because each voice has a different cadence".
- Script audio is a separate button that reads it aloud AND SHOWS THE PLAYER.
The audio was always being made and uploaded, then rendered into #alignout,
which only exists on the Layout view - so pressing the button on the Words
view produced fifteen seconds of nothing.
- The voice is now named. With an Omni engine the film ships Omni's own native
voice, so the ElevenLabs read-through is a stand-in and says so.
- Rewrites are no longer capped at three. The real cost from OpenRouter is
shown beside the buttons, added to the bill at cost with no markup.
- "Approve and lock it" now explains what it does.
A measured run of the 80s NIMBY script came back at $0.1589 for 946 characters
- the ElevenLabs rate of $22 per 131,000 - so the copy understated it by more
than tenfold. Compute it from the words actually in the box instead.
The whole piece is now editable at the top of the review page, the rewrite
controls sit directly under it rather than below eight shot cards, and cutting
the narrative into shots is an explicit Setup shots button.
- narrative editable via set_narrative; shots marked stale until re-cut
- writer dropdown (Grok 4.6/4.5, Claude Opus 5/Sonnet 5, GPT-5.6/5.5, Gemini 3.1)
- align no longer runs unasked on page load, which spoke the script and moved
every shot boundary for a cent nobody asked to spend
- align's fit check reads shot length from the model instead of assuming 8s,
which reported every Omni shot 2s short
The file URL ends in /public with no filename, which is fine for anything that
reads headers — it already serves Content-Type video/mp4 and a
Content-Disposition of final.mp4 — but clients that sniff the extension out of
the URL string get it wrong. A filename query is accepted and ignored by the
file service, so the link can end in .mp4 at a cost of a dozen characters.
The button uses the native share sheet where there is one, which on a phone is
the shortest path into a text message, and falls back to copying.
Sending the video itself by MMS puts it through carrier transcoding and it
arrives looking far worse than it left. So the picture carries the hook and a
link carries the video at full quality.
The still is 720x1280 and lands around 90KB at near-maximum quality — carrier
limits are typically 600KB and up, so there is no reason to compress hard. The
frame is chosen by sampling several moments and keeping the one that compresses
largest, which in practice means eyes open and in focus rather than mid-blink.
The play button sits at two thirds height so it never covers the face.
"I am not" and "extraordinarily unimpressed" are both three words and nowhere
near the same length, so any word budget is a guess about something we can
measure. Three attempts at tuning one were wrong in both directions.
Speaking the monologue once returns the start and end of every character, so
word boundaries are exact. This cuts each shot at the word whose end sits
nearest the boundary, preferring a sentence ending when one is close. On the
first real test it produced a 20 word shot landing at 7.8s and cutting on a
full stop, where the budget had said 18.
The run-through is spoken by ElevenLabs, but the finished audio is Veo's
performance re-voiced, and Veo enunciates more slowly - it was asked to, to
make the mouth movement readable. Measured on a real clip: 17 words ran 7.02s
filmed (about 2.4 words a second) against roughly 3.0 words a second spoken.
So the raw previz number understates the filmed length by about a quarter, and
the dead-air warning was correspondingly overstated - it claimed 2.4 seconds of
silence per shot where the filmed figure is nearer one. Show both numbers and
name the factor rather than quietly applying it.
The first real measurement was the opposite of what was expected: four lines
spoke in 21.7s against 32s of video, so every eight second shot carries about
2.4 seconds of silence. That is almost certainly where the sound effects and
echo come from, since the model fills a gap it was given nothing to do with.
So flag short as loudly as long, and say what it means.
Filming is $0.40 a shot. Speaking the same words is a few hundredths of a cent.
So the narrative — does it flow, is the joke in the right place, does it drag —
can now be judged for about one percent of the cost, using the opening stills
as placeholders and the pet's real voice.
It also replaces a guess with a measurement. Line length was estimated from a
word count and hoped to fit eight seconds; speaking it says exactly how long it
takes, and a line that runs over is flagged before anything is filmed rather
than after.
"locked" used to mean "the script is approved" and now means "not on the layout
stage", so the price card read Approved on every stage but one — including on a
script still waiting to be read. It should follow whether the words are still
open, which is what canEditScript says.
The page showed the script, every shot's directing controls, your pictures,
filming, the joins and the cut simultaneously. That is four different jobs on
one screen, and the first of them - reading the words and getting them right -
is the one that matters most and was the easiest to skip past.
It is now four stages with a stepper: the words, the layout, filming, the cut.
Each shows only what belongs to it, and you can step back to look at an earlier
one at any time.
This also fixes something that was simply wrong: approving the script used to
hide every shot control, so the setting, camera and cutaways became uneditable
at exactly the moment you were meant to be arranging them. Directing now lives
in its own stage, after the words are settled and before anything is filmed.
The "$'" substitution token injected a second copy of everything after the
match point. The first copy was repaired into the real bindPost; this deletes
the leftover fragment and the duplicate that followed it, so the file ends at
its only </html> again.
The previous commit's replacement text contained the sequence "$'", which the
patch tool treats as a substitution token meaning "everything after the match".
It injected a duplicate copy of the rest of the file and left script.html with
a syntax error, so the page showed only "Loading...".
This closes the broken string and reopens bindPost so the duplicated tail that
follows becomes its legitimate body. A second patch removes the leftover.
Filming again used to overwrite the video outright. Models drift between
generations, so re-filming to fix one thing can make another worse, and there
was no way back.
Every take is now kept and listed under the cut, newest first, with what it
cost and which voice it used. Restoring one keeps the take you are leaving, so
going back is reversible too.
This deliberately lands before the "remake this shot" button, because that is
what will make people re-film often enough to need it.
The first and last ruler labels were centred on the track edges, so "0s" and
"32s" were half cut off.
More importantly the page had become busy: a finished video showed the cut, the
timeline, the joins and the shots all at once. The timeline is now behind an
"Open the video editor" button next to the video, so the default view is the
finished thing and the editing bay is somewhere you choose to go.
Playing to the end left the button reading "Pause" forever, because nothing
listened for the video ending. It now resets on end and on pause, shows the
current time against the total, and the track can be clicked or the playhead
dragged to reach a moment directly. Clicking a shot marker jumps to that shot.
Fixes and additions from watching it in use:
- the play button stayed on "Pause" after the video ran to the end
- there was no time readout and no way to reach a particular moment
- a cutaway was an anonymous blue rectangle, when the useful thing to show is
the picture it will actually put on screen
- nothing marked where one shot ended and the next began, which is the whole
reason the cutaways sit where they do
Shots are now marked above the bar with their opening frame, cutaways carry a
thumbnail of the image itself, and the playhead can be dragged or the track
clicked to move through the piece.
The cut could be measured and executed but never seen or adjusted. This adds a
track view of where each cutaway sits and a player that switches sources at
each boundary, so the edit can be judged before spending anything on a render.
The audio is deliberately never touched: the talking head plays underneath
throughout and only the picture changes, which is what makes a cutaway hide a
join instead of interrupting the piece.
Dragging a block moves that cutaway; the handle on its right edge changes how
long it runs. Nothing is saved until Save, and Reset returns to the placement
the system chose.
Matching pictures to the script is a model call and cannot answer inside the
few seconds the API gateway allows, so it returned "Function timed out". It now
starts in the background and this waits for it, the same way frames and joins
already do.
Three things were unclear or missing:
- "Make the frame" reads as though it might film the shot. It draws one still,
the opening frame, and films nothing. Say so.
- Once a project was ready there was no way to film it again, so editing a
shot afterwards led nowhere. The film card now appears for a finished video
as "Film it again".
- Nothing explained that these per-shot buttons are optional. Filming draws
any missing frame and makes any missing cutaway itself, so they are there to
preview, not to complete.
A video was filmed where shot 1 had a photo selected but no opening frame. The
seed was therefore empty and Veo was handed the written description alone, so
it invented a plausible bulldog: no collar, wrong eye colour. Nothing on this
page suggested anything was missing.
The worker now draws any missing frame before filming rather than proceeding
without a likeness, so this says that is going to happen and why it takes
longer.
The reason was already being stored on the project, but the page showed a bare
"could not finish" and a Start again button. A dead link is the most common
cause and it is the reader's to fix, so it needs saying out loud.