brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
videoMake trailers, music videos, cutscenes and page recordings for a Homie studio — a gameplay trailer captured from the studio's own running game and cut on the beat of its music; a recording of any web page while a script drives it (clicks, taps, keys, typing, scrolls, waits; Play and a few moves on the studio's own game page; a site walkthrough or product de…
Independent GitHub snapshot · Automated classification · Original documentation governs
Server rules keep real time. A trailer for a rules game uses live capture, not a
virtual browser clock: slowing the view cannot slow the server. Scripted inputs use
record, then edl/cut. Keep a real participant in the room while filming a wall/TV;
a spectator does not keep an empty room alive. Do not switch to a browser host for footage
presented as online play. Inspect the room and actual action, sound and held frames.
A studio is the folder with studio.json. Videos live in videos/<slug>/, listed in
videos/manifest.json; a published entry is a page at /videos/<slug>/ on the studio's site,
playing the 16:9 cut (the 9:16 cut on a portrait phone). Everything runs through
scripts/video.mjs in this skill's folder (Claude Code:
node "${CLAUDE_PLUGIN_ROOT}/skills/video/scripts/video.mjs" <command>), from inside the studio.
It needs Node 22, ffmpeg and Chrome; --json on every command.
Check disk before any capture or render (df -h .): keep 10 GB free. Raw frames are deleted
once encoded. Finish in this turn: start the local site with the app's background-task tool
(not nohup ... & inside a command, which dies with the command) and wait on it.
references/HONESTY.md has them in full. In short:
references/TRAILER.md.references/METHOD.md.references/RECORD.md.Both start with node <video.mjs> check (ffmpeg, Chrome, the studio; fal only if a key is set).
One command films the game, picks the shots and delivers three shapes. Server-rules
games record live picture and speaker audio; the older browser-game path below rebuilds
sound and steps a virtual clock. A rules trailer defaults to --view play: its idle
camera occupies a seat and keeps the room awake. Use --view tv only with another
participant connected in that room. Inspect actual action; an idle seat is not gameplay.
npm run dev # background task; the site at http://127.0.0.1:8787
node <video.mjs> trailer <slug> --game <id> --url http://127.0.0.1:8787 --seconds 45 --length 20 --title "<GAME NAME>" --end "Play free"
(npx --no-install homie-studio trailer <id> … is the same command, into videos/<id>-trailer/.)
The following virtual-clock options apply only to older browser games:
requestAnimationFrame, performance.now, Date, setTimeout, setInterval and its CSS and Web
animations all move on by exactly 1/30 s, then the frame is copied. No frame is ever held, however
heavy the game or slow the computer (heldFrames is 0; speed says how it compared with real time).sound skill's player (sound.js) logs every sound it
schedules when a recorder asks; the files it names are fetched from the game's own address and mixed
again on the film's clock (work/capture/sound.wav, and sfx.wav and music.wav apart). A game
whose sound does not go through sound.js (and does not push the same events) comes out silent,
and the result says so: use capture below for those, which records the speaker in real time.shotsPlayed says what
is in each shot. work/edl.json is the edit: change it and run cut <slug> --square.<slug>.mp4 (16:9), <slug>-square.mp4 (1:1), <slug>-vertical.mp4 (9:16), a poster, an end
card (and a title card with --title) in the game's look. --keep-capture edits the film already
made (another --length, another --end) without filming again. --steps <file> presses things
while it films (references/RECORD.md; only wait, waitFor, click, tap, key, keys, type, focus).<video>/<audio> elements, Web
Workers, and the network. The default page is the big screen of a live room: a relay keeps real
time, so when frames render slower than real time (speed well under 1) the room runs fast in the
film, and the result warns. Then render smaller (--scale 0.5) or film in real time with capture.
--page <path> films another page of the site (one whose game runs in the page itself is exact).sheet), check (qa.mjs on each file), and add. Detail: references/TRAILER.md.By hand, in real time, with the studio's own song as the bed:
npm run dev # background task; the site at http://127.0.0.1:8787
node <video.mjs> capture <slug> --game <id> --view play --url http://127.0.0.1:8787 --seconds 60
node <video.mjs> card <slug> --name title --text "<GAME NAME>" --sub "<one line>" --game <id> # the game's palette and font
node <video.mjs> card <slug> --name end --text "Play free" --sub "<site>/<id>/play" --small "Real gameplay, recorded live."
node <video.mjs> edl <slug> --length 30 --song <music slug> --title "<GAME NAME>"
node <video.mjs> cut <slug>
node <video.mjs> sync <slug>
node <video.mjs> sheet <slug> --in videos/<slug>/<slug>.mp4
npx --no-install homie-studio dev --stop
(it stops exactly this studio's server), never with a name pattern like pkill -f wrangler.npx --no-install homie-studio dev --port <free port> and use that address.capture records the game's big screen (/<id>/tv: a spectator in a live public room, bots in
empty seats) off a headless GPU Chrome for real seconds, with the game's own WebAudio sound on
the same clock (the browser is muted; nothing plays out loud). It presses nothing. The live
site works too (--url the studio's address); a local run keeps strangers out of the shot.
A heavy game paints few frames at 1920x1080 (it prints "fps from the page" and a note when that
is well under the film's 30): capture again with --scale 0.67 (a 1280x720 page, scaled up to
1920x1080 when it is encoded) or a lower --fps. One game measured 8 fps at 1080p and 39 at 720p.--song, from the music skill) or the game's sound alone.
No song yet: the sound skill synthesizes a theme for free (its bar grid cuts the trailer the same
way), or the music skill renders one with ElevenLabs (credits: ask).edl finds the song's bar lines, picks the busiest moments of the capture (motion, not guesses),
and writes work/edl.json: a title card of one bar, shots of one bar each, the end card on what
is left. Title and end cards made with card … --game <id> wear the game's palette and display font
(games/<id>/style.json, the style skill's decisions). Every cut lands on a bar line. Read it and change it: order, which moments, where the
song starts (--bed-from-bar).cut renders both deliveries: 16:9 (1920x1080) and 9:16 (1080x1920, the whole game frame over a
blurred fill, never a crop that hides the action), and 1:1 (1080x1080) with --square; 30 fps, the
song and the game's sound mixed, -14 LUFS, faststart, and a poster frame. Every cut is on the frame
nearest its beat (segments are counted in frames, so cuts never drift from the music: cuts lists
each one's frame), and the picture is yuv420p, limited range, converted and tagged BT.709 (a
capture's frames are full range; cut fails if the file does not read back that way).sync checks every cut: the picture must change within one frame of the sound's onset.
sheet makes a contact sheet: open it and look before anyone else sees the video.node <skill folder>/scripts/qa.mjs videos/<slug>/<slug>.mp4 checks the delivered file itself: every
frame decodes, phone-safe encoding, faststart, no black or frozen stretches, loudness and true peak,
under the site's 25 MiB. Fix every FAIL before add. Delivery, capture and honesty traps:
references/DELIVERY.md.Use an already connected fal account for paid generation; never ask the person to find or set a key.
The local paid script requires an existing FAL_KEY in its environment. If none is available, continue
with local assets or captured footage and explain that this paid-script connection is not ready.
Do not make account configuration a prerequisite for a live studio. fal's own MCP server is the way to find models and read their input schemas and prices
(search_models, recommend_model, get_model_schema, get_pricing): use it rather than memory,
because video endpoints retire and reprice monthly. Not connected? Offer it, and the person signs in
on fal's own page (no key): in Claude Code claude mcp add --transport http --scope user fal https://mcp.fal.ai/mcp-relay, then /mcp; in Codex codex mcp add fal --url https://mcp.fal.ai/mcp-relay, then codex mcp login fal. Paid calls go through video.mjs gen,
never the MCP's run_model or submit_job: gen prices, caps, receipts and resumes; the MCP call
does none of that (the Homie mod in Claude Code, and Homie's hooks in Codex, hold one as a call whose cost cannot be read first).
The budget, before the first call. Plan the shots, price one of each kind, add them up, and ask the person for a cap in dollars:
node <video.mjs> price --model <fal model> --input <input.json> # free: fal's own unit price x this input
node <video.mjs> budget <slug> --cap <dollars they agreed to>
node <video.mjs> gen <slug> --model <model> --input <input.json> --out work/<file> --dry-run
node <video.mjs> gen <slug> --model <model> --input <input.json> --out work/<file> --yes
gen refuses with no budget, refuses past the cap, and asks again without --yes. The receipt is
written the moment fal accepts the job (videos/<slug>/budget.json, videos/receipts.jsonl).
Running the same gen again resumes the job from <out>.request; it never pays twice. Strings
"@file:<path>" in the input are uploaded to fal's storage first (free). A model whose billing
unit the script cannot count is refused, not guessed: price it by hand from the model page. Spend
the riskiest shot first (the pilot), look at it, then the rest.
The method (references/METHOD.md has every step):
videos/<slug>/STYLE.md): palette, line, type, what the look risks.node <video.mjs> grid <slug> --song <music slug> gives the
song's beats, bars and every sung word's time; each shot starts and ends on a bar line.slice), so mouths and hits land on the music. 480p or 720p is
enough under a draw-over.lag <slug> --base <clip> --ref <cut>. A clip that sings its
reference plays it back at 0 ms; anything else is regenerated or shifted.film init <slug>, film frames <slug> --base <clip> --id <shot>,
edit film/shots.json and film/look.js, then film render <slug> (and --mode v for 9:16).
Every frame is a function of time, rendered frame-exactly in a headless browser.words <slug> --in <cut> (a frame at every sung word, labelled) and
sync <slug> --in <cut> --at <hit times>. Fix and re-render until every word and hit lands.sheet <slug> --in <cut> --every 1. Look at every cell. Then
scripts/qa.mjs <cut> on each delivery (references/DELIVERY.md: continuity, faces, green screens).npm run dev # background task; the site at http://127.0.0.1:8787
node <video.mjs> record <slug> --steps <steps.json> --url http://127.0.0.1:8787 [--device phone] [--name <take>]
node <video.mjs> sheet <slug> --in videos/<slug>/work/record/recording.mp4
click, tap, drag,
hover, key, keys, type, scroll, goto, wait, and waitFor a selector, a text or a state
("js"), in the page or in the game's own frame ("frame": "game"). Any step can carry a
"caption". references/examples/studio-play.json records a studio's own game from its landing:
Play, then the arrow keys; studio-play-phone.json the same on a phone with the game's touch stick.
Both are tested against a real studio. Every step: references/RECORD.md. A Game Lab's side by side
(New beside Today, slowed down, stepped through its phases) is a page too:
the lab skill's references/record-lab.json records it from the lab's own address.--no-cursor drops them); the take's honesty line says so.recording.json has each step's second, what its frame received (keys, presses, touches: delivered,
not judged), the page's and the game's real frame rates and the renderer. Under 20 fps or on a
software renderer it warns: that take shows a slow computer, not the page.sheet and look; add <slug> --file videos/<slug>/work/record/recording.mp4 --kind clip --title "…" (copy its captions.vtt to videos/<slug>/captions.vtt first) and publish. Or cut
it into a trailer like any capture.node <skill folder>/scripts/record-page.mjs --steps <steps.json> --out <folder>
records any address (it needs Chrome, ffmpeg and puppeteer-core).A published trailer with --for-game <id> plays, muted, in the hero of that game's landing page when the
game has no hero loop of its own, and the landing links to it ("Watch the trailer"). The best hero is a
short silent loop of real play in games/<id>/hero/ (the game skill, "Its landing page", cuts it from a
capture).
node <video.mjs> add <slug> --title "<Title>" --blurb "<one line>" --kind trailer|music-video|cutscene [--for-game <id>] [--for-song <music slug>] --publish
node <video.mjs> publish <slug>
A studio needs no storage for this: the site serves each file itself, up to 25 MiB a file, and
cut and film render hold their deliveries under that (a longer video gets a lower bitrate).
Bigger masters stay in the studio folder. A studio with storage (storage add: R2 on the studio's
own Cloudflare account, which asks the person for a payment method first) keeps its big media there
by default: the deploy uploads each delivery (over 1 MiB, or left out of git), reads it back and
checks it by SHA-256 before the site stops carrying it, and the page plays it at the same address.
Cuts can then run at 12 Mbit/s. R2 has no egress fees; storage is free up to 10 GB-month, then
US$0.015 per GB-month.
add writes the manifest entry: the 16:9 cut the player plays, the 9:16 cut, the poster,
captions if there is a captions.vtt, the credits (the models used, the song's credits), the
honesty line from the capture (or the recording), and the money spent. publish redeploys (with
storage, the deploy moves the big files to R2 first, each checked by SHA-256), and checks the page
(200) and the video (a byte range, 206: phones seek with them) at its address. A studio not online
yet: the publish skill first.
The page link, the length, both files, what it cost (from the receipts) against the cap, what
is generated and what is captured, and what the sync check and the contact sheet showed. Commit
videos/ (big media stays out of git; the manifest, edl, shot list, film code and receipts go in).