render-search-grid
Render the 'search-grid' format from a config. It is a deterministic assembler — no
generative image/video, no AI-rendered text. Everything on screen is the brand's REAL
product/lifestyle photography + logo, so the typed hook, feature captions, wordmark, and
photos stay pixel-crisp. The only paid input is an optional music bed, produced upstream
by create-music-elevenlabs and passed to render.py --music.
The format (four beats, one continuous ~18s motion piece, 1080×1920)
- Search (0–5s) — a 3-column masonry grid of the brand's catalog behind a Pinterest
search bar; a believable phrase types in letter-by-letter. Side columns drift DOWN, the
middle column drifts UP (counter-parallax).
- Cards (5–7s) — 3 room/product cards slide in from the RIGHT and stack over a warm
blurred backdrop.
- Features (7–14.5s) — the TOP card physically expands (its box grows from the
stacked rect to full-screen, animating width/height — NOT
transform:scale, which would
stretch the image), then swipe-left → swipe-left through the SAME 3 rooms, each now
full-bleed with a caption. The 3 cards ARE the 3 features.
- End card (14.5–18s) — hero + wordmark + tagline + CTA on a warm background.
Scripts (all free / deterministic)
scripts/build_html.py --config config.json --out index.html — config → a single
self-contained HTML page exposing window.seek(tMs) (images base64-embedded).
scripts/capture.js --html index.html --out frames --fps 30 --duration 18000 — headless
Chromium frame-steps seek() to a PNG per frame (auto-discovers a cached Playwright
chromium, or pass --exe).
scripts/render.py --config config.json [--music bed.m4a] --out master.mp4 — orchestrates
build → capture → FFmpeg (muxes the bed with -map 0:v:0 -map 1:a:0 when --music is
given; $0 silent pass without it).
scripts/prep_assets.py crop in.jpg out.jpg / logo logo.jpg wordmark.png — the two
fixes this format needs almost every time: crop baked-in white L/R margins off heroes, and
key the white out of a black-on-white logo JPG to a transparent PNG.
See scripts/config.example.json for the full config shape (a real worked example).
Config contract (bound by the recipe/orchestrator at remix time)
canvas, hook, grid_cols (3 columns × 6 distinct tiles), rooms (exactly 3
{image, caption} — the cards AND the features), stack_bg (blurred warm backdrop),
endcard (hero, wordmark, tagline, cta, bg). Craft rules the renderer assumes the
inputs already honor: real brand assets only; 6 distinct tiles/column so the drift never
repeats; warm (never near-white) backdrops; cropped hero margins; transparent-bg wordmark;
captions/tagline/CTA are the brand's OWN approved copy (no invented claims).
Prereqs
node + playwright-core (or a cached Playwright chromium) and ffmpeg on PATH; python3
with Pillow (for prep_assets.py).
1---2name: render-search-grid3description: Render a 'search-grid' (Pinterest search-moodboard) video from a config — a real-DOM page with four continuous beats (masonry search grid + typing hook with counter-drift columns → 3 cards slide in from the right and stack → the top card box-grows to fullscreen then swipe-left ×2 through captioned feature shots → warm end card), frame-stepped via Chromium and encoded with FFmpeg — deterministic assembly, FREE (real brand photos + logo; the optional music bed comes from create-music-elevenlabs), so type/logo/photos stay pixel-crisp. Use for the search-grid format.4---5
6# render-search-grid
7
8Render the 'search-grid' format from a config. It is a **deterministic** assembler — no
9generative image/video, no AI-rendered text. Everything on screen is the brand's REAL
10product/lifestyle photography + logo, so the typed hook, feature captions, wordmark, and
11photos stay pixel-crisp. The **only** paid input is an optional music bed, produced upstream
12by `create-music-elevenlabs` and passed to `render.py --music`.
13
14## The format (four beats, one continuous ~18s motion piece, 1080×1920)
15
161. **Search** (0–5s) — a 3-column masonry grid of the brand's catalog behind a Pinterest
17 search bar; a believable phrase types in letter-by-letter. Side columns drift DOWN, the
18 middle column drifts UP (counter-parallax).
192. **Cards** (5–7s) — 3 room/product cards slide in from the RIGHT and stack over a warm
20 blurred backdrop.
213. **Features** (7–14.5s) — the TOP card **physically expands** (its box grows from the
22 stacked rect to full-screen, animating width/height — NOT `transform:scale`, which would
23 stretch the image), then **swipe-left → swipe-left** through the SAME 3 rooms, each now
24 full-bleed with a caption. The 3 cards ARE the 3 features.
254. **End card** (14.5–18s) — hero + wordmark + tagline + CTA on a warm background.
26
27## Scripts (all free / deterministic)
28
29- `scripts/build_html.py --config config.json --out index.html` — config → a single
30 self-contained HTML page exposing `window.seek(tMs)` (images base64-embedded).
31- `scripts/capture.js --html index.html --out frames --fps 30 --duration 18000` — headless
32 Chromium frame-steps `seek()` to a PNG per frame (auto-discovers a cached Playwright
33 chromium, or pass `--exe`).
34- `scripts/render.py --config config.json [--music bed.m4a] --out master.mp4` — orchestrates
35 build → capture → FFmpeg (muxes the bed with `-map 0:v:0 -map 1:a:0` when `--music` is
36 given; `$0` silent pass without it).
37- `scripts/prep_assets.py crop in.jpg out.jpg` / `logo logo.jpg wordmark.png` — the two
38 fixes this format needs almost every time: crop baked-in white L/R margins off heroes, and
39 key the white out of a black-on-white logo JPG to a transparent PNG.
40
41See `scripts/config.example.json` for the full config shape (a real worked example).
42
43## Config contract (bound by the recipe/orchestrator at remix time)
44
45`canvas`, `hook`, `grid_cols` (3 columns × 6 distinct tiles), `rooms` (exactly 3
46`{image, caption}` — the cards AND the features), `stack_bg` (blurred warm backdrop),
47`endcard` (`hero`, `wordmark`, `tagline`, `cta`, `bg`). Craft rules the renderer assumes the
48inputs already honor: real brand assets only; 6 distinct tiles/column so the drift never
49repeats; warm (never near-white) backdrops; cropped hero margins; transparent-bg wordmark;
50captions/tagline/CTA are the brand's OWN approved copy (no invented claims).
51
52## Prereqs
53
54`node` + `playwright-core` (or a cached Playwright chromium) and `ffmpeg` on PATH; `python3`
55with Pillow (for `prep_assets.py`).