promo-video
Skill5 starsChanged 21 days agoMITView on GitHub
Make short promotional videos as code — product spots, feature demos, launch and game trailers — with HyperFrames (HTML + GSAP rendered to MP4). Covers the brief and facts check, a 15–35 s story shape, a runnable template with one camera and a pure frame(t) driver, ElevenLabs voiceover with word timings, burned-in captions checked against Netflix rules, synthesized or engine-rendered sound effects placed from a cue log, a loudness-checked mix that survives the AAC encode, recorded browser/canvas gameplay, recreated product UI, review frames, and web/social delivery. Use when the user asks for a promo, spot, teaser, trailer, demo or explainer video, or a social video of a product or game.
- Reads credentials
19 other projects use a version of this.
Adopted by
Most recently taken up by
- CatCatUncle/openworkbuddytheir own version, first seen Nov 5, 2026
- chessunyan/renoise-skillstheir own version, first seen Nov 1, 2026
- thesleax/video-kittheir own version, first seen Nov 1, 2026
- qxryz/workflowgeneratortheir own version, first seen Oct 25, 2026
- prajwal-svm/product-promo-harnesstheir own version, first seen Oct 20, 2026
- ryokun6/ryostheir own version, first seen Oct 17, 2026
Other versions19 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 19
- ryokun6/ryos★ 1.3ksame name
- pedronauck/skills★ 649same name
- CatCatUncle/openworkbuddy★ 398same name
- OpusGameLabs/game-creator★ 360same name
- AKCodez/promo-video-skill★ 53same name
- qxryz/workflowgenerator★ 48same name
- smit-vanani/claude-promo-video★ 14same name
- iart-ai/ecommerce-video-skills★ 8same name
- divakarwl12/skills★ 4same name
- FelipeAngeli/skills★ 2same name
9 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository19 other files it gives its agents.
- code-reviewSkill
- context-guardSkill
- graylog-searchSkill
- html-slide-deckSkill
- image-generationSkill
- implement-specSkill
- jiraSkill
- lighthouseSkill
- mac-maintenanceSkill
- mysql-schema-designSkill
- phaser-gamedevSkill
- pixelart-cleanupSkill
- pixellab-apiSkill
- pixellabSkill
- product-hunt-launchSkill
- publish-report-artifactSkill
- seo-blogSkill
- seo-optimizationSkill
- usage-statuslineSkill
The file
We split this skill into the rules it gives an agent
12 rules. 0 are common, 5 are distinctive, and 7 are very specific to this project.
0
Common
Most of the 16 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 57
Brief and facts: Before writing a word of script, check every product name, number and claim against the product's own sources (site, docs, …
- Very specific: line 97
Every beat has its own idea: No two beats in a row with the same layout and the same move.
- Distinctive: line 101
Picture follows the voice: Every visual change lands on the word that names it, or on the beat in a music-led piece.
- Very specific: line 129
Home: every project is $PROMO_VIDEO_HOME/<slug>/ (default ~/promo-videos/<slug>/), one per video.
- Very specific: line 147
A local render is not a publication: Never run hyperframes publish, upload, or post without the user's explicit go-ahead for that …
- Very specific: line 153
Do not widen either to make a page load.
- Distinctive: line 155
No invented facts: No player counts, revenue, ratings, testimonials, endorsements or competitor claims you cannot source.
- Distinctive: line 158
Other brands: When a scene borrows another product's look (a chat, an editor), take tokens and layout from real screenshots and source, but …
- Very specific: line 169
Every font family needs an @font-face with a vendored file, or lint fails with font_family_without_font_face.
- Very specific: line 171
Every <video> needs an id, or the renderer freezes it (media_missing_id).
- Distinctive: line 176
Do not measure text during the build: fonts may still be settling.
- Very specific: line 182
Do not silence check with data-layout-ignore on content layers: with everything ignored it reports "Timeline did not advance" and checks …
“Common” is measured: kept by at least 6 in 10 of the 16 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Promo video as code
A video is a web page that HyperFrames renders frame by frame: an index.html composition, one paused GSAP
timeline, and a soundtrack built from stems. Everything is a pure function of time, so a render is reproducible and
a one-word change is a re-render, not a re-edit.
The method comes from four finished videos: two 30–33 s product spots with voiceover, a 36 s how-to for one module of a product, and a 30 s music-led game trailer with recorded gameplay. Pick the shape that fits:
| Shape | Carried by | Mix targets (integrated) |
| Voiceover spot: one story told by a voice, product UI doing the work, captions | voice | voice −14 LUFS, music bed −36, SFX −26, mix −12 |
| Music-led trailer: supers on a beat grid, footage, no voice | music | music −16, SFX −22, mix −14 |
Both: true peak ≤ −1 dBTP after a test AAC encode, 1920×1080, 60 fps, an end card held about 2 s.
What ships with the skill
| Path | Does |
scripts/new-project.sh <slug> | creates ~/promo-videos/<slug> (or $PROMO_VIDEO_HOME) from the template and scripts, vendors GSAP 3.14.2 and Inter, starts README.md and facts.md; refuses inside a git repository |
assets/index.html, assets/scene.js | the template: an 8 s working composition, hook → product does one task (typing, cursor, click, result) → end card, with one camera, frame(t), a cue log and a caption layer |
assets/script.json | example voiceover script (say, group, chunks, gap_after, burn) |
scripts/probe.mjs | serves the project, reports errors, --cues exports the SFX cue log, --shots saves review frames (videos seeked) |
scripts/tts.mjs | ElevenLabs take with character timestamps; --line re-takes one line; --dry-run |
scripts/voice-test.mjs | the same script lines read by 3–4 candidate voices, for picking by ear; prints the credits first |
scripts/build_vo.py | splits the take into lines, re-spaces them, masters the voice, writes word timings |
scripts/build_captions.py | caption chunks + .srt/.vtt; fails the build on Netflix rule breaks |
scripts/sfx.py | 17 deterministic synthesized UI sounds (pop, click, tick, whoosh, ding, …) |
scripts/build_mix.py | voice + back-timed music + SFX from cues → audio/mix.wav; fails if the AAC true peak is above −1 dBTP |
scripts/record-canvas.mjs | records a live canvas + its WebAudio from a scripted input plan |
scripts/cut_clips.py | trims, fixes the frame rate, crops and scales recordings into composition clips |
scripts/deliver.sh | versioned web encodes (H.264 MP4, VP9 WebM, poster) |
scripts/audiolib.py, scripts/browser.mjs | shared helpers (ffmpeg/numpy audio, headless Chrome lookup) |
Requirements: Node 22+, Python 3 with numpy, ffmpeg with libx264, libvpx and libopus. HyperFrames runs through
npx; the browser scripts reuse the puppeteer-core it installs and its chrome-headless-shell
(npx -y hyperframes@0.8.111 browser ensure downloads one). Tested with HyperFrames 0.8.111, GSAP 3.14.2,
ffmpeg 8.1, Python 3.9, numpy 2.0.
Workflow
- Brief and facts. Before writing a word of script, check every product name, number and claim against the
product's own sources (site, docs, code) and keep a facts file next to the project: claim → source, a
made up on screen list (example data, names, compressed timing), and a don't claim list. When sources
disagree, leave the number out or round it to something that stays true ("40+ games"). Open every URL of the
product that will appear on screen with
curl -L; never open a third party's (competitors, shops, links in example data). Made-up URLs and e-mails useexample.comor a.testdomain only, never a real-looking one. Seereferences/story.md. - Ask only the decisions that are the user's: where the video will live (homepage with sound, feed autoplay,
ad), voice or music-led, which claims to make, permission to use generated or third-party assets, any real person's
name on screen. Decide the rest yourself and write the decisions down. For a client, pitch 3–4 concepts and
get one picked before any script exists (
references/story.md, "Concepts before production"). - Script as a beat table: time, picture, super/caption, sound. Hook in the first 3 s on the product's own
surface, brand by 5 s, one task end to end, one CTA. Keep it working with the sound off.
Get it approved before anything paid runs (a voice take, a voice test, licensed music): show the spoken
lines as they will be read and the cost (
tts.mjs --dry-runandvoice-test.mjs --dry-runprint characters and credits). Revise until the user says go; every rewrite after the take costs a re-take. - Start the project with
bash <skill>/scripts/new-project.sh <slug>and run the build loop once on the untouched template, so a broken toolchain shows up before your content does. Keep to the layout and names in "Projects and files" below. - Assets: record footage (
references/footage.md), take clean screenshots, rebuild UI from the product's CSS and real data, vendor fonts and the official logo. Draw illustrations yourself. - Voiceover (voice spots): offer 3–4 candidate voices and let the user pick from
voice-test.mjssamples of the real script, thentts.mjs,build_vo.py,build_captions.py. The printed total becomesdata-duration. Key every visual beat to word times (references/audio.md). - Compose: replace the template's beats in the project's
index.htmlandscene.js, followingreferences/composition.md. Log a cue for every sound withcue(t, type, {gain}). - Sound:
probe.mjs --cues, thenbuild_mix.py. Report the measured loudness, not the targets. - Review: frames every 0.5–1 s with
probe.mjs --shots, thenlintandcheck, then the render, then frames extracted from the final MP4. Look at them;checksamples and can miss a shot. Hold the frames against "What a good result looks like" below and fix what fails before showing the user. - Deliver and document: render, verify with ffprobe and ebur128, encode for the destination
(
references/delivery.md), and write the project README: sources and licenses, what is made up, measured loudness, what it cost, posting notes, rebuild steps.
What a good result looks like
The tools above make a video correct. These make it worth watching; check every review pass against them.
- The real thing, not a symbol for it. The product's own screen, output, map or document rather than an icon that stands for it. When the point is a sentence on a page, show that page and mark the sentence, so the viewer sees it really says so.
- Every beat has its own idea. No two beats in a row with the same layout and the same move. One main
transition style, one accent at the climax (
references/composition.md, "Motion"). - Nothing stands still. Something moves in every shot: a slow camera drift, the product doing its job, a counter, a cursor. A frame that holds for more than about 1.5 s outside the end card reads as a slide.
- Picture follows the voice. Every visual change lands on the word that names it, or on the beat in a music-led piece. A change that comes half a second late reads as a mistake.
- Lines written to be heard. Whole sentences with verbs, one thought per line, numbers written as they are said. A keyword staccato ("Fast. Simple. Secure.") sounds read off a slide.
- It works with the sound off. Captions and supers carry the story; the video still makes sense muted.
Build loop
export HYPERFRAMES_NO_TELEMETRY=1 HYPERFRAMES_SKIP_SKILLS=1 # no telemetry, no skills update check
HF="npx -y hyperframes@0.8.111" # pin the version you tested with
node scripts/tts.mjs && python3 scripts/build_vo.py && python3 scripts/build_captions.py # voice spots only
node scripts/probe.mjs --cues audio/cues.json # SFX cue log from the scene
python3 scripts/build_mix.py # → audio/mix.wav, audio/loudness.json
node scripts/probe.mjs --shots every:0.5 --out review/frames
$HF lint . # 0 errors
$HF check --samples 24 --timeout 15000 --caption-zone "x0=0;y0=.86;x1=1;y1=1" .
$HF render --quality delivery --fps 60 --output renders/<slug>-v1.mp4 . # add --video-frame-format png for pixel art / UI footageRerun from the step you changed: a scene edit that moves a cue needs --cues and build_mix.py again; a script
edit needs the whole voice chain. The render embeds audio/mix.wav as it is on disk.
Projects and files
Details and the migration steps are in references/projects.md.
- Home: every project is
$PROMO_VIDEO_HOME/<slug>/(default~/promo-videos/<slug>/), one per video. All of its languages and formats stay in that one project. - Never committed: projects live outside every git repository, media and composition code alike. Reusable code goes into this skill, not into another project or a product repository.
- Fixed layout:
README.md,facts.md,script.md,index.html,assets/,audio/,scripts/, optionalresearch/,capture/andconcepts/, thenreview/(scratch),renders/(deliverables) andarchive/(rejected versions). No other top-level directories. - Render names:
renders/<slug>[-<lang>][-<variant>]-v<N>.mp4, sidecar captions with the same stem. A file that has been sent or posted is never overwritten; the next cut is the next version. - Publication records stay with the system that publishes, pinned to the render's SHA-256. The project README lists each live post's date, platform and URL.
Rules that are not negotiable
- Keys stay in
.env.tts.mjsreadsELEVENLABS_API_KEYfrom the environment or a.envfile and never prints it. Never put a key in a prompt, a script, a log or a README. Do not read.env*, credentials or keys in the product's repository while researching it. - Nothing from a video project is committed, and no project is started inside a repository.
- A local render is not a publication. Never run
hyperframes publish, upload, or post without the user's explicit go-ahead for that destination. Record a platform, date and URL only after the post is live and you have opened it. - Network stays narrow. The skill reaches jsDelivr (GSAP and Inter, checked against SHA-256 in
new-project.sh), the ElevenLabs API, and the product's own domains.probe.mjsblocks every request that is not the local composition;record-canvas.mjsonly loads the plan URL's host plus the hosts listed in itsallow. Do not widen either to make a page load; add a host toallowonly after the user confirms it belongs to the product. Made-up addresses useexample.comor.test(references/story.md). - No invented facts. No player counts, revenue, ratings, testimonials, endorsements or competitor claims you cannot source. Example data is labelled as made up in the README. Do not put words in a third party's mouth (an AI assistant's answer, a customer quote); show placeholders instead.
- Other brands. When a scene borrows another product's look (a chat, an editor), take tokens and layout from real screenshots and source, but leave out its logo, name and UI chrome unless the user has the rights. Keep legal disclaimers out of the picture unless asked; put them in the README and the post text.
- Determinism. No
Math.random, no clocks, no infinite repeats, no network at render time. Seed every RNG. - Accessibility. At most 3 flashes in any one second (WCAG 2.3.1), no saturated red flashes; captions or supers carry the story for viewers without sound.
Gotchas that cost the most time
- Build the timeline inside
document.fonts.ready.then(build). A synchronous build, or one gated ondocument.fonts.load(), rendered blank frames. - Every font family needs an
@font-facewith a vendored file, orlintfails withfont_family_without_font_face. Subset fonts miss glyphs (Google'slatinsubset has no → or ✓): draw those as SVG. - Every
<video>needs anid, or the renderer freezes it (media_missing_id). Write videos as static markup: ones created from JS may not be discovered. HyperFrames controls the opacity of active clips, so animate a wrapper<div>. Overlapping clips need differentdata-track-indexvalues. - Hard-code the composition id and duration in
scene.js. Reading them from the root at script time threw undercheck. - Do not measure text during the build: fonts may still be settling. Give elements explicit absolute layout.
- Pass
immediateRender: falseto everyfromToafter the first one on the same element and property. Otherwise itsfromvalues are applied when the timeline is built and show before the tween starts. - HyperFrames turns the whole soundtrack down when the true peak after its AAC encode is above −1 dBTP. One sibilant
cost a render 4.4 dB. A 16 kHz low-pass before the limiter fixes it;
build_mix.pydoes that and checks. render --resolutiononly supersamples. A 9:16 or 4:5 version is its own composition with its own framing.- Do not silence
checkwithdata-layout-ignoreon content layers: with everything ignored it reports "Timeline did not advance" and checks nothing. Usedata-layout-allow-overflowanddata-layout-allow-occlusionwhere an overlap is intended. snapshotsends frames to Gemini whenGEMINI_API_KEYis set; pass--describe falseunless the user wants that.
References
references/story.md: facts sheet, concepts for client work, story shapes for voice spots and music-led trailers, beat grids, captions rules, accessibility, other aspect ratios.references/composition.md: HyperFrames structure, the timeline andframe(t), one camera, motion and easing, cursor, video clips, fonts, SVG and text gotchas, checks.references/audio.md: ElevenLabs voiceover and re-takes, music licensing and back-timing, synthesized SFX, rendering a game's own audio offline, the mix and the AAC guard.references/footage.md: recording canvas gameplay with input plans, cutting clips, clean page screenshots, rebuilding a product's UI from its CSS and real states.references/projects.md: where projects live, the directory layout, render names and versions, publication records, finishing a project, moving an old project into the layout.references/delivery.md: the review loop, render settings, platform notes (X, YouTube, LinkedIn, DEV.to, self-hosted website), the project README and publication records.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

