Scriptable · Cinematic · Zero Config

YAML in.
Polished MP4 out.

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.

npx clipwise demo
View on GitHub npm
Features

Everything you need for demo videos

Write a YAML scenario, hit record — Clipwise handles all the cinematic magic.

📄
YAML Scenarios
Describe your demo in a simple YAML file. Define steps, actions, timing, and effects — no code required.
🎬
Cinematic Effects
Adaptive zoom, cursor trails, click ripples, speed ramp, background gradients, and fade transitions.
📱
Device Frames
Wrap recordings in browser, iPhone, iPad, or Android mockups with a single config line.
Zero Config Demo
Run npx clipwise demo to instantly record the built-in dashboard — no setup needed.
🔧
CLI + Programmatic API
Use the CLI for quick recordings or integrate via the TypeScript API for advanced workflows.
🎯
Playwright-Powered
Pixel-perfect frame capture via Playwright CDP. Consistent results across every run.
Fast Pipeline
Concurrent streaming pipeline + frame deduplication. −28% total time vs v0.3 on Apple M1 Max — 92s for a 44s demo at 1280×800. Smooth cursor movement with automatic CSS transition suppression.
Quick Start

Up and running in 60 seconds

Install Clipwise, run the demo, or write your own scenario.

1
Install
Add Clipwise as a dev dependency (or use npx without installing).
bash
npm install -D clipwise
2
Try the built-in demo
Records the built-in dashboard with cinematic effects. No config needed.
bash
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
3
Create your own scenario
Generate a template, edit the URL, and record your site.
bash
npx clipwise init                             # Creates clipwise.yaml
# Edit clipwise.yaml — set URL to your site
npx clipwise record clipwise.yaml -f mp4      # Record!

Programmatic API

typescript
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);
YAML Format

Scenario structure

A scenario has 4 sections: metadata, effects, output, and steps.

clipwise.yaml
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

Basic Actions

Action Parameters Default Description
navigateurl, waitUntil?networkidleNavigate to URL
clickselector, delay?, timeout?Click an element
typeselector, text, delay?, timeout?delay: 50Type text (char-by-char)
hoverselector, timeout?Hover over element
scrolly?, x?, selector?, smooth?, timeout?smooth: trueScroll by offset
waitdurationWait (ms)
screenshotname?, fullPage?fullPage: falseCapture marker

Async Wait Actions

Action Parameters Default Description
waitForSelectorselector, state?, timeout?visible, 15sWait for element state
waitForNavigationwaitUntil?, timeout?networkidle, 15sWait for page load
waitForURLurl, timeout?15sWait for URL match
waitForFunctionexpression, polling?, timeout?raf, 30sWait for JS expression to be truthy
waitForResponseurl, status?, timeout?30sWait for network response (URL substring match)
yaml — Async wait examples
# 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
AI-Ready

Full schema for AI tools

Copy the complete YAML schema reference. Paste into Claude Code, Cursor, ChatGPT, or any AI assistant to generate scenarios instantly.

Complete Schema Reference

This block contains everything an AI needs: all 12 action types with parameters, defaults, effects config, output options, and usage rules.

yaml — Clipwise Full Schema Reference
# 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
Claude Code

AI-powered workflow

Install the built-in Claude Code skill and generate scenarios with natural language. One command to install, one slash command to use.

1
Install the skill
Copies the Clipwise skill to your .claude/skills/ directory. Run once per project (or globally).
bash
npx clipwise install-skill
2
Use in Claude Code
Type /clipwise in any Claude Code session. Describe what you want to record in plain language.
Claude Code
/clipwise
> Record a demo of my dashboard at http://localhost:3000
  — click login, type credentials, navigate to analytics
3
Claude handles the rest
Claude generates a complete YAML scenario, validates it with npx clipwise validate, then records the MP4 — all automatically.

After upgrading clipwise, re-run npx clipwise install-skill to get the latest skill.

Effects

Cinematic effects

All effects are optional with sensible defaults. Mix and match for your style.

Zoom
Adaptive zoom follows the cursor and zooms in on click targets automatically. Use intensity presets (subtle → dramatic) or set a numeric scale.
followCursor intensity autoZoom
zoom:
  enabled: true
  intensity: moderate  # subtle|light|moderate|strong|dramatic
  duration: 500
Cursor
Custom cursor with click ripple, trail, glow highlight, and speed control.
trail highlight clickEffect
cursor:
  enabled: true
  speed: "fast"
  trail: true
  highlight: true
Background
Gradient or solid padding with rounded corners and drop shadow.
gradient shadow
background:
  type: gradient
  value: "linear-gradient(135deg,
    #667eea 0%, #764ba2 100%)"
  padding: 48
  borderRadius: 14
Device Frame
Wrap recordings in a device mockup — browser, iPhone, iPad, or Android.
browser iphone android
deviceFrame:
  enabled: true
  type: browser   # or iphone/ipad/android
  darkMode: true
Keystroke HUD
Shows what was typed at the bottom of the screen. Multi-session: each input field gets its own line (up to 3, oldest dimmed). Off by default — set showTyping: true to enable.
bottom-center multi-session
keystroke:
  enabled: true
  showTyping: true   # default: false
  position: bottom-center
  fontSize: 16
  fadeAfter: 1500
Speed Ramp
Slows down near clicks and speeds up idle sections automatically.
idleSpeed actionSpeed
speedRamp:
  enabled: true
  idleSpeed: 3.0
  actionSpeed: 0.8