npx skills add ...
npx skills add goldlegendw80/llm-video-maker --skill make-video
Turn a prompt or brief into a fully rendered MP4 video. Generate AI videos and social clips (TikTok, Reels, YouTube Shorts), YouTube intros, startup website hero loops, product promos, marketing videos, explainers and animated data visualizations - with AI voiceover (text-to-speech), word-synced captions/subtitles, music, and real assets (icons, brand logos, stock photos, video b-roll, GIFs, memes); record live website or app demos via Playwright screen capture; render code snippets with syntax highlighting; and composite it all deterministically from HTML/GSAP. Use when asked to make, generate, create or render a video, turn a script, topic, codebase or recorded narration into a video, or produce social, marketing or explainer video content. Trigger - /make-video <brief.json|inline description>.
npx skills add goldlegendw80/llm-video-maker --skill make-video
You are the pipeline orchestrator. Deterministic work runs through scripts/CLIs; creative work
(design, storyboard, composition) is yours, guided by the hyperframes skill. Every stage writes
its artifact into the project directory so the run is resumable and auditable.
$SKILL_DIR below means this skill's own directory (shown as "Base directory for this
skill" when the skill loads). All pipeline scripts are bundled in $SKILL_DIR/scripts/ — the
skill is fully self-contained and works in any repository.
npx hyperframes@0.6.91 doctor in preflight.hyperframes 0.6.91 exact (pin it in the host repo's package.json; never float
a pre-1.0 engine).hyperframes skill is not already
available, install the official set: npx hyperframes@0.6.91 skills (non-interactive under
agents). The DESIGN/COMPOSE stages below lean on them./edit-video (chapter-scoped edits of finished videos) ships alongside this
one; for "turn this URL into a video" requests, the official website-to-hyperframes skill
is the specialist — this pipeline's capture asset type embeds recorded demonstrations,
which is different from re-composing a captured site.PEXELS_API_KEY / PIXABAY_API_KEY (stock images +
videos), GIPHY_API_KEY / TENOR_API_KEY (gifs), FREESOUND_API_KEY (CC0 sound effects),
OPENAI_API_KEY + IMAGE_GEN=openai (real image generation).npm i -D hyperframes@0.6.91 (the engine pin) and, only if you
will use the capture asset type, npm i -D playwright-core (drives your installed Chrome —
no browser download). Create briefs/ and projects/ as needed; both are plain directories.A brief — either a JSON file conforming to $SKILL_DIR/schema.json (enforced by the preflight
validator), or an inline description you
normalize into one. Required: id, platform (tiktok|reels|shorts → 1080×1920@30 ·
youtube → 1920×1080@30 · square → 1080×1080@30 · or custom {width,height,fps}), story,
source. Write the normalized brief to projects/<id>/brief.json before starting. Ask the user
only if source or timing inputs are genuinely ambiguous.
Three source types decide the GROUNDING rule:
codebase / topic — every on-screen claim traces to facts.json. No invented stats.script — the script IS the truth. Full creative freedom: interpret tone, concepts, and
emotional register from the text; choose design, rhythm, metaphors, and techniques to serve
the story. The hard gates (determinism, contrast, layout, vision pass) still apply — freedom
is creative, never structural.Two TIMING modes decide who owns the clock:
duration_s is the target.narration.mode: "user-audio") — the user supplies a recording and a
timestamped transcript. The transcript owns the clock: scene boundaries derive from segment
timestamps and are NOT negotiable. duration_s is optional and ignored (warn if it diverges
5% from the audio). Precedence: transcript words own all on-screen verbatim text;
source.textis creative direction only.
Every stage writes its artifact before the next begins. On any re-run of a project:
skip in the run-log);Append one JSON line per stage event to projects/<id>/run-log.jsonl:
{"ts": "<iso>", "stage": "compose", "event": "start|done|fail|skip", "note": "..."}.
On fail, append a final line telling the user exactly which stage failed and that re-running
/make-video projects/<id>/brief.json resumes from that stage. Give the user a one-line
progress message as each stage completes ("design locked: dark-premium, Bricolage/Inter").
hyperframes authoring skill is missing or its path is a broken symlink, install the
official set first: npx hyperframes@0.6.91 skills (writes into .agents/skills/ + symlinks;
gitignored — every machine runs this once).node "$SKILL_DIR"/scripts/validate-brief.mjs <brief.json> → zero errors required. Fix what's fixable
from the user's words; otherwise surface the validator's message and stop.npx hyperframes doctor → FFmpeg, FFprobe, Chrome and Node must pass. Docker may fail —
irrelevant for local renders. If a required dep is missing, print the doctor hint and stop.mode: tts → verify Kokoro is ready, don't download mid-run. The TTS model
(~/.cache/hyperframes/tts/models/kokoro-v1.0.onnx + voices-v1.0.bin) and a Python with
kokoro_onnx + soundfile are provisioned ONCE at setup (the desktop app bundles them; a CLI
user runs setup once). Check: python3 -c "import kokoro_onnx, soundfile" (the engine resolves
python3 from PATH — the app puts its prewarmed venv first on PATH). If the import fails on a
manual/CLI bootstrap, create the venv once: uv venv ~/.video-maker/runtime/python && uv pip install --python ~/.video-maker/runtime/python/bin/python kokoro-onnx soundfile (no uv?
python3 -m venv ~/.video-maker/runtime/python && ~/.video-maker/runtime/python/bin/pip install kokoro-onnx soundfile), and confirm the model file exists. Never download the TTS model
inside a generation run — if it is missing, stop and tell the user to run workspace setup.mode: user-audio → ffprobe the audio file now (codec + duration); the validator already
confirmed the files exist.
Nothing past this line runs until preflight is green — dep failures must never cost
storyboard/compose tokens.node "$SKILL_DIR"/scripts/analyze-codebase.mjs <path> --out projects/<id>/facts.json{name, description, metrics?, keyFacts: [{fact, source}]} to facts.json.facts.json as {name, script, themes: [...], tone, concepts: [...]} —
your reading of the script: its register, key concepts, emotional arc, recurring images. This
becomes the creative grounding (visual metaphors should trace to identified concepts).node "$SKILL_DIR"/scripts/normalize-transcript.mjs <transcript> --out projects/<id>/transcript.json --audio <audio_file> — canonical segments [{id, start, end, text}]. If the user gave audio
but no transcript, transcribe locally first: npx hyperframes transcribe <audio> --model base.
If captions are enabled, word-level timestamps are MANDATORY — a sentence-level transcript must
be supplemented by npx hyperframes transcribe <audio> --model base (words drive the caption
composition). Always pass --model base (the bundled MULTILINGUAL model) — never the engine
default small.en or any .en model: .en models silently TRANSLATE non-English audio into
English. The model ships with the app (no download mid-run); if it is missing, stop and tell the
user to run workspace setup. Copy the audio into projects/<id>/assets/audio/narration.<ext>.facts.json. No invented stats. If a fact is missing, gather it now or drop the element.Read the hyperframes skill (SKILL.md + house-style.md + references/video-composition.md +
references/typography.md + references/motion-principles.md). Honor brief.style: existing
design_md wins (a brand-kit design.md is the brand's source of truth — adopt it verbatim);
else palette preset from house-style palettes;
else pick by audience/tone. Write projects/<id>/design.md: bg/fg/accent hexes, font pairing,
corner/density/depth rules, motion personality, and 3-5 "don'ts". Video-medium scale applies
(60px+ headlines, decoratives at 12-25% opacity, ambient motion on every decorative).
Checkpoint beat: after design.md is written, give the user a 2-line summary (palette, fonts, motion personality). In an interactive session they may object before tokens go to composition; in an autonomous run, proceed after posting it.
Transcript-locked mode: run the deterministic mapper FIRST —
node "$SKILL_DIR"/scripts/plan-scenes.mjs projects/<id>/transcript.json --out projects/<id>/scene-plan.json.
The plan's scene windows and segmentIds are the sync contract: copy them into the storyboard
verbatim. You may extend a scene's end into a following gap (breathing room) — never move a
boundary into speech. Your creative work is everything else: beats, techniques, on-screen
content, transitions, chapter grouping.
Write storyboard.json:
onScreen roles (hierarchy contract): every entry carries role:
primary (the one message of the scene — max 1, may be 0 for pure-visual scenes) ·
secondary (supporting claim/stat) · support (chips, labels, small data) ·
decorative (textures, glows — no information). Entrance order follows role priority
(primary first, decoratives ambient from frame 1). Array order carries NO meaning.
Reading-load budget: total words across primary+secondary+support ≤ 3.5 × scene duration
in seconds. Captions don't count (they replace narration, not add to it).
Audio-first mode rules: scene durations sum to duration_s (±5%). 2.5–6s per scene
typical; the hook lands in the first 2s for social platforms. Narration ≈ 2.3–2.6 words/sec —
count words BEFORE locking durations. Narration drives timing, not vice versa.
Transcript-locked mode rules:
scene-plan.json (see above). subBeats: n hints mean: stage that
many visual sub-beats INSIDE the scene window — the window itself never splits.brief.chapters provides from_s/to_s, snap
each boundary to the nearest segment gap. Chapter boundaries are where heavier transitions
(shader/accent) belong.music.enabled, warn and ignore.node "$SKILL_DIR"/scripts/plan-scenes.mjs projects/<id>/transcript.json --check projects/<id>/storyboard.json
→ must pass before COMPOSE.In both modes chapters make sections independently editable: a later edit request ("redo the
architecture chapter") re-storyboards and re-renders only the scenes in that chapter — see the
/edit-video skill.
Media research. Before writing the manifest, actively hunt for the media each scene
deserves: WebSearch for the exact chart/screenshot/clip a claim needs, browse provider
catalogs mentally (stock video, gifs, memes, sfx), and consider a live capture of any
website or local app the video talks about. The storyboard's technique should already
say WHICH medium carries each beat — this step finds the best instance of it.
node "$SKILL_DIR"/scripts/fetch-assets.mjs projects/<id>/assets.manifest.json --dest projects/<id>/assets
Use --strict when a missing asset would break the design. The full menu:
| type | source | key | notes |
|---|---|---|---|
icon | Iconify (275k+) | none | ?color= recolor for monotone sets |
brand | simple-icons | none | CC0 (trademarks still apply) |
image | Pixabay / Pexels | yes | photo search, orientation-aware |
video | Pexels / Pixabay videos | yes | b-roll; max_s caps length; ≤1080p rendition picked |
gif | Giphy / Tenor | yes | saved as MP4 (loops cleanly as a <video> clip) |
meme | imgflip templates | none | template image only — the COMPOSITION overlays caption text (sharper, on-palette, animatable) |
sfx | Freesound (CC0-filtered) | yes | whooshes, clicks, risers ≤ max_s |
web | any direct URL | none | license + source fields REQUIRED — refused otherwise |
capture | live browser recording | none | see below |
imagegen | OpenAI (or styled placeholder) | opt-in | prompt sidecar in placeholder mode |
capture — recorded demonstrations. "type": "capture" drives the user's real Chrome
via Playwright over any URL — including http://localhost:* to demo a local app — executing
a declarative action script (scroll at controlled speed, click, type, hover) and vendoring
an MP4 ($SKILL_DIR/scripts/capture-demo.mjs). Use it whenever the video is ABOUT software:
a real recorded tour beats a faked mockup. Author actions to match the narration window the
clip will fill (e.g. a 6s scene → ~6s of scripted interaction). The recording is source
footage — determinism applies to the composition render, not to footage capture.
Licensing is non-negotiable: every fetched asset gets a .credit.json sidecar
(provider, source URL, license). Meme/gif library content carries platform-ToS terms, not
open licenses — fine for personal/social use; the sidecar records that the user owns that
call. web-type entries without explicit license+source are rejected by the script.
Placeholder rule — ALL asset classes: any failed or ungenerated asset renders as a styled slot — a card in the design.md palette with a subtle pattern + short label derived from the prompt/query. The video must look FINISHED with placeholders, not broken.
Embedding motion media in the composition (COMPOSE stage rules, stated here once):
<video> elements with data-start/data-duration on their
own track or inside a scene — muted by default; sfx are <audio> clips ducked under
narration (-14dB typical).npx hyperframes remove-background <file> (see the hyperframes-media skill).Narration:
mode: tts: write narration/sNN.txt per scene, then
npx hyperframes tts narration/sNN.txt --voice <voice> --output assets/audio/sNN.wav
(Kokoro venv from preflight; OpenAI TTS as fallback engine). If ANY scene's TTS fails,
HALT here — never reconcile durations against partial audio. Then ffprobe each wav and
RECONCILE scene durations to actual audio + 0.4-0.8s breathing room. Update
storyboard.json — audio wins.mode: user-audio: the narration track already exists. Do NOT alter timings — verify
instead: every scene's [start, end] must cover its segmentIds spans exactly (the
--check already proved this; re-run it after any storyboard touch-up).Code visuals: render code snippets with Shiki INSIDE the composition (npm dep or CDN at build time, vendored), never as screenshots.
After fetching, verify manifest.resolved.json — replace failed assets or amend the design.
npx hyperframes init projects/<id> --yes (if not yet a HyperFrames project — check for
hyperframes.json), then set the composition to brief geometry: <meta name="viewport">,
data-width/data-height, html,body CSS size, data-duration = final duration.
Safe zones (platform chrome). Define CSS vars on the composition root and wrap content:
| platform | top | bottom | left | right | chrome being avoided |
|---|---|---|---|---|---|
| tiktok | 144px | 320px | 48px | 144px | status bar · caption/CTA/nav · action rail |
| reels | 144px | 256px | 48px | 120px | header · caption+CTA · action rail |
| shorts | 120px | 240px | 48px | 120px | header · title/channel · action rail |
| youtube | 54px | 120px | 96px | 96px | player controls on hover |
| square | 54px | 54px | 54px | 54px | none (feed margin) |
Named zones: .zone-safe (absolute inset by the vars — ALL primary/secondary/support text
and CTAs live here) · .zone-caption (reserved caption band, see CAPTIONS) ·
.zone-bleed (full canvas — decoratives only). These are conservative supersets of current
platform chrome; a brief may override with explicit insets.
Follow the hyperframes skill EXACTLY: layout-before-animation (hero frame as static CSS
first), entrance-only tweens (gsap.from), transitions between scenes (no jump cuts, no exit
tweens except final scene), ≥3 distinct eases per scene, paused timelines registered on
window.__timelines, deterministic code only (no Math.random/Date.now/repeat:-1/async
timeline construction).
Prefer registry blocks over hand-rolling: npx hyperframes add data-chart flowchart logo-outro caption-highlight … and adapt them.
Scene = sub-composition in compositions/ when it's substantial; inline clip when trivial.
Density: 8-10 visual elements per scene (background texture + midground content + foreground accents); every decorative gets ambient motion. Scenes <3s: one message element (see stage 2).
Checkpoint beat: after the first scene composes clean, snapshot it
(npx hyperframes snapshot --frames 2) and show the user — the early reveal catches a wrong
design direction before five more scenes compound it.
CAPTIONS (when captions.enabled) — arbitration rules:
data-track-index, data-start="0",
spanning the whole video) driven by word-level timestamps. Chapter re-renders never touch it..zone-caption: bottom-centered, above the platform's bottom inset
(e.g. tiktok: y from ~72% to calc(100% - 320px - 24px) region) — nothing else renders there,
and captions never leave it.Run in order; fix everything each pass:
npx hyperframes lint → zero errors (determinism/structure contract)npx hyperframes validate → fix WCAG contrast warnings within the palette familynpx hyperframes inspect --json → fix every overflow/clip not intentionally marked; check
primary/secondary text boxes stay inside .zone-safe bounds and out of .zone-caption.node .claude/skills/hyperframes/scripts/animation-map.mjs projects/<id> --out projects/<id>/.hyperframes/anim-map
(KNOWN ISSUE hyperframes 0.6.91: producer npm dist throws __dirname is not defined in ES module scope. If hit: record "anim-map: degraded" in report.md and compensate with
npx hyperframes snapshot --frames 16 — never silently skip.)
→ check flags: offscreen, collision, invisible, paced-fast/slow, dead zonesnode "$SKILL_DIR"/scripts/plan-scenes.mjs … --check …) — composition tweaks must not have drifted
any data-start.npx hyperframes snapshot --frames 8 → fresh-context vision pass. Judge the PNGs against
design.md + storyboard.json + the defect taxonomy ONLY — do not re-read your composition
code while judging (it makes you lenient). Prefer a subagent that receives just the PNGs,
design.md, and storyboard. The defect taxonomy:
Non-convergence delivery semantics: after 3 iterations, STOP iterating — the render still
proceeds. The report.md headline and your final message both carry the label
PASSED or DELIVERED WITH N OPEN ISSUES; each open issue lists scene id, time range,
one-line description, and what was tried. Offer the retry affordance: fix by hand, or rerun
/make-video projects/<id>/brief.json (resume re-enters VALIDATE only). Never silently ship a
degraded video as if it passed.
Audio placement: mode: tts → per-scene <audio> clips on a dedicated track with data-start
at scene starts. mode: user-audio → ONE <audio> clip for the whole narration at
data-start="0" (never slice the user's recording). 4K: --resolution portrait-4k|landscape-4k
(integer supersample). Renders are cacheable — re-render only what changed (--composition for
one scene/chapter sub-composition).
ffprobe the output: duration/resolution/fps/codec match the brief (duration within ±5% or
matching reconciled storyboard total; user-audio: matching the audio duration).ffmpeg -ss <t> -frames:v 1) across the timeline and LOOK at them —
final visual check at output resolution, esp. text legibility and H.264 banding on gradients.report.md — creator-first order:
PASSED / DELIVERED WITH N OPEN ISSUES) + video path + duration/size/edit-video <id> <chapter> '<instruction>'"projects/<id>/assets/.plan-scenes --check must pass; the user's
recording is never sliced, stretched, or re-cut.hyperframes version that
rendered it; record it in report.md.manifest.resolved.json + credit sidecars).{
"rhythm": "fast-fast-SLOW-fast-hold", // declare before scenes (beat-direction.md)
"chapters": [{ // editable/re-renderable sections
"id": "ch1-intro", "title": "Introduction", "sceneIds": ["s1-hook", "s2-context"]
}],
"scenes": [{
"id": "s1-hook", "start": 0, "duration": 3.5,
"chapter": "ch1-intro",
"beat": "hook", // hook|context|feature|proof|how|outro …
"narration": "One sentence, ≤ 2.5 words/sec.", // audio-first mode
"segmentIds": [1, 2], // transcript-locked mode
"onScreen": [ // grounded per source rules; roles required
{ "text": "Ship every week", "role": "primary" },
{ "text": "12 releases this quarter", "role": "secondary" },
{ "text": "release-cadence chip", "role": "support" },
{ "text": "grid texture", "role": "decorative" }
],
"factRefs": ["metrics.files", "git.commits"],
"technique": "kinetic-type|data-chart|flowchart|code-reveal|logo-outro|…",
"transitionOut": "crossfade|wipe|shader:…"
}],
"assets": [ /* fetch-assets.mjs manifest entries, incl. imagegen slots with prompts */ ]
}npx hyperframes render projects/<id> --quality <brief> --fps <brief> --output projects/<id>/renders/<id>.mp4