- TypeScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Scrap the vhs tape stack in favour of a single Remotion project at the repo root. The content plan moved to system design and concepts, where the terminal is not the subject: vhs anchors output to the top ~34% of a vertical frame and forces content to fit a 42-character line limit. Motion Canvas was prototyped and rejected — nicer authoring model, but no release in 18 months, no official CLI, and renders that report success while silently writing no file when a scene throws. Remotion produces the silent visual only; voiceover and subtitles stay in DaVinci. - src/scenes/: reusable library (Title, Caption, Reveal, chain/, code/) extracted from the two prototype videos, not designed up front - src/videos/<name>/: script.md (human) + Video.tsx, composition id = folder - Taskfile replaces the Makefile - Tokyo Night palette, single source of truth in src/theme.ts - Timing is data: beats declare durations, start frames are derived Both videos render clean: out/dns.mp4 (20.9s), out/cilium-mtu.mp4 (9.6s). Claude-Session: https://claude.ai/code/session_01RELXRbnJ8csa987Moow7QR |
||
| .github | ||
| docs/superpowers/specs | ||
| src | ||
| .gitignore | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| remotion.config.ts | ||
| Taskfile.yml | ||
| tsconfig.json | ||
faceless-shorts
Repeatable pipeline for faceless short-form video (YouTube Shorts / Reels / TikTok) on DevOps, observability, and system design topics. Spanish first. No on-camera presence.
Videos are animated with Remotion — React that renders to video, so every short is version-controlled, diffable, and reproducible. Remotion produces the silent visual; voiceover and subtitles are added afterwards in DaVinci Resolve.
Backlog of topics lives in the Obsidian vault: Resources/youtube-shorts-backlog.md.
Toolchain
| Job | Tool | Install |
|---|---|---|
| Animation / render | Remotion (React) | task install |
| Task runner | Task | brew install go-task |
| Voiceover | Piper (fast) or XTTS-v2 | local |
| Subtitles | WhisperX | local |
| Edit / mux / export | DaVinci Resolve (free) | cask |
Repo layout
shorts/
├── Taskfile.yml
├── src/
│ ├── Root.tsx # composition registry — one entry per video
│ ├── theme.ts # brand palette + fonts
│ ├── lib/anim.ts # clamp / lerp / ease / progress
│ ├── scenes/ # the reusable library
│ │ ├── Title.tsx
│ │ ├── Caption.tsx
│ │ ├── Reveal.tsx
│ │ ├── chain/ # request hopping through N parties
│ │ └── code/ # syntax-highlighted config with animated diffs
│ └── videos/
│ └── <name>/
│ ├── script.md # the script, written by hand
│ └── Video.tsx # the animation
└── out/ # renders (gitignored)
Per-short workflow
- Pick a topic from the backlog in the vault.
- Create the folder —
src/videos/<name>/— and writescript.md: the idea, the beats in order, and any notes on emphasis. This is a script for a human, not a config file; nothing renders from it. - Build the animation in
Video.tsx, composing scenes fromsrc/scenes/. Register it insrc/Root.tsxwith the compositionidmatching the folder name. - Preview with
task studio— hot-reloads while you edit. - Render —
task render VIDEO=<name>→out/<name>.mp4. - Finish in DaVinci — voiceover, burned-in subtitles from WhisperX, export vertical 1080×1920. Publish, cross-post, mark 🟩 in the backlog.
task list # available videos
task render VIDEO=dns # one video → out/dns.mp4
task all # every video
task studio # live preview
task typecheck # tsc --noEmit
task clean # remove renders
Scene library
Everything reusable lives in src/scenes/ so a second short on a similar shape
costs a script and a Video.tsx, not a new set of components.
Title— kicker + subject, pinned top.monofor literals (domains, commands).Caption— narration strip. Shared so pacing reads consistently across videos.Reveal— the payoff beat: an answer, large, after the scene dims back.chain/Chain— a request travelling through parties that answer or refer onward. Fits DNS, TLS handshakes, OAuth redirects, ingress paths, mTLS. Takesnodesandhops; hops declare adurationand start frames are derived.code/CodeBlock— syntax-highlighted config (Prism) with a character-level animated diff and line focus. Handles changes within lines; inserting or deleting a line would misalign the rows below it.
Convention: anything reusable across shorts goes in src/scenes/. Keep only
topic-specific data and composition in videos/<name>/Video.tsx.
Resist reaching for Chain just because it exists — roughly a third of the
backlog suits it. The rest wants shapes that aren't built yet, largely
symptom → cause → fix, which is the highest-retention format.
Conventions
- 1080×1920, 30fps. Set once in
src/Root.tsx. - Timing is data. Scenes take durations, not hardcoded start frames. Derive starts from durations so retiming one beat doesn't cascade.
- Keep animation math pure.
scenes/chain/timing.tsis a function of(frame, hops)with no React imports — that is what makes it testable and debuggable at a specific frame. - Tween state changes, don't switch them. Discrete per-frame states read as hard cuts on screen.
- No audio in Remotion. Voiceover and subtitles belong to the DaVinci step.
Cadence
Start 2/week for 4 weeks, batch-rendered in one session. Track retention +
saves; double down on the best-performing format. Weekly check in vault Reviews/.