Skip to main content

create-apple-notes-video-ad

Produce a 9:16 social-native ad recreating the iPhone Apple Notes typing experience — the note begins with 1–2 visible lines, then progressively types additional paragraphs character-by-character with per-keystroke iPhone keyboard SFX. One continuous Playwright recording, music bed underneath, brand end card with CTA. Assembled to a master MP4 plus a 9:16 social export.

Jump to install

Source facts

Repository
criptogus/agent-evolve-network
Last source activity
July 4, 2026 at 17:42
Detected SKILL.md language
English
Stars
289
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
18 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
create-apple-notes-video-ad
description
Produce a 9:16 social-native ad recreating the iPhone Apple Notes typing experience — the note begins with 1–2 visible lines, then progressively types additional paragraphs character-by-character with per-keystroke iPhone keyboard SFX. One continuous Playwright recording, music bed underneath, brand end card with CTA. Assembled to a master MP4 plus a 9:16 social export.
# create-apple-notes-video-ad Use this skill when an ad concept calls for an authentic Apple Notes typing reveal: a note appears with a pre-existing first line, then the user "types" the rest in front of you (more lines, more thoughts, a CTA) — punctuated by iPhone-keyboard ticks. Finishes on a brand end card. ## Purpose Produce a 9:16 social-native ad that recreates the iPhone Apple Notes typing experience: the note opens with 1–2 pre-existing lines, then progressively types additional paragraphs character-by-character with per-keystroke iPhone keyboard SFX, a music bed underneath, and a brand end card with CTA. The whole typing portion is captured as **one continuous Playwright recording** (no per-paragraph reloads, no scene-by-scene concatenation) so there are no micro-flickers — the only cut is a 300ms crossfade into the static end card. The driver in `scripts/record-master.template.js` renders the Apple Notes screen once via the `create-apple-notes-mockup` atom (`generateHtml()`), tags the typed paragraphs, then walks a deterministic seeded timing table on `requestAnimationFrame`, appending one character at a time to a growing `span.typed` while the yellow cursor follows and the note auto-scrolls to stay above the keyboard. The same table produces a keystroke-aligned SFX cue list (`master-typing.sfx.json`), and `scripts/stitch.template.sh` mixes the music bed + per-keystroke SFX (`kb-tick`/`kb-space`/`kb-return`), crossfades typing → end card, and exports a master MP4 (1180×2100) plus a clean 9:16 (1080×1920) social cut. Reach for this when the punchline is the **note itself** — a written-out promo code, a confession, a checklist, a calm "memo to self". For a back-and-forth chat reveal, use `create-imessage-video-ad` instead. ## Reference build Sample run lives at `tests/output/sample-run/` (generated by `bash tests/run-all.sh`). It uses the canonical script "Hello / This is a screen recording of me typing on apple notes app." with three typed paragraphs landing on `I'm not sure how th|` mid-word, ending on a "FREEDEMO" end card. 12s total. ## When to use - Reveals where the punchline is the **note itself** — a written-out promo code, a confession, a checklist, a thought spiral, a "memo to self". - Brands whose voice is calm/considered/personal (vs the rapid-fire iMessage angle). - Sustainable-pace ads — typing typically averages **~80–140ms per character**, which gives copy time to land. If your concept is a back-and-forth chat reveal, use `create-imessage-video-ad` instead. ## Composed Atoms - `create-apple-notes-mockup` — renders the light-mode Apple Notes HTML (status bar, title, body paragraphs, yellow cursor, autocorrect underline, smart quotes, floating toolbar). `record-master.template.js` calls its `generateHtml()` once with `show_keyboard: true`, then the molecule drives it character-by-character via Playwright. - `render-ios-keyboard` — supplies the iOS QWERTY keyboard chrome (suggestion bar + alpha/format rows) the Apple Notes screen shows during typing. It is static-only; the molecule owns the layout — the recorder anchors the keyboard at the shortened `VIEW_H = 2100` canvas (`bottom: 0`) and the auto-scroll math targets the keyboard's top edge (`.kbd-format`/`.kbd`) to keep the active cursor visible. - `stitch-videos-ffmpeg` — final concat + crossfade + audio mux. `stitch.template.sh` is adapted from this atom: it xfades typing → end card (300ms), layers the per-keystroke SFX cues from `master-typing.sfx.json`, mixes the music bed, and exports the master plus the 9:16 social cut. - (optional) ElevenLabs / Suno music generation OR a music asset already in the repo (the `music_bed_path` in `note.json`). ## Inputs 1. **`brief`** (required) — what the note says, where it pauses, what the brand sting at the end is. 2. **`output_folder`** — usually `<brand>/ads/video-NN-apple-notes-<slug>/`. 3. **`pre_typed_body`** — list of paragraphs already on screen at t=0 (1–2 lines is the typical sweet spot). 4. **`typed_body`** — list of paragraphs that get typed during the video. Each has `text`, `type_seconds`, `pre_pause_seconds`, optional `autocorrect_underline[]`. 5. **`end_card`** — `{wordmark, code, tagline, background_color, ink_color}`. The master `note.json` lives at `<ad>/notes/note.json`. Schema is in `examples/full-note.example.json`. ## Critical knowledge — read before producing your first ad These are mistakes that have already been made and fixed. Do not re-introduce them. ### 0. Use the real brand wordmark on the end card, never styled text Drop the official SVG at `assets/brand-logo.svg` and `scripts/render-end-card.template.js` injects it via the `<!--{{BRAND_LOGO_SVG}}-->` placeholder. CSS-styled lookalikes read as amateur even when the typeface is close — same lesson the imessage molecule has under its own #0. ### 1. Use real iPhone-keyboard click SFX, not generic typing sounds **The "Apple Notes feel" is 60% the SFX — but ONLY with real iPhone clicks.** Generic mechanical-keyboard clack ruins it, and so do the bundled placeholders: the files in `assets/sfx/{kb-tick,kb-space,kb-return}.mp3` are short noise-burst stand-ins for LOCAL PREVIEW ONLY. **A final delivery ships either real iPhone clicks (source them per `assets/sfx/README.md`) or silent (`--no-sfx`) — never the stand-ins, and never sounds you synthesized yourself** (fabricated ffmpeg noise bursts shipped once, 2026-06-11 Alitu, and read as static). When the real clicks can't be sourced (offline, LFS stubs), silent is the correct delivery, stated to the user. After swapping, **do NOT re-run the recorder** — only re-run `bash edits/stitch.sh`. The cue list (`clips/master-typing.sfx.json`) only references files by name; the new MP3s are picked up automatically. ### 2. Record the entire note as ONE continuous Playwright session Same architecture as `create-imessage-video-ad` — no per-paragraph reloads, no scene-by-scene concatenation. The driver walks a deterministic per-character timing table and appends one character at a time to a growing `.note-paragraph > span.typed`. The yellow cursor follows. ### 3. Pop-pending must remove from layout, not just hide If a typed paragraph occupies its full height at t=0 (zero text inside a fixed-height `<p>`), the note doesn't appear to "grow" as the user types. The driver leaves each typed paragraph at `min-height: 1.38em` so it always occupies one line — that mirrors how an empty paragraph behaves in real Apple Notes when the user presses Return. ### 4. Per-character timing must be deterministic (seeded RNG) The driver's `buildTimingTable()` uses an LCG seeded at `0xa5b1c2d3` so the same `note.json` always produces identical typing rhythm — which means the SFX cue list lines up exactly with the visual keystrokes. If you swap the seed (or use `Math.random()` directly), the audio will drift away from the visual after a few re-runs. ### 5. End card stays static — no Ken-Burns, no zoom-pan Same rule as the imessage molecule. Render the end-card PNG once, ffmpeg it to a fixed-frame MP4 via the bundled `render-end-card.template.js`. Crossfade typing → end card with a **300ms** `xfade`. ### 6. ffmpeg `amix` divides by N inputs by default If you mix the music + the silent base + ~80 keystroke SFX, every input gets divided by 82 unless you pass `normalize=0`. Audio sounds mysteriously quiet. Always: ``` amix=inputs=N:duration=first:dropout_transition=0:normalize=0 ``` Then take the mix through `volume=2.2,alimiter=limit=0.95`. Already in `scripts/stitch.template.sh`. ### 7. Music bed: ambient/lofi, 60Hz highpass, ~-11dB Keep the bed unobtrusive: `volume=0.28` plus `highpass=f=60` to clear room for the typing ticks. Fade out 1.5s before the end so the brand sting lands in (relative) silence. Same envelope as the imessage molecule. ### 8. Pacing: typing is not uniform Real human typing has rhythm — fast strings, slow strings, pauses on punctuation. The seeded LCG hands a 35% jitter to each character (`step = avg * (0.65..1.35)`). Most ads benefit from a **~0.5–0.8s pause before each new paragraph** — set via `typed_body[].pre_pause_seconds`. That breathing room is what makes the typing feel intentional and not robotic. ### 9. Smart punctuation is applied by the atom, not the driver The atom's `smartQuotes()` converts straight `'` `"` `...` to curly `'` `"` `…` before HTML escaping. The driver inherits this for free — typing `I'm` yields `I'm` in the output. If you skip the atom and write raw HTML, you'll lose this. ### 10. Autocorrect underlines fire per-paragraph, on the return event The driver fires autocorrect underlines on the **return event for each paragraph** — right when that paragraph finishes typing, before the cursor jumps to the next paragraph. This matches iOS behavior: the spelling layer runs at end-of-word/end-of-line, not mid-character. The end-of-run forEach still handles the last paragraph (which has no return event). Per-paragraph words go in `typed_body[].autocorrect_underline` (case-sensitive list). The driver looks them up in `window.__underlineMap` keyed by paragraph pid and calls `applyUnderline()` from the return handler. **Anti-pattern:** moving underline application to a single end-of-run loop. The operator sees the typed word complete without any underline for the full remaining seconds of the typing animation — reads as a bug. The per-event firing was a deliberate fix during the Bioma run; do not refactor back. ### 11. 9:16 delivery requires a 9:16 source canvas — never crop The iPhone's real screen is 19.5:9 (1180×2556). Social-native delivery is 9:16 (1080×1920). Every Y-crop value forces a destructive choice: lose the status bar, or lose the bottom of the keyboard, or split the loss across both. The fix is to record at 9:16 natively: `VIEW_H = 2100` in `record-master.template.js` plus a styleOverride that injects `html, body, .screen { height: ${VIEW_H}px !important; }` to override the atom's hardcoded 2556. The keyboard's `position: absolute; bottom: 0` then anchors to the shorter canvas, moving the entire keyboard up ~460px naturally. The `--export-9x16` step becomes a clean linear scale to 1080×1920 — no crop, no Y-offset to guess at. The atom's static screenshots (consumed by `apple-notes-skill-build.html`) still render at the real iPhone 1180×2556 — only the molecule's recording uses the shortened canvas. **When changing `VIEW_H`, all three files must move together** (the constant drifts otherwise and the xfade between typing video and end card fails silently): 1. `record-master.template.js` — `const VIEW_H = 2100` 2. `render-end-card.template.js` — `const VIEW_H = 2100` 3. `end-card.template.html` — `html, body { width: 1180px; height: 2100px; }` ## Workflow ### State 0 — Lock the script Write the full text of every paragraph (pre-typed + typed), the per-paragraph timing, and the CTA copy. Most "the ad is fine but feels off" feedback comes from the script, not the production — get it right here. A starter timing model: - `pre_typed_body` paragraph: ~3.0–4.5s on screen before typing starts - Each typed paragraph: 1.5–2.2s typing time + 0.6–1.0s pre-pause - Post-hold: 1.0s after the last character lands before crossfade ### State 1 — Build `notes/note.json` Match the schema in `examples/full-note.example.json`. The atom's note JSON schema is in `create-apple-notes-mockup/SKILL.md` — every field there is honored here, plus the molecule-specific `pre_typed_body / typed_body / post_hold_seconds / end_card / music_bed_path` fields. ### State 2 — Wire up the ad folder Standard 5-folder layout. Inside the ad folder: ``` <ad>/ notes/note.json assets/brand-logo.svg # optional but recommended audio/sfx/kb-tick.mp3 # copied from this skill's assets/sfx/ audio/sfx/kb-space.mp3 audio/sfx/kb-return.mp3 audio/music-bed.mp3 # optional clips/record-master.js # adapted from scripts/record-master.template.js clips/render-end-card.js # adapted from scripts/render-end-card.template.js clips/end-card.template.html # adapted from scripts/end-card.template.html edits/stitch.sh # adapted from scripts/stitch.template.sh ``` ### State 3 — Record + render + stitch ```bash # 1) Record the typing as one continuous video. NODE_PATH=<repo>/create-apple-notes-mockup/node_modules \ node clips/record-master.js # 2) Render the end card. NODE_PATH=<repo>/create-apple-notes-mockup/node_modules \ node clips/render-end-card.js # 3) Stitch. bash edits/stitch.sh # produces edits/master-final.mp4 at 1180×2556 bash edits/stitch.sh --export-9x16 # ALSO produces meta-upload/master-9x16-1080.mp4 ``` ### State 4 — Review Open the master in a player and watch start to finish. Re-time the typing by editing `notes/note.json` (the only file you'll touch for iterations) and re-running step 1 + step 3 — step 2 only needs to re-run if `end_card` changes. ### State 5 — Export variants `--export-9x16` scales the native 1180×2556 down to 1080×2340 and center-crops to 1080×1920, retaining the keyboard and the body. Adjust the vertical crop with `EXPORT_Y_CROP=<px>` if your spec needs different framing (default `260`, which keeps keyboard + body and trims status bar). ## Output ``` <ad>/ notes/note.json clips/ master-typing.mp4 # 1180×2556, no audio, ~5–15s typically master-typing.sfx.json # deterministic cue list end-card.png end-card.mp4 edits/ master-final.mp4 # 1180×2556 typing + xfade + end card, h.264 + AAC meta-upload/ master-9x16-1080.mp4 # 1080×1920 social export ``` ## Quality Checks Before declaring the ad shippable: - [ ] No micro-flicker between paragraphs — there should be NO scene-by-scene boundaries inside the typing portion. Only one cut: typing → end card (crossfade). - [ ] Every character in `typed_body` shows up on screen exactly once. - [ ] Autocorrect underlines appear on the right words after typing finishes, case-sensitive. - [ ] Smart curly quotes (`'`, `"`, `…`) render correctly — never `'`, `"`, `...`. - [ ] Cursor is yellow `#FFCC00`, sits at the end of the actively-typing paragraph, jumps when a new paragraph begins. - [ ] No Dynamic Island at the top. - [ ] Audio peaks at 0dB (`ffmpeg -af volumedetect`); SFX audible but not dominating any music bed. - [ ] End card is static, crossfade is 300ms. ## Failure Modes | Symptom | Cause | Fix | |---|---|---| | `cannot locate create-apple-notes-mockup/generate.js` | Ad folder is not under a tree that contains `skills/` within 8 levels up | Edit `resolveAtomGenerate()` in `record-master.js` to hardcode the path | | Typing animation runs but no characters appear | The driver couldn't find `p.note-paragraph[data-typed-pid]` — usually means `tagTypedParagraphs()` failed because the atom's HTML changed | Update the regex in `tagTypedParagraphs()` to match the atom's current paragraph markup | | SFX cues fire but no sound comes out of the final mp4 | LFS pointer files instead of audio at `assets/sfx/*` | `git lfs pull --include="skills/molecules/create-apple-notes-video-ad/assets/sfx/*"`. The goose-video CLI cache clones WITHOUT LFS, so stubs are guaranteed there — re-source per `assets/sfx/README.md` or ship `--no-sfx` | | Keyboard ticks sound like static / noise bursts | Shipped the bundled placeholder stand-ins, or synthesized substitute clicks | Placeholders are preview-only. Real iPhone clicks (`assets/sfx/README.md`) or `--no-sfx` silent — NEVER fabricate stand-in audio (live incident 2026-06-11, Alitu) | | Final video is silent | `amix` divided by N inputs | Confirm `normalize=0` is in the `amix=` filter | | 9:16 export crops out the body | `EXPORT_Y_CROP` too aggressive | Lower the value (e.g. `EXPORT_Y_CROP=180 bash edits/stitch.sh --export-9x16`) | | Cursor sits in the wrong paragraph at start | First typed paragraph's `pre_pause_seconds` is 0 | Set `pre_pause_seconds >= 0.4` so the cursor blink is legible before typing kicks in | | Music drowns out the ticks | Music volume too high | Lower `volume=0.28` to `0.22` in the stitch script's music filter | | Typing feels robotic / metronome-like | Seed jitter not applied | Confirm `buildTimingTable()` is using the seeded RNG, not `Math.random()` | | End card's wordmark renders as styled text instead of brand SVG | `assets/brand-logo.svg` is missing | Drop the official SVG there; `render-end-card.js` injects it automatically | ## Decision Rules - Use this molecule when the reveal **is the note itself** typed in front of the viewer (a written-out promo code, a confession, a checklist, a memo-to-self). For a back-and-forth chat reveal, use `create-imessage-video-ad`. For a single static Apple Notes screenshot (no typing animation), call the `create-apple-notes-mockup` atom directly. - Pick a calm/considered/personal brand voice for this format; the typing pace (~80–140ms/char) is deliberately slower than the rapid-fire iMessage angle, so copy has time to land. - Keep the typing portion as **one continuous Playwright recording** — never record per-paragraph and concat. The only cut in the whole ad is the 300ms `xfade` from typing → end card. - Per-character timing **must** come from the seeded LCG in `buildTimingTable()` (seed `0xa5b1c2d3`), never `Math.random()` — otherwise the SFX cue list drifts away from the visible keystrokes across re-runs. - Use real iPhone-keyboard click SFX (`assets/sfx/kb-{tick,space,return}.mp3`), never generic mechanical-keyboard sounds — the "Apple Notes feel" is ~60% the SFX. After swapping SFX, re-run **only** `bash edits/stitch.sh`; the cue list references files by name, so don't re-run the recorder. - Record at a 9:16-native canvas (`VIEW_H = 2100`), never crop a 19.5:9 iPhone screen — and move all three files (`record-master.template.js`, `render-end-card.template.js`, `end-card.template.html`) together whenever `VIEW_H` changes, or the typing↔end-card xfade fails silently.
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub