Skip to content

feat(vapor): promo video pipeline for the Pocket Vapor announcement#151

Draft
doodlewind wants to merge 3 commits into
mainfrom
feat/vapor-promo
Draft

feat(vapor): promo video pipeline for the Pocket Vapor announcement#151
doodlewind wants to merge 3 commits into
mainfrom
feat/vapor-promo

Conversation

@doodlewind

@doodlewind doodlewind commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

What

A fully reproducible pipeline that renders the Pocket Vapor promo video for X, end to end, from the repo itself:

bun vapor/scripts/promo/capture.ts   # 882 synchronized gameplay frames per console
bun vapor/scripts/promo/music.ts     # original chiptune/electro bed, synthesized from scratch
bun vapor/scripts/promo/promo.ts     # final cut -> dist/vapor/promo/pocket-vapor.mp4 (1080p60, 46.9 s)

How

  • mgba_runner gains two scenario commands: K <mask> (set held keys) and M <n> <prefix> (run n frames, dump each as PPM). Frame-exact, deterministic.
  • capture.ts replays one 31-press choreography (toggle, filters, live-typing "HN" in the glyph editor, save, toggle done) on GBA, GB, and NES with a shared 24-frames-per-press schedule, so the side-by-side composite stays in lockstep.
  • music.ts synthesizes the 125 BPM A-minor bed (square arps, triangle bass, noise hats, sine kick) directly to WAV; the bed is exactly as long as the timeline and the lead melody enters at bar 10.
  • promo.ts draws the timeline on the PocketJS brand field with @napi-rs/canvas and streams raw RGBA into ffmpeg: title ("Reactive JavaScript in 2 KB of RAM") → the real Vue component typing itself out → you-write/it-ships TS-vs-generated-C split → three consoles playing live at 19.2 s, ROM sizes chip-labelled under each screen, button read-out driven by the same press schedule → receipts (940 bytes of compiled state, 218 KB Vue runtime, 2 KB NES work RAM, 7,264 assertions) → close. A PocketJS logo badge rides every frame; no appended end card.
  • Demo entry is beat-locked: gameplay starts on soundtrack bar 10, the exact bar the lead melody enters.

Gotcha fixed along the way: Bun FileSink.write() returns a Promise under backpressure; not awaiting it truncates the rawvideo stream mid-frame (ffmpeg: packet size < expected frame_size, then EPIPE). Every frame write/flush is now awaited.

Every number shown in the video is cross-checked against the blog post and vapor/README.md. bun test vapor/tests: 41 pass, 7,264 expect() calls.

🤖 Generated with Claude Code

doodlewind and others added 3 commits July 23, 2026 06:30
…ynthesized chiptune, brand-styled composite

- mgba_runner: K (set held keys) and M (movie frame dump) commands for
  frame-exact footage capture
- capture.ts: one 31-press choreography replayed on GBA/GB/NES in a
  shared 24-frames-per-press schedule, every frame dumped as PPM
- music.ts: original 125 BPM A-minor chiptune/electro bed synthesized
  from scratch (square arps, triangle bass, noise hats, sine kick),
  structured to the storyboard
- promo.ts: 1080p60 timeline renderer (title, live-typed component,
  TS-to-C split, cartridge sizes, lockstep three-console gameplay with
  button read-out, receipts, close) streamed raw into ffmpeg; finished
  with the pocketjs-video-outro brand card in X-compatible mode

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ps, logo badge, new tagline

- drop the cartridge-sizes interstitial; ROM sizes now sit under each
  console in the gameplay segment, demo starts at 19.2 s (was 23 s)
- gameplay entry stays locked to the soundtrack: lead melody moved to
  bar 10 = 19.2 s, breakdown/resolve retimed, bed now exactly 46.9 s
- PocketJS logo badge on every frame; no appended end card
- title tagline: Reactive JavaScript in 2 KB of RAM
- fix rawvideo truncation: await FileSink.write/flush per frame — an
  unawaited backpressure Promise dropped bytes mid-frame (EPIPE)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oolchain jargon line

- the badge previously drew assets/images/logo.png (the launcher's play
  icon), not the brand mark; redraw the site's 32x32 lens/viewfinder SVG
  (rounded-rect edge, lens dot, two bars, silver gradients) in canvas
  vectors so it stays crisp at any size
- remove the 'no JS engine on device · arm-none-eabi-gcc · sdcc · cc65'
  footer from the TS-vs-C split page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant