Skip to content

Demo Video Recording

Automated Playwright scripts that record product demo videos for the landing page and marketing.


Prerequisites

  • Backend running at localhost:8000
  • Frontend running at localhost:3000
  • A pre-analyzed project with edits and at least one asset overlay
  • Chromium installed: cd e2e && npm install && npx playwright install chromium

Quick Start

cd e2e

# Record all three demos
npx playwright test demo/desktop.spec.ts --headed
npx playwright test demo/mobile-portrait.spec.ts --headed
npx playwright test demo/mobile-landscape.spec.ts --headed

# Convert WebM to MP4
./record-demos.sh

Output goes to e2e/demo-videos/.


Seeding a Demo Project

If you need to set up a fresh demo project from scratch:

  1. Place longer_video.MOV in the project root (not in git — 1.2GB)
  2. Optionally place an overlay image (PNG) for asset testing
  3. Run the seed script:
cd e2e

# With asset overlay
DEMO_ASSET=../path/to/overlay.png ./seed-demo-project.sh

# Without asset (skips overlay insertion)
./seed-demo-project.sh

The seed script: - Logs in as admin (admin@example.com / password from env) - Creates a project named "longer_video" - Uploads the video via presigned URL - Runs analysis (pacing 50, false start sensitivity 50, pt-BR) - Optionally uploads an asset image and inserts it as an overlay at ~2:31

After seeding, open localhost:3000, log in, and resume the project so localStorage is populated.


Demo Scripts

Desktop (demo/desktop.spec.ts)

Viewport: 1920x1080, 2x DPR, visible cursor

What it records (~80s):

  1. Login → Projects list → Resume project
  2. Play video with captions visible
  3. Click edit card → popover (ACCEPT/DISMISS)
  4. FALSE START filter toggle
  5. Expanded timeline (E key)
  6. Click + drag-resize edit in expanded mode
  7. Undo
  8. Zoom in/out on expanded timeline
  9. Asset overlay: click blue asset on waveform → overlay appears with handles → resize → drag to reposition → undo
  10. Loop viewport toggle
  11. A/B Original/Edited toggle
  12. Add manual cut + undo
  13. Format switching (Vertical → Square → Original)
  14. Caption position change (top → bottom)
  15. Caption font change
  16. Captions panel open/close
  17. Export button
  18. Final playback

Mobile Portrait (demo/mobile-portrait.spec.ts)

Viewport: 390x844 (iPhone 12 Pro), touch events

What it records (~40s):

  1. Login → Projects → open project
  2. Play video with captions
  3. Accept/dismiss edits via buttons
  4. CutFocusMode: play, loop toggle, close
  5. FocusMode via Layers button: zoom in, CUT/KEEP toggle, +CUT/undo
  6. CAPTIONS tab
  7. STYLE tab: caption position change
  8. FORMAT tab: 9:16, 1:1

Mobile Landscape (demo/mobile-landscape.spec.ts)

Viewport: 844x390 (iPhone 12 Pro landscape), touch events

What it records (~50s):

  1. SwipeReview landscape layout (video left, content right)
  2. Play video
  3. Accept/dismiss edits
  4. FORMAT tab: 9:16, 16:9
  5. FocusMode landscape: zoom in (8x), click edit, CUT/KEEP, +CUT/undo
  6. Navigate to asset card → expand → AssetFocusMode
  7. Overlay: drag to reposition, resize via corner drag
  8. Visual mode switching (OVERLAY → FULL → OVERLAY)

Note: Uses a two-context strategy — logs in via portrait context first to set cookies/localStorage, then opens landscape context for recording.


Architecture

Helpers (demo/helpers.ts)

Function Purpose
login(page, email, password) Fill credentials + submit + wait for dashboard
injectCursor(page) Add visible orange cursor circle (desktop only)
smoothClick(page, selector) Move cursor smoothly then click
smoothClickAt(page, x, y) Move to coordinates then click
smoothDrag(page, from, to) Smooth drag between two points
smoothTap(page, selector) Tap for touch devices
swipe(page, ...) Simulate swipe gesture
pause(page, ms) Wait for viewer to absorb

Play Button Overlay Issue

The mobile video player has a full-screen Play button overlay (z-10, absolute inset-0) that intercepts pointer events. This blocks Playwright's elementHandle.tap() for buttons behind it (Layers, zoom, nav arrows).

Solution: Use page.evaluate() to call .click() directly on the DOM element, bypassing Playwright's pointer event interception check. All mobile FocusMode interactions use this pattern:

await page.evaluate(() => {
  const btn = document.querySelector('[aria-label="Timeline overview"]') as HTMLElement;
  if (btn) btn.click();
});

Credentials

Scripts read DEMO_EMAIL and DEMO_PASSWORD env vars, defaulting to admin@example.com / TestPassword123!.


Post-Processing

The record-demos.sh script converts WebM to MP4:

ffmpeg -y -i input.webm \
  -c:v libx264 -crf 18 -preset slow \
  -pix_fmt yuv420p \
  -movflags +faststart \
  output.mp4

For trimming or speed adjustment:

# Trim to 3s-25s
ffmpeg -i demo.mp4 -ss 00:00:03 -to 00:00:25 -c copy trimmed.mp4

# 2x speed
ffmpeg -i demo.mp4 -filter:v "setpts=0.5*PTS" -an fast.mp4