Camera control and motion — what separates slop from craft — step 8 of 8
One last thing before we move on. Same surface as a write step — but the lesson doesn't complete until this passes.
Final drill. Synthesize the shot vocabulary + camera moves into a
shot planner: plan_sequence(beats) that takes a list of "beat"
dicts and returns a list of "shot" dicts, one per beat, with shot
type and camera move filled in according to these rules.
Each beat dict has:
intent(str): one of"establish","reveal","intensify","reaction","product_beauty","resolution"subject(str): what's being filmedduration_sec(float)
For each beat, assign a shot_type and a camera_move using these
rules:
| intent | shot_type | camera_move |
|---|---|---|
establish | "wide" | "slow pan right" |
reveal | "medium wide" | "dolly out" |
intensify | "close-up" | "slow push-in" |
reaction | "medium close" | "static" |
product_beauty | "medium" | "orbit 180 degrees" |
resolution | "wide" | "slow pull-out" |
Return a list of {"subject": ..., "shot_type": ..., "camera_move": ..., "duration_sec": ...} dicts in beat order.
THEN, after building the sequence, validate that each shot's camera move fits its duration:
"orbit 180 degrees"requiresduration_sec >= 4.0"slow push-in"requiresduration_sec >= 2.0- everything else is fine at any duration
Append "warning": True to any shot that fails the duration check.
Pass-through shots get no warning key.
A 6-beat sequence runs. Expected output:
city / wide / slow pan right / 5.0 / ok
shop facade / medium wide / dolly out / 4.0 / ok
closing the deal / close-up / slow push-in / 1.5 / WARN
the buyer / medium close / static / 3.0 / ok
the product / medium / orbit 180 degrees / 3.0 / WARN
city again / wide / slow pull-out / 5.0 / ok
One last thing before we move on. Same surface as a write step — but the lesson doesn't complete until this passes.
Final drill. Synthesize the shot vocabulary + camera moves into a
shot planner: plan_sequence(beats) that takes a list of "beat"
dicts and returns a list of "shot" dicts, one per beat, with shot
type and camera move filled in according to these rules.
Each beat dict has:
intent(str): one of"establish","reveal","intensify","reaction","product_beauty","resolution"subject(str): what's being filmedduration_sec(float)
For each beat, assign a shot_type and a camera_move using these
rules:
| intent | shot_type | camera_move |
|---|---|---|
establish | "wide" | "slow pan right" |
reveal | "medium wide" | "dolly out" |
intensify | "close-up" | "slow push-in" |
reaction | "medium close" | "static" |
product_beauty | "medium" | "orbit 180 degrees" |
resolution | "wide" | "slow pull-out" |
Return a list of {"subject": ..., "shot_type": ..., "camera_move": ..., "duration_sec": ...} dicts in beat order.
THEN, after building the sequence, validate that each shot's camera move fits its duration:
"orbit 180 degrees"requiresduration_sec >= 4.0"slow push-in"requiresduration_sec >= 2.0- everything else is fine at any duration
Append "warning": True to any shot that fails the duration check.
Pass-through shots get no warning key.
A 6-beat sequence runs. Expected output:
city / wide / slow pan right / 5.0 / ok
shop facade / medium wide / dolly out / 4.0 / ok
closing the deal / close-up / slow push-in / 1.5 / WARN
the buyer / medium close / static / 3.0 / ok
the product / medium / orbit 180 degrees / 3.0 / WARN
city again / wide / slow pull-out / 5.0 / ok
this step needs the editor
on desktop today; in the app (coming soon). save your spot and we'll bring you back here when you're ready.