Product UI video: a presenter and the live product, cut as an ad
The person brings a company's website (a URL) or product screenshots, and says who should present. You deliver a ~30 s 1920×1080 ad: the presenter full frame to camera with one headline, then the product's own screens, rebuilt as HTML layers, with a camera pushing into each one on the words while the presenter talks from a face bubble, ending on the logo and the site's headline.
How it differs from its siblings: recipe-sales-video-from-pitch-deck and
recipe-slide-deck-presenter put a presenter beside real slides, shown as they are, for
a walkthrough of 1–3 min. Here nothing is a slide: the product's UI is rebuilt so its
rows, pills and charts move, a camera pushes into it, and it is cut as a 30 s ad. The
talking-head mechanics (chunking, pinned stills, sync checks) are the slide-deck recipe's;
this recipe names them and adds only what differs.
Job notes (brief, site copy, brand, references, picks, each round's notes) go in
job-notes.md in the job folder; rounds and deliverables follow the core skill.
Steps
-
Capture the site. In the browser the session has:
- Copy: the headline, the sub, the value props in homepage order, quotes, the people named.
- Brand from computed styles, not by eye:
getComputedStyleon theh1(font family), the primary button (backgroundColor) and the body. The product's own screens often use a different font from the marketing site: judge the app font from the screenshots (hn4a-compositor-rebuild-product-screen). - Every image: scroll to the bottom in steps first (lazy loads), then collect every
imgsource and CSSbackground-imageURL and download each intoreferences/site/(exec python, urllib). Make a contact sheet with Pillow and look at every one; open odd shapes on their own. - The logo: note its pixel size. A small site PNG (444 px wide on the worked job)
looks poor full screen. Look for a bigger copy: the
og:image, the app icon or favicon set, the footer, a press kit. Failing that, ask the person for a vector file (SVG, PDF, EPS). Never let an image model redraw a logo: it changes letterforms. - A real person on the site (a founder in a video): embeds often won't play off-site and demo pages sit behind lead forms (never submit them). A video's thumbnail image is often enough; ask before downloading a video.
Write it all into
job-notes.md, with a table of the captured references and a proposed use for each, and propose avatar options (a real person from the site, the brand's mascot, a stock-style presenter). A real person followsguide-depict-real-people-and-places; keep a spec with a real employee private to the client. Gate: the person picks the avatar and the screens that matter (usually 1–2). -
Script (
guide-make-a-script-from-source,llm-convert-written-script): ~30 s is ~72 words (the worked take ran 2.7 words/s: 28.3 s of speech in a 30 s comp). Open on the customer's pain, one line per value prop in the site's order, close on the site's own headline. Draft it as a beat table (time, picture, VO) plus a voice brief. The accent comes from the person, never from the face. Gate: the script word for word, and the brief. -
Storyboard review: the gate before any paid render. The strongest lesson of the worked job: it had no storyboard, so every layout note arrived after both talking heads and both screen rebuilds were done. Right after the script, while casting runs, build ~10 frames of the whole ad from cheap parts in the same spec the cut will use: site screenshots as image layers under a camera at the planned scale and point, the presenter (a placeholder still or a grey silhouette) at the planned size, the bubble as a circle at its real size, the headline in the brand font, prop graphics as rough boxes, and each camera target drawn as a dashed box with a label. How to build and show it:
references/storyboard.md. Gate: ad or slides? background? how big is the presenter and the bubble? how many camera moves? Nothing that costs money starts until the person says the direction is right. -
Voice (
guide-cast-a-voice,guide-voice-a-script,elevenlabs-tts-v4): audition the top 6 on two of the script's own lines; the person picks (on the worked job, the listener's 4th-ranked voice). A real employee as the avatar gets a cast library voice. pcm_48000, −16 LUFS on the finished track. Transcribe it withwhisper-cliand check it word for word against the script (72/72 on the worked job); read cue times for every phrase the picture lands on (python-read-word-timings) intoaudio/cues.md. Re-cue from a new transcript on every re-take, never by hand. Gate: the track, listened to whole. -
Presenter stills (
guide-edit-a-frame-before-animating,nano-banana-restage-for-new-aspect): from one reference, nano banana Pro edit and 2.1 edit, 2 prompts × 2 models, 2K, "no text other than the chest logo" when the wardrobe is branded (the logo as a second reference).- Check every still for invented brand marks. All four worked-job stills grew a
sportswear logo on the sleeve. Patch it locally (
nano-banana-remove-small-objectwithpython-crop-and-paste-back); never a full-frame pass. - Make both framings from one 2K image: place the picked 3:4 still on a grey 16:9 canvas where the presenter should stand (worked job: x 1100, y 90, 920 px tall), then nano banana Pro: "REPLACE THE ENTIRE BACKGROUND, the grey canvas and everything behind them, with …" a coherent new set, the left two-thirds open, calm and a little darker for a big white title; 3 seeds. The bubble's 3:4 still is a crop of that same 2K image around the face (worked: x 1488..2640, 1152×1536): same set, more face pixels. Gate: both stills locked.
- Check every still for invented brand marks. All four worked-job stills grew a
sportswear logo on the sleeve. Patch it locally (
-
Two talking heads of the same take (
guide-lipsync-video,h3-max-lipsync-a-still, chunked and checked as inrecipe-slide-deck-presenterstep 4): the 16:9 still for the full-frame opening and the 3:4 crop for the bubble, H3 Max 768P, chunks ≤ 15 s at sentence pauses, $0.08/s at list each. Check every chunk (python-check-pinned-frames,hn4a-lipsync-check) and re-sync a drifting one withveed-v2-resync-existing-take. H3 at 768P returns 1344×768 for a 16:9 still (narrower than 16:9) and 768×1024 for 3:4. Expect a two-finger "count" on a list of three even when it's banned: harmless where that span is in the bubble. Gate: both assembled heads, unmuted, with their sync plots. -
Rebuild the screens, one subagent per screen in parallel, each with the brief in
hn4a-compositor-rebuild-product-screen(references/subagent-brief.mdthere): the contractPOI+build(group_id, cues) -> layers, the cue keys, believable data in the client's market sharing one "today", and the checks. Move every value prop's beat onto the 1–2 screens that matter, rather than one screen per prop. Gate: each screen's test spec as a live{"compose": "ui/<screen>_test.json"}candidate on areview_openpage, with its side-by-side still as a ref. -
The cut: one build script,
cut/build_cut.py, writescut/cut_vN.json; layers bottom first are the page background, each screen's camera group and its builder's layers, overlays that must not zoom, the end card, the presenter, the scrim, the opening titles. The layer-by-layer specifics (presenter to bubble,trim, the bubble crop, the camera numbers, overlays, the end card):references/cut.md. Then:exec python cut/build_cut.py --version v3 exec hn4a-compositor still cut/cut_v3.json cut/stills/v3/t9.0.png --at 9.0 exec hn4a-compositor render cut/cut_v3.json exec hn4a-compositor render cut/cut_v3.json --frames 480:660 --into cut/cut_v3.mp4Gate:
review_openwith the new spec as a{"compose": "cut/cut_vN.json"}candidate beside the last version the person saw, and a row of stills at the cue times; render only once the look is approved. -
Rounds. Write each note into
job-notes.md, fix it in the build script (never by hand in the JSON), and re-render only the span that changed (--into). A re-voiced line means a new transcript, new cues and the affected chunks of both heads. Deliver per the core skill: the 1920×1080 30 fps master and a 960×540 copy.
Choices this recipe makes
- An ad, not slides. The presenter full-bleed behind the type, never in a rounded window beside a kicker, headline and subhead. One headline at most; never print what's being said. A face bubble is right for the UI beats.
- Camera: one push per spoken beat, then hold. Start 0.2–0.5 s before the word, land
1–1.5 s later (inOut), hold until the next beat; ~3 pushes per screen at 1.35–1.6×, each
framing a region where several cues land. The calm-camera rule and the group mechanics
are in
hn4a-compositor-motion-graphics(references/spec-format.md, A camera over a group); this ad wants the upper end of that range. - The UI is rebuilt, never animated by a video model (it warps text). Screenshots as still image layers under a camera are fine for the storyboard only.
- One take, two framings. The 16:9 head and the 3:4 bubble head are lip-synced from the same track and the same 2K still, so the set matches and the bubble has more face pixels (265 source px in a 320 px bubble, 1.2× up, against 199 px and 1.6× from the 16:9 take).
- A new set rather than an outpainted one, with the title side planned into the prompt.
- Overlays aren't UI. A value prop that isn't a screen feature (syncing with three ERP names) is ad type over a frosted, blurred screen, not a small card in a corner.
Dead ends
- Outpainting around the original still: the background's geometry extended into nonsense, and nano banana 2.1's take had a seam. Replace the whole background instead.
- Image-to-video on a UI screenshot: the text warps.
- The small site PNG logo full screen: 444 px wide, it looked poor on a 1080 end card. Find a bigger copy or ask for a vector.
- A slides-and-bullets layout: a windowed presenter beside eyebrow, headline and subhead read as a deck; the person asked for an ad.
- No storyboard: every layout note came after the paid renders.
- Too many and too few moves: 9 moves in 15 s at 1.45–1.85× was frantic; 2 moves at 1.12–1.42× was sleepy.
- A video layer starting after 0 without
trim: it played from its source's 0 and lagged the voice by its start time. - Scaling 1344×768 to the frame's height: it left 15 px strips at the sides, shipped in
rev1. Scale by
max(W/sw, H/sh)and centre.
Worked example
Acme Co., a CRM for field-service firms: a 30 s spec ad from its site. Two screens rebuilt (an Activities table from a 1377×969 screenshot; a pipeline dashboard from a 1199×550 one plus a chart from a second image). A company salesperson as the avatar, with a cast library voice. Script 72 words; take 28.3 s, 72/72 words by transcript. Talking heads: 3 chunks each (13.3 / 8.1 / 6.9 s) at 768P, about $4.50 for both at $0.08/s list; the 16:9 chunk 00 was flagged (lag −789 to +597 ms) and its VEED re-sync passed (−69 to +154 ms), about $0.90 more. Delivered: 1920×1080, 30 fps, H.264 + AAC, 20 MB, and a 960×540 copy of 3 MB; the v3 cut was approved as rev1.
| Round | Note (paraphrased) | Fix |
|---|---|---|
| v1 | Feels like slides and bullets; make it an ad | Presenter full-frame 16:9 behind the titles, no window; then the shrink into the bubble |
| v1 | Bubble bigger | 220 → 320 px |
| v1 | Camera too fast, too many places (9 moves in 15 s at 1.45–1.85×) | v2: 2 moves at 1.12–1.42× |
| v2 | Now it's sleepy | v3: ~3 pushes per screen at 1.35–1.6×, holding between |
| v2 | Background doesn't make sense (outpainted) | A new set, whole background replaced, left two-thirds calm for the title |
| v2 | Drop the eyebrow and subhead; the presenter says them | One headline; the prop graphic bigger and central |
| v2 | Bubble out of sync | trim = the layer's start; bubble from the 3:4 take (check: −10 ms in v3) |
| rev1 | 15 px strips at the sides of the opening | Scale by max(W/sw, H/sh) |
| all | The animated screens land well | Kept |


