product-demo-video
This skill should be used when the user asks to "make a product demo video", "create an app demo video", "build a feature announcement video", "do a SaaS demo", "make a UI walkthrough video", "animate product screenshots into a demo", "add an animated cursor/click to a screenshot", or "zoom into part of a UI". Covers turning static screenshots/designs (not screen recordings) into a clean animated demo: device/browser frames, animated cursor + click ripple, zoom/pan to focal UI, callout annotations, step pacing, screen-to-screen transitions, and feature captions.
1How It Works & Mechanism
Core proposition: Replace screen recordings with static screenshots + code-driven animations, addressing structural issues in screen recordings like real cursor jitter and having to re-record after a single step change.
| Real cursor drifts, overshoots, jitters | Cursor follows an eased path to an exact target |
Inputs must be captured at 2× (DPR 2) so that a 2× zoom does not become blurry.
Capture inputs at twice the display size (retina / `deviceScaleFactor: 2`) so a 2× zoom stays sharp.
The cursor uses smoothstep easing (non-linear); the ripple and visual state change must occur on the exact same clickAt frame.
The cursor uses **smoothstep** so it accelerates out of rest and decelerates into the target (a linear cursor reads as robotic). Fire the ripple *and* the screen's state change on the same `clickAt` frame so cause and effect line up.
Zooming into focus uses transform-origin pointing to target pixels, capped at ~2×, sustained for 2–4s before pulling back.
Cap zoom around 2× (over 3× loses context and disorients). Hold the zoomed state 2–4s — long enough to read the detail — then ease back out before moving to the next screen.
Only one motion is allowed at any given time: either camera movement (zoom) or cursor movement.
Move the camera *or* the cursor, rarely both at once — two simultaneous motions split attention.
Every screen must be enclosed in a browser/device frame with a real URL populated; otherwise, screenshots read like bug reports.
A raw screenshot reads as a bug report; a framed one reads as product. Wrap every screen in a browser or device chrome on a branded background.
The coordinate workflow is strictly fixed: all cursor/zoom targets are measured in display space (values read on a 2× image are divided by 2).
Because the cursor `to` and the zoom target are screen-pixel coordinates, measure them once against the screenshot at display size (1280×800 here, even though the PNG is 2560×1600).
The validation loop centers on "whether the correct image is loaded and whether the cursor lands on the exact target pixel".
The demo ingests user assets — screenshots/designs, optional logo — so verification is mostly *did the right image actually load and land in frame*.
The annotation layer is data-driven rather than hardcoded: spotlights, arrow callouts, typed caption tracks, and four types of screen transitions are all frame-driven.
const captions = [ { text: "Open any report", in: 30, out: 110 },
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| package | remotion(AbsoluteFill/Sequence/Img/staticFile/useCurrentFrame/interpolate/spring) |
| package | playwright(含首次下载 Chromium 二进制) |
| network | 用户目标应用 URL(抓图时由无头浏览器访问;示例为占位域名) |
| cli | npx remotion still / render(首帧预览与编码) |
| cli | ffmpeg / ffprobe(仓库共享 contact-sheet.sh、probe-mp4.sh) |
| network | iart.ai(reference 尾部推广链接,具 utm 参数;仅读者点击才请求) |
4Recommended Governance & Evidence
- scripts/: Execute local scripts or commands: No bundled scripts inside the skill directory; reference instructs the agent to run `node capture.mjs` (Playwright capture, source code in docs)
- 网络端点: Declare external network access: Capture phase: Headless Chromium accesses the **user-provided app URL** (e.g., app.example.com placeholder), which is the only network access in the skill path
- 本地工作目录: File read/write and artifact output: Writes `public/shots/*.png` (capture artifacts), `out/f-settle.png`/`out/f-click.png`/`ou...` inside the user project
5Second-Pass Verification
- [discrepancy] 抓图脚本的目标 URL 是否可直接使用 — reference 的 SHOTS 列表硬编码 https://app.example.com/dashboard 等占位 URL(含 /reports/42?export=open 这类路径),原样运行必然失败;SKILL.md 未提示必须先替换成真实或本地 URL。
- [discrepancy] Callout 组件的可渲染性 — annotation-and-transitions.md 的 Callout 把 HTML div 包在 SVG 元素 g 里(g 只在 SVG 上下文有效),作为 React 组件直接放入 HTML 层会得到无效结构;该片段需改写才能用。
- [ok] 光标/涟漪与 clickAt 的因果同步是否有实现支撑 — screenshot-demo.md 的 Cursor 用 since = frame - clickAt 同时驱动 ripple 与 press 缩放,并把「ripple 与状态变化不同帧」列入失败模式,与 SKILL.md 的因果同帧要求一致。
- [ok] 2× 抓取与 ≤2× 缩放口径 — SD 的 deviceScaleFactor: 2(→2560×1600)与 SKILL.md/reference 的 zoom scale 2(个别 1.8)自洽。
- [ok] 多比例安全区口径 — AT 的 16:9/9:16 表(caption 在下方 80–120px;9:16 保留中心 80% 宽、避开底部 18%)与 SKILL.md 的 Keep captions in the lower third 一致。
- [unlocatable] 需登录的目标页如何处理 — skill 未提供任何会话/凭证注入方式(无 storageState、无 cookie 导入);「需要登录的应用怎么抓图」源码内无解答,记 unlocatable(也正因此不存在凭证读取面)。
6Verdict & Scenarios
1e7d345b4981c5db…