This is the default camera for the presenter-over-UI ad recipe, with the timing and
scale approved on its two rebuilt product screens. Give it the cue times of the beats
(from the voice's word times) and the point and
scale each beat frames; get the moves for a camera group that pushes on every beat,
holds, and cuts or whips to the next screen. The group's mechanics are
hn4a-compositor (references/spec-format.md, A camera over a group) and the keys are
written by camera(id, start, moves) in hn4a-compositor-in-scene-titles
(references/titles-fx.md). For a calmer piece (an explainer, a summary, slides, a
screen recording walked through), use hn4a-compositor-calm-explainer-camera. To zoom
into footage rather than layers, render the move in ffmpeg (ffmpeg-eased-punch-in).
Inputs
- The beats: the words the picture lands on, timed from the voice
(
python-read-word-timings,cues), and for each the point of the screen to frame and a scale. - The screen as layers in the camera group: rebuilt
(
hn4a-compositor-rebuild-product-screen) or cut up from a screenshot (hn4a-compositor-make-product-ui-exciting), with its points of interest in group px, and the screen's box in group px forwithin.
Run it
Write references/ad_camera.md's module into the job's scripts/ folder as
scripts/ad_camera.py, beside scripts/titles_fx.py, and call it from the build script
there:
moves = push_on_beats(4.0, [(5.1, P["status_col"], 1.45), (7.6, P["new_row"], 1.55)])
moves = cut_to(moves, 10.0, P["donut"], 1.35) # a hard cut on a beat
moves, blur = whip(moves, 4.0, 13.5, P["won_row"], 1.6) # a whip pan landing at 13.5
layers.append(camera("cam_act", 4.0, moves, end=16.0, within=SCREEN, anim={"blur": blur}))
pace(moves, 4.0, 16.0)
| Helper | Does | Defaults |
|---|---|---|
push_on_beats(start, beats) | Holds the opening framing, then for each (word_time, point, scale) pushes from lead before the word and lands land later, then holds | lead 0.3 s, land 1.2 s, inOut |
cut_to(moves, t, point, scale) | A hard cut: the next frame is the new framing | |
whip(moves, start, t, point, scale) | A fast pan landing at t, with the group blurred most at its midpoint | dur 0.3 s, blur 18 px |
pace(moves, start, end) | Prints moves per 15 s and the scale range, against the numbers below |
within=(x, y, w, h) on camera() keeps every framing inside the screen, so a push
on a card at the screen's edge never shows the stage past it.
The pace that worked
From a 30 s presenter ad over two rebuilt screens, three versions of the same 15 s:
| Version | Moves in 15 s | Scale | The person's note |
|---|---|---|---|
| v1 | 9 | 1.45–1.85× | Too fast, too many places |
| v2 | 2 | 1.12–1.42× | Sleepy |
| v3 | ~3 per screen, holding between | 1.35–1.6× | Approved |
- Defaults for the presenter-over-UI ad: one push per spoken beat, then hold. Start 0.2–0.5 s before the word, land 1–1.5 s later (inOut), and hold until the next beat. Use about 3 pushes per screen at 1.35–1.6×, the approved version above.
- Frame a region where several cues land rather than chasing each one.
- Cut, zoom or whip to the next screen; never drift across empty space to get there.
- A part of the screen builds as the camera arrives at it
(
hn4a-compositor-make-product-ui-exciting): cue its build at the move's start.
Tips and traps
- A push on a point near the screen's edge shows past it (a quarter of the frame was
the stage on a test push to a corner card at 1.5×): pass
within. - A
holdkey at the old framing starts each move. Without it a move eases from the previous key's time, so it starts drifting the moment the last move landed. - Text stays sharp at the closest push only while the children are re-rasterized
(
hn4a-compositor, A camera over a group): check a still at the closest point. - The whip's numbers are a starting point, not a job's: look at a still at its midpoint and at landing.
Check it
- For the presenter-over-UI ad, check
pace(...)against the approved defaults above. Keep the reading time in the holds and review the result in the live spec. - A still at every landing time: the beat's point at the frame's centre, nothing past the screen's edge, text crisp.
- The live spec with the voice (
hn4a-compositor, Live spec, then render): pace is only judged moving, on the words.


