$npx -y skills add gooseworks-ai/goose-skills --skill render-chatgpt-chatAssemble a ChatGPT chat-reveal video ad from a thread + timeline JSON — one continuous Playwright recording of a ChatGPT mobile chat (user types with the iOS keyboard up → taps send → keyboard slides down + header cluster swaps in one beat → one gray loading dot → the assistant a
| 1 | # render-chatgpt-chat |
| 2 | |
| 3 | The free renderer for the **chatgpt-chat** video ad format — the "I just asked |
| 4 | ChatGPT…" creative, where someone asks ChatGPT a question and the *streamed |
| 5 | assistant answer is the punchline* (the brand surfacing as the natural response). |
| 6 | Deterministic Playwright + ffmpeg assembly; no generative video of the UI, so the |
| 7 | bubble text and streamed answer stay pixel-crisp. |
| 8 | |
| 9 | This is the **ChatGPT sibling of `render-imessage-chat`**. Reach for this one when |
| 10 | ChatGPT is the more credible host for the answer; reach for iMessage when the |
| 11 | punchline is a peer's reaction in a DM. The template recipe (DB) supplies the |
| 12 | per-brand `thread` + `timeline` + `end_card` config and gates the paid music call |
| 13 | (music bed → `create-music-elevenlabs`) to its own capability. |
| 14 | |
| 15 | ## What it renders |
| 16 | |
| 17 | One continuous take — never scene-by-scene (every reload flickers): |
| 18 | |
| 19 | 1. **User types** in the composer with the iOS keyboard up (`composer-type`). |
| 20 | 2. **Send-tap is ONE beat** — the user bubble pops, the keyboard slides down, and |
| 21 | the header right-cluster swaps (`personPlus/dottedCircle` → `edit/more`) all on |
| 22 | the same `t`. Never sequence them across frames. |
| 23 | 3. **One gray loading dot** holds ~500ms (never three — three reads as iMessage |
| 24 | typing, wrong app), silently (no SFX on the dot). |
| 25 | 4. **The assistant answer streams in word-by-word** (`stream-words`, ~7 wps) with a |
| 26 | soft opacity ramp; the conversation auto-scrolls to keep it in view. |
| 27 | 5. **Crossfade to a designed end card** (wordmark + ⭐ proof row + trust trio + CTA |
| 28 | pill) and **mux a ducked music bed** → master MP4. |
| 29 | |
| 30 | The chat records at the ChatGPT-native **~9:19.5 (default 750×1624)** to match a real |
| 31 | iPhone screen recording. **Never stretch the chat to a different aspect ratio** — the |
| 32 | end card is scaled-to-fit + padded to the chat's dimensions in stitch, so the chat |
| 33 | is never touched. |
| 34 | |
| 35 | ## Run |
| 36 | |
| 37 | ```bash |
| 38 | cd scripts && npm install # once — installs Playwright |
| 39 | npx playwright install chromium # once |
| 40 | node record-chat.js --config config.json --out-dir <work> # → master-chat.mp4 + .sfx.json |
| 41 | node render-end-card.js --config config.json --out-dir <work> # → scene-end-endcard.mp4 |
| 42 | bash stitch.sh --chat <work>/master-chat.mp4 --end <work>/scene-end-endcard.mp4 \ |
| 43 | --sfx <work>/master-chat.sfx.json --out <work>/master-final.mp4 \ |
| 44 | --pad-color "#ffffff" [--music <work>/music-bed.mp3] [--also-1x1] |
| 45 | ``` |
| 46 | |
| 47 | 1. **`record-chat.js`** — reads `config.json` (`thread` + `timeline` + geometry), |
| 48 | renders the bundled `create-chatgpt-mockup` HTML once with every message |
| 49 | pending, walks the timeline on `requestAnimationFrame` inside the page, records |
| 50 | it as one continuous MP4, and emits the deterministic SFX cue list. |
| 51 | 2. **`render-end-card.js`** — fills `end-card.template.html` from `config.end_card` |
| 52 | (wordmark/`logo_svg`, stars, proof, trust trio, CTA, colors) → still MP4. This is |
| 53 | the SAME generic end card as `render-imessage-chat` (copied verbatim). |
| 54 | 3. **`stitch.sh`** — normalizes the end card to the chat's dimensions, crossfades |
| 55 | chat → end card, layers the subliminal ChatGPT SFX, optionally ducks a music bed |
| 56 | under it, and optionally derives a 1:1 crop. All FREE ffmpeg. Pass |
| 57 | `--pad-color` = `end_card.bg` (default `#ffffff`, ChatGPT light mode) so the pad |
| 58 | under the end card is seamless. |
| 59 | |
| 60 | ## The chat body: bundled create-chatgpt-mockup |
| 61 | |
| 62 | The ChatGPT chat HTML comes from **`create-chatgpt-mockup`** (its `generate.js` + |
| 63 | `templates/` produce the light-mode ChatGPT iOS HTML — status bar, header, message |
| 64 | rows, streaming word-spans, composer, and the inline iOS keyboard). Those files are |
| 65 | **bundled into `scripts/mockup/`** so this capability renders the chat body |
| 66 | **standalone** — no sibling fetch of `create-chatgpt-mockup` is required. `record-chat.js` |
| 67 | does `require('./mockup/generate.js')`. |
| 68 | |
| 69 | The keyboard is inlined by the mockup (`renderKeyboard`) — no separate keyboard atom. |
| 70 | |
| 71 | ## Timeline events (consumed by record-chat.js) |
| 72 | |
| 73 | | Kind | Meaning | |
| 74 | |---|---| |
| 75 | | `composer-type` | `{ text, dur_sec }` — type into the composer. SFX = one key-tap per word. | |
| 76 | | `composer-clear` | Wipe the composer instantly (fire at send-tap). | |
| 77 | | `keyboard-show` / `keyboard-hide` | Slide the iOS keyboard up / down. | |
| 78 | | `send-tap` | Pulse the send button. SFX = send-tap. | |
| 79 | | `pop |