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