# Cinematic Demo Recording

> 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".

- Skill: `panchal-ravi/cinematic-demo-recording` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add panchal-ravi/cinematic-demo-recording`
- Raw SKILL.md: https://api.skillmd.com/api/skills/panchal-ravi/cinematic-demo-recording/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: panchal-ravi (https://skillmd.com/u/panchal-ravi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/panchal-ravi/cinematic-demo-recording

---


# 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
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:

1. Render the card (`node record.mjs --card N`).
2. Write the click-by-click script (`script-scene-N.md` — pre-flight,
   timestamped take instructions, common reshoots, save path).
3. **Stop.** Present card + script. Wait for the user to review + record.
4. 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

1. **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.

2. **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.

3. **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).

4. **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.

5. **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.

6. **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.

7. **`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

