npx skills add ...
npx skills add theorcdev/skills --skill intro-video
Build Remotion intro, reel, and brand-film videos with remocn and ASR-timed captions. Use when making an intro, reel, Shorts thumbnail, product demo film, or when the user mentions caption timing, remocn video, or invokes /intro-video. Caption times must come from ASR — never estimate.
npx skills add theorcdev/skills --skill intro-video
Build Remotion intros, reels, and brand films with remocn. Load the remocn skill for components. This skill owns captions, audio, intros, and render.
Three rules, each learned by shipping the wrong video:
Ask for both before any caption work:
Script gives spelling. ASR gives timing. Map script words onto ASR timestamps; keep the times.
Trim the dead air off both ends before anything else. Raw VO arrives with silence at the head and tail. Ship it untrimmed and the video opens on a still frame with no voice and ends hanging on nothing.
Order matters: trim first, then ASR. ASR timestamps are relative to the file you feed it. Trim afterwards and every caption is offset by the length of the head silence.
Measure the real boundaries — do not guess a fixed number:
Read the first silence_end (voice starts) and the last silence_start
(voice ends), then cut to those with a small margin so consonants survive:
Confirm with ffprobe -show_entries format=duration before moving on. Then run
ASR on voice-trim.wav, and use that same file in the composition — the
timings only line up against the file they were measured from.
-40dB suits a clean close-mic recording. Noisy room, raise toward -30dB.Uses sherpa-onnx from PyPI and a zipformer model from GitHub release assets (allowlisted). Whisper weights on Azure/HuggingFace are often blocked — do not start there. The script downloads the model on first run.
Drive every caption from / durationInFrames from that JSON. 2–4 words on screen. Keyword in amber.
| Vertical 1080×1920 | Landscape 1920×1080 | |
|---|---|---|
| Font | Inter 800, 70px | Inter 800, 58px |
| Fill | white | white |
| Stroke | 13px black, paint-order: stroke fill | same |
| Keyword | #FFD93D | same |
| Max width | 790 (clears the Reels button column) | — |
| Over footage | plate behind the text | same |
1.0 → 1.09, origin 50% 42%.Animations, logo lockups, code cards, stat callouts and every other overlay go in the empty space in the top-left. Never across the whole screen — unless the user explicitly asks for full-screen.
A full-bleed animation covers the speaker and fights the captions. The top-left is empty on nearly every talking-head frame, so that is where an overlay reads without hiding anything.
Look at an actual frame first and put the overlay in the empty corner you see there. Where the subject genuinely sits left, mirror the box to the top-right. Starting points:
| Vertical 1080×1920 | Landscape 1920×1080 | |
|---|---|---|
| Box origin | x 60, y 200 | x 80, y 80 |
| Box size | ~560 × 560 | ~680 × 420 |
| Max scale | ~50% of width | ~35% of width |
typescript@5. TS7 drops ts.sys and breaks Remotion's bundler.@/ needs a webpack alias in remotion.config.ts.@sparticuz/chromium (brotli).remocn.dev 403s, pull components from the remocn GitHub repo registry-artifacts/ instead of shadcn add. npm, PyPI, and GitHub release assets work.df -h / and clear out/ first.ffprobe the frame count after every chunk before concatenating.Render the 1920×1080 composition with --scale=2. It stays 1920×1080
logically but rasterizes at 2× device pixels, so captions, lockups and
vignettes come out natively sharp. Do not build a separate 4K composition.
Concat the chunks, then mux the audio.
Safe areas differ per platform and the wrong guess buries the type. See
references/cover-art.md.
python3 scripts/asr.py voice-trim.wav --fps 30 --out captions.json<Sequence from={startFrame} durationInFrames={endFrame - startFrame}>
<Caption text={phrase} keyword={keyword} />
</Sequence>npx remotion render src/index.ts <Comp> out/kN.mp4 \
--codec=h264 --crf=17 --muted --scale=2 --frames=A-B