Scriptable cinematic screen recorder for product demos — write a YAML scenario, get a professional recording. Powered by Playwright CDP. The demo above was made with 1 file, 239 lines.
Write a YAML scenario, hit record — Clipwise handles all the cinematic magic.
npx clipwise demo to instantly record the built-in dashboard — no setup needed.Install Clipwise, run the demo, or write your own scenario.
npx without installing).npm install -D clipwise
npx clipwise demo
npx clipwise demo --device iphone # iPhone mockup
npx clipwise demo --device android # Android mockup
npx clipwise demo --url https://kwakseongjae.github.io/clipwise/demo/ # Custom URL
npx clipwise init # Creates clipwise.yaml
# Edit clipwise.yaml — set URL to your site
npx clipwise record clipwise.yaml -f mp4 # Record!
import { ClipwiseRecorder, CanvasRenderer, encodeMp4, loadScenario } from "clipwise";
const scenario = await loadScenario("my-scenario.yaml");
const recorder = new ClipwiseRecorder();
const session = await recorder.record(scenario);
const renderer = new CanvasRenderer(scenario.effects, scenario.output, scenario.steps);
const frames = await renderer.composeAll(session.frames);
const mp4 = await encodeMp4(frames, scenario.output);
A scenario has 4 sections: metadata, effects, output, and steps.
name: "My Demo"
description: "Optional description"
viewport:
width: 1280 # Browser width (default: 1280)
height: 800 # Browser height (default: 800)
effects:
# See "Effects" section below
output:
format: mp4 # gif | mp4 | png-sequence
width: 1280
height: 800
fps: 30 # 1-60
preset: balanced # social | balanced | archive
steps:
- name: "Step name"
actions:
- action: navigate
url: "https://example.com"
captureDelay: 200 # ms to wait after actions
holdDuration: 800 # ms to hold on result
transition: none # none | fade
| Action | Parameters | Default | Description |
|---|---|---|---|
navigate | url, waitUntil? | networkidle | Navigate to URL |
click | selector, delay?, timeout? | Click an element | |
type | selector, text, delay?, timeout? | delay: 50 | Type text (char-by-char) |
hover | selector, timeout? | Hover over element | |
scroll | y?, x?, selector?, smooth?, timeout? | smooth: true | Scroll by offset |
wait | duration | Wait (ms) | |
screenshot | name?, fullPage? | fullPage: false | Capture marker |
| Action | Parameters | Default | Description |
|---|---|---|---|
waitForSelector | selector, state?, timeout? | visible, 15s | Wait for element state |
waitForNavigation | waitUntil?, timeout? | networkidle, 15s | Wait for page load |
waitForURL | url, timeout? | 15s | Wait for URL match |
waitForFunction | expression, polling?, timeout? | raf, 30s | Wait for JS expression to be truthy |
waitForResponse | url, status?, timeout? | 30s | Wait for network response (URL substring match) |
# Wait for element to appear after API call
- action: waitForSelector
selector: ".result-panel"
state: visible
timeout: 20000
# Wait for AI streaming to complete
- action: waitForFunction
expression: "document.querySelector('.ai-response')?.dataset.done === 'true'"
timeout: 60000
# Wait for specific API response
- action: waitForResponse
url: "/api/chat/completions"
status: 200
timeout: 60000
Copy the complete YAML schema reference. Paste into Claude Code, Cursor, ChatGPT, or any AI assistant to generate scenarios instantly.
This block contains everything an AI needs: all 12 action types with parameters, defaults, effects config, output options, and usage rules.
# Clipwise YAML Schema Reference (v0.5.0)
# Scriptable cinematic screen recorder — YAML in, MP4 out
# Install: npm install -D clipwise
# Record: npx clipwise record scenario.yaml -f mp4
# ─── Top-Level Structure ──────────────────────────────
name: "Scenario Name" # required
description: "Optional" # optional
viewport:
width: 1280 # default: 1280
height: 800 # default: 800
# ─── Output ───────────────────────────────────────────
output:
format: mp4 # mp4 | gif | png-sequence
fps: 30 # 1-60 (default: 30)
preset: balanced # social | balanced | archive (default: balanced)
# quality: 80 # DEPRECATED — use preset instead
width: 1280 # default: 1280
height: 800 # default: 800
# ─── Steps ────────────────────────────────────────────
steps:
- name: "Step Name" # optional
captureDelay: 200 # ms after actions (default: 300)
holdDuration: 800 # ms to hold result (default: 1500)
transition: none # none | fade (default: none)
actions:
# --- Basic Actions (7) ---
- action: navigate
url: "https://example.com"
waitUntil: networkidle # load | domcontentloaded | networkidle (default)
- action: click
selector: "#btn" # CSS selector (Unicode/CJK OK)
delay: 0 # optional click delay (ms)
timeout: 15000 # optional element wait timeout (ms)
- action: type
selector: "#input"
text: "hello"
delay: 50 # per-char delay ms (default: 50)
timeout: 15000 # optional element wait timeout (ms)
- action: hover
selector: ".menu"
timeout: 15000 # optional
- action: scroll
y: 400 # vertical px (default: 0)
x: 0 # horizontal px (default: 0)
selector: ".container" # optional target element
smooth: true # default: true
timeout: 15000 # optional
- action: wait
duration: 1000 # ms
- action: screenshot
name: "result" # optional label
fullPage: false # default: false
# --- Async Wait Actions (5) ---
- action: waitForSelector
selector: ".result"
state: visible # visible (default) | attached | hidden
timeout: 15000 # default: 15000
- action: waitForNavigation
waitUntil: networkidle # load | domcontentloaded | networkidle (default)
timeout: 15000 # default: 15000
- action: waitForURL
url: "https://example.com/dashboard"
timeout: 15000 # default: 15000
- action: waitForFunction
expression: "document.querySelector('.done') !== null"
polling: raf # raf (default) or ms number (e.g. 500)
timeout: 30000 # default: 30000
- action: waitForResponse
url: "/api/chat" # URL substring match
status: 200 # optional HTTP status filter (100-599)
timeout: 30000 # default: 30000
# ─── Effects ──────────────────────────────────────────
effects:
zoom:
enabled: true # default: true
intensity: moderate # subtle(1.15x)|light(1.25x)|moderate(1.35x)|strong(1.5x)|dramatic(1.8x)
# scale: 1.35 # numeric override (1-5); ignored when intensity is set
duration: 600 # ms (default: 600)
autoZoom:
followCursor: true # default: true
transitionDuration: 300 # ms (default: 400)
padding: 200 # px (default: 200)
cursor:
enabled: true # default: true
size: 20 # px (default: 20)
color: "#000000" # default: #000000
speed: fast # fast | normal | slow (default: fast)
clickEffect: true # default: true
clickColor: "rgba(59,130,246,0.3)"
trail: false # default: false
trailLength: 8 # default: 8
highlight: false # default: false
highlightRadius: 40 # default: 40
background:
type: gradient # gradient | solid | image (default: gradient)
value: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
padding: 60 # px (default: 60)
borderRadius: 12 # px (default: 12)
shadow: true # default: true
deviceFrame:
enabled: false # default: false
type: browser # browser | iphone | ipad | android | none
darkMode: false # default: false
keystroke:
enabled: false # default: false
showTyping: false # show typed text (default: false — shortcuts only)
position: bottom-center # bottom-center | bottom-left | bottom-right
fontSize: 18 # default: 18
fadeAfter: 1500 # ms (default: 1500)
speedRamp:
enabled: false # default: false
idleSpeed: 3.0 # default: 3.0
actionSpeed: 0.8 # default: 0.8
watermark:
enabled: false # default: false
text: ""
position: bottom-right # top-left | top-right | bottom-left | bottom-right
opacity: 0.5 # 0-1 (default: 0.5)
# ─── Rules ────────────────────────────────────────────
# - Selectors: CSS selectors (#id, .class, [data-*], [aria-label="..."])
# - Unicode/CJK characters in selectors are supported
# - Click/type elements must be visible — scroll first if needed
# - Focus input with click before type action
# - Prefer async wait actions over fixed wait for reliability
# - waitForFunction: use for AI streaming, dynamic content, DOM changes
# - waitForResponse: set up before triggering action (auto-handled)
# - Timing: captureDelay 50-100ms, holdDuration 500-800ms for snappy demos
Install the built-in Claude Code skill and generate scenarios with natural language. One command to install, one slash command to use.
.claude/skills/ directory. Run once per project (or globally).npx clipwise install-skill
/clipwise in any Claude Code session. Describe what you want to record in plain language./clipwise
> Record a demo of my dashboard at http://localhost:3000
— click login, type credentials, navigate to analytics
npx clipwise validate, then records the MP4 — all automatically.After upgrading clipwise, re-run npx clipwise install-skill to get the latest skill.
All effects are optional with sensible defaults. Mix and match for your style.
zoom:
enabled: true
intensity: moderate # subtle|light|moderate|strong|dramatic
duration: 500
cursor:
enabled: true
speed: "fast"
trail: true
highlight: true
background:
type: gradient
value: "linear-gradient(135deg,
#667eea 0%, #764ba2 100%)"
padding: 48
borderRadius: 14
deviceFrame:
enabled: true
type: browser # or iphone/ipad/android
darkMode: true
showTyping: true to enable.keystroke:
enabled: true
showTyping: true # default: false
position: bottom-center
fontSize: 16
fadeAfter: 1500
speedRamp:
enabled: true
idleSpeed: 3.0
actionSpeed: 0.8