| name | cinematic-demo-recording |
| description | Build polished, audio-free 4K demo screencasts that alternate cinematic motion-graphics scene cards with live screen recordings (CleanShot or similar). Use whenever the user wants a presentation-quality product demo, a multi-scene tutorial video with animated chapter intros, or any explain-then-show video that needs consistent typography, smooth transitions, and 4K text crispness. Triggers on phrases like "demo recording", "screencast with intro cards", "stitch CleanShot clips", "explain-then-show video", "multi-scene tutorial". |
| user-invocable | true |
Cinematic Demo Recording Pipeline
Produce a polished, audio-free demo video by alternating two kinds of segments:
- Scene cards — short (5–12s) motion-graphics intros rendered headlessly with
Playwright + ffmpeg. Cinematic dark theme, IBM Plex typography.
- Live segments — recorded manually by the user (CleanShot X or similar)
showing the actual product. Saved as
raw/scene-N.mp4.
ffmpeg crossfades the segments into a single 4K mp4.
Bundled assets
This skill ships working source. To bootstrap a new demo dir from these assets:
bash <skill-dir>/scripts/bootstrap.sh <target-demo-dir>
That copies cards.html, cards.css, record.mjs, package.json, a starter
cards.config.json (showing all three variants), and build.sh into the target.
.claude/skills/cinematic-demo-recording/
├── SKILL.md
├── assets/
│ ├── cards.html # Renders one card; reads ?card=N
│ ├── cards.css # Carbon-dark theme + animations + variants
│ └── cards.config.example.json # Three-card example (centered, default, planes)
└── scripts/
├── record.mjs # Playwright headless recorder
├── build.sh # ffmpeg xfade stitcher (edit SEQUENCE)
├── package.json # Pins playwright; npm install first
└── bootstrap.sh # One-shot copy into a target dir
Prerequisites on the host machine — node (≥ 18) and ffmpeg / ffprobe
on PATH. After bootstrap, run npm install inside <target>/scene-cards/
to fetch Playwright; first node record.mjs invocation downloads the chromium
binary.
When to use
- Product demos, sales decks turned into video
- Multi-scene technical walkthroughs (auth flow, deployment, debugging)
- Conference / internal presentation alternatives where the user wants control
over pacing and look but no live narration
- Anywhere the user wants "explain-then-show" structure with chapter cards
If the user just needs a single screen recording with no animation, this is overkill — point them at CleanShot directly.
Architecture
demo/
├── scene-cards/
│ ├── cards.html # Single template, picks its card from ?card=N
│ ├── cards.css # Animation timeline + Carbon-dark theme
│ ├── cards.config.json # Per-card content + duration_ms
│ ├── record.mjs # Playwright headless → trimmed mp4
│ └── out/card-N.mp4
├── raw/scene-N.mp4 # User's live recordings
├── script-scene-N.md # Click-by-click takedown
├── build.sh # Stitches everything → demo.mp4
└── demo.mp4
One render template, one config file, one stitcher. New cards = new entries
in cards.config.json. New scenes = new lines in build.sh's SEQUENCE array.
Build process — pilot first
For multi-piece deliverables, validate one piece end-to-end before producing
the rest. Build a "pilot": one scene card + one live clip + the crossfade.
Get user sign-off on the look (font weight, transition, hold duration, colors)
before producing the remaining cards. Without this, you waste hours rendering
20 cards in a style the user vetoes after seeing the first one.
build-pilot.sh is a stripped-down build.sh with hardcoded inputs.
Scene-by-scene review-before-record
This is a gated loop, not autonomous:
- Render the card (
node record.mjs --card N).
- Write the click-by-click script (
script-scene-N.md — pre-flight,
timestamped take instructions, common reshoots, save path).
- Stop. Present card + script. Wait for the user to review + record.
- Once
raw/scene-N.mp4 lands, proceed to the next scene.
Don't render N+1 before the user has watched N's card and signed off.
Card variants (in cards.css)
default — badge + title + bullets, left-aligned. Workhorse for content scenes.
centered — title + subtitle, centered. Intros, outros, transitions.
planes — title + 4-column "WHAT/HOW" grid. Architecture summaries.
Add a new variant by extending cards.css (body[data-variant="..."] rules)
and cards.html (the JS that builds the DOM).
Bullet-writing style
- Business-first, technical detail in parens. Lead with what the audience
cares about; tuck the jargon behind. Example:
Token carries the user's role (groups claim in the subject token)
- Brief. ≤ 12 words per bullet. Long sentences read poorly at 30fps.
- Parallel framing for paired scenes. If scene 2 is "denied" and scene 3
is "allowed," use the same bullet structure on both cards so the visual
contrast lands. Example: AI agent tries to update the email in both.
Critical gotchas — these will burn you
-
4K via CSS zoom, not deviceScaleFactor. DSF=2 with viewport=3840×2160
makes Chromium paint only the top-left quadrant. Set viewport=3840×2160,
DSF=1, pass ?ssr=2, and the page sets documentElement.style.zoom = 2
on a 1920×1080 logical layout. The CSS is authored at 1920×1080.
-
Never animate letter-spacing. It triggers full layout per-frame;
visibly choppy on 30fps recordings, worst on long titles. Animate only
opacity and transform. Add will-change: transform, opacity on
animated elements so they're promoted to a GPU layer.
-
Wall-clock trim, not webm timestamps. VP8 keyframes in Playwright's
webm are sparse — -ss before -i lands on the wrong frame. Measure
animStart - ctxStart in JS, pass to ffmpeg as -ss AFTER -i
(decode-and-discard, accurate but slower).
-
Static HTTP server, not file://. fetch('cards.config.json') from
a file:// origin is blocked. record.mjs spins a one-shot Node http
server on a random port.
-
Card-out delay derives from --total. Use
calc(var(--total) - 1320ms) everywhere card-out fires. The 1320ms = 700ms
fade-out + ~620ms tail. This lets duration_ms per card work without
re-tuning each keyframe.
-
Normalize before xfade. Mixing 4K cards with 1080p raw clips at the
xfade boundary causes a visible resolution / fps shift. Normalize every
input to 3840×2160 / 30fps / yuv420p / silent AAC first, then xfade.
-
tune animation for libx264. Cards have flat backgrounds with text
transitions; -tune animation reduces banding on the gradient and ringing
on the text edges. CRF 16–18 is the sweet spot for 4K text.
Output specs (defaults — adjust if user has different requirements)
- 3840×2160, 30fps, libx264, yuv420p, BT.601
- Silent AAC stereo @ 48kHz (so the audio stream stays continuous across xfades)
- 0.7s xfade between adjacent segments
- CRF 16 for individual cards, CRF 18 for the final stitch (avoids
re-encoding loss compounding)
Animation timeline (per card, in --total ms)
corner ticks 100ms → 900ms fade in
badge 200ms → 800ms slide from -32px X
title 400ms → 1100ms fade + slight lift
subtitle 750ms → 1350ms fade + slight lift
bullets 1000ms+stagger(140ms each) for 500ms each
planes 1100ms+stagger(200ms each) for 600ms each
rail-fill 0 → --total linear fill (visual progress bar)
card-out --total-1320ms → --total-620ms blur(0→8px) + opacity(1→0)
Recovery from common failure modes
- "Animation feels choppy" → check
letter-spacing is not in any
@keyframes; check will-change is set on animated elements.
- "Text is blurry" → confirm
?ssr=2, viewport 3840×2160, DSF 1.
If still blurry, check ffmpeg uses scale=...:flags=lanczos+accurate_rnd+full_chroma_int.
- "Card-out timing wrong" → some keyframe still uses a hardcoded
8480ms
instead of calc(var(--total) - 1320ms).
- "Resolution shift between card and live clip" → live clip wasn't
normalized; check
build.sh's normalize step ran for that input.
Inputs you need from the user
- Two test users / accounts if the demo shows authorization (one with each role)
- Pre-warmed environment / seed data so re-takes are deterministic
- Confirmation of MFA-disabled test accounts (otherwise auth screens block recordings)
- Approved style cues for the pilot before producing the rest