Commands and files
How to make a promo by hand, and what each file says. Every file is checked when it is read, and an error names the exact field.
Commands
npm run promo -- make <promo> # record every shot, then render
npm run promo -- record <promo> [shot…] # film all or some shots again
npm run promo -- render <promo> [--edit=edit-x.json] # edit only; a variant → video-x.mp4
npm run promo -- import <promo> <shot> <video> [--from=s] [--to=s] [name=s…]
npm run promo -- publish <promo> <review link> "<message>" # only on the owner's word
npm run promo -- features <project> <guide file>
npm run promo -- scenarios <project> --accounts=a,b [--target=android] [--count=3]
npm run promo -- scenarios-check <project> <scenario set.json>
npm run check # typecheck, lint, format, tests
make |
Records, then renders: the whole video from nothing. |
record |
Resets the demo data and films the shots in steps.json. |
render |
Cuts the video from edit.json (or a variant) and runs the checks. |
import |
Turns any screen recording (an iPhone video) into a shot, with marks. |
publish |
Sends the review link to the owner's phone through a GitHub issue. |
features |
Reads the app's own guide into a list of what the app can do. |
scenarios |
Writes the brief for the scenario writer. |
scenarios-check |
Checks the scenarios it wrote and saves them as drafts. |
Files of a project
projects/<app>/
target.json where the app lives, its logins file, its demo-data reset
features.json what the app can do (from its guide)
filming.json what can and can't be filmed
scenarios/*.md ad ideas in plain words
assets/ music, logo
promos/<promo>/
steps.json how to drive the app
edit.json how to cut the video (variants: edit-store.json …)
out/<app>/<promo>/ recordings and videos (not kept in git)
target.json
The platform (web or android); the app's address, or its Android package
and link scheme; the path to the logins file, which stays outside the project and never appears in
logs; the reset command run before every recording; and for Android a demo clock (time zone and time
of day).
steps.json
setup steps run off camera (log in, open the start screen). Each of the
shots is filmed as its own clip. A shot can name an account to film as,
two devices to film at once, or notifications to let banners appear. Any
step can say on which device it runs.
| Step | What it does |
|---|---|
open |
Opens a screen by its path. |
tap |
Taps an element; optional skips it if it isn't there. |
fill |
Types into a field; {account.login} fills in a login at run time. |
type |
Types text into whatever has focus. |
scroll |
Scrolls by a number of points, in natural swipes. |
waitFor |
Waits until an element appears. |
wait |
Holds for some seconds. |
mark |
Names this moment, so the edit can cut to it. |
locate |
Finds an element and records its box, for lifts and labels; with around, the
smallest element holding both (a whole card, from its title to its button).
|
Elements are found by test id, by role and name (button,
tab, link, checkbox; exact by default), or by text. Test ids
and paths work in every language of the app.
edit.json
The output size and frame rate, a theme (colours, font, and the screen style:
phone, floating or full), and the scenes in order.
| Part | What it says |
|---|---|
phone scene |
A clip from a time or a mark, for some seconds: caption,
zoom keyframes, lifts, speed, a second screen beside
it (with), a transition in.
|
card scene |
Big words on colour; with an image and a call to action, the end card. |
say |
The narrator reads the scene's own words, or other text at a set time. |
audio |
Music (style: upbeat, calm, pop, with a seed; or a song
file), the narrator's voice and speed, voice lines, effects on or off.
|
checks |
Which rules to check against: social or appStore. |
poster |
The cover still written next to the video (poster.jpg); by default when the
phone has landed and the first caption is written.
|
Checks
Run on every render and written to checks.json. Any failure stops the video.
hook |
The video opens on the app, not on a card. |
reading-time |
Every caption stays long enough to read. |
length |
The length fits the destination (App Store: 15 to 30 seconds). |
forbidden-words |
No words the stores forbid in our texts (Apple 2.3.8: "kids", "child" …). |
end-card |
There is an end card with the logo and a call to action. |
callouts |
Labels land on the screen. |
voice-timing |
Voice lines fit inside the video and never overlap. |
no-still-stretches |
Nothing stands still for too long. |
framing |
A zoom never cuts a lifted control: it stays in view, with a margin. |