Skip to content

API Overview

API Reference

For full auto-generated details including all type signatures, see the API Reference.

defineConfig(config)

Full details

Defines the ScreenCI configuration. Wraps Playwright’s config with ScreenCI defaults and enforces settings required for reliable video recording.

Enforced settings (cannot be overridden):

  • workers: 1 — sequential execution
  • fullyParallel: false
  • retries: 0
  • testMatch — only *.video.* files
import { defineConfig } from 'screenci'
export default defineConfig({
videoDir: './videos', // Directory containing video test files (default: './videos')
use: {
videoOptions: {
resolution: '1080p', // '720p' | '1080p' | '4k' | { width, height }
fps: 30, // 24 | 30 | 60
quality: 'high', // 'low' | 'medium' | 'high'
},
trace: 'retain-on-failure', // 'on' | 'off' | 'retain-on-failure'
sendTraces: true,
},
})

Any other valid Playwright config options (e.g. timeout, reporter, webServer) are passed through.


video(title, body)

video(title, details, body)

Full details

The test fixture for declaring a recorded video. Works like Playwright’s test() with automatic screen recording and event tracking wired in.

import { video } from 'screenci'
video('Title of the video', async ({ page }) => {
await page.goto('https://example.com')
})

With Playwright test details:

video('Checkout flow', { tag: '@critical' }, async ({ page }) => {
await page.goto('https://example.com/checkout')
})

Fixtures available inside video()

All standard Playwright fixtures are available (page, context, browser, request), plus:

The page fixture returns a ScreenCIPage — a thin wrapper whose .locator() and related methods return ScreenCILocator instead of Playwright’s Locator, adding animated cursor and typed input behaviour.

FixtureTypeDescription
videoOptionsRecordOptionsCurrent video recording options

Overriding options per test

video.use({ videoOptions: { resolution: '4k', fps: 60 } })
video('4K demo', async ({ page }) => {
await page.goto('https://example.com')
})

autoZoom(fn, options?)

Full details

Zooms in on each interaction inside the callback, panning to follow clicks and fills. The camera zooms back out after the callback resolves.

Cannot be nested — calling autoZoom() inside another autoZoom() throws.

import { video, autoZoom } from 'screenci'
video('Settings demo', async ({ page }) => {
await page.goto('https://example.com/settings')
await autoZoom(
async () => {
await page.locator('#name').fill('Jane')
await page.locator('#email').fill('jane@example.com')
await page.locator('button[type="submit"]').click()
},
{ duration: 400, easing: 'ease-in-out', amount: 0.4 }
)
})

Options

OptionTypeDefaultDescription
durationnumber400Zoom-in and zoom-out transition duration in milliseconds
easingstring'ease-in-out'CSS easing for the zoom transitions
amountnumber0.5Fraction of output dimensions visible when zoomed (0–1)

hide(fn)

Full details

Hides recording events (mouse movements, clicks) while the callback runs. The hidden section is cut from the final video, making navigation and setup steps invisible to viewers.

Cannot be nested — calling hide() inside another hide() throws.

import { video, hide } from 'screenci'
video('Dashboard demo', async ({ page }) => {
await hide(async () => {
await page.goto('https://example.com/dashboard')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(2000)
})
// Recording resumes here — viewer sees the dashboard ready to go
await page.locator('#reports').click()
})

caption(content)

Full details

Displays a text caption on the video. Returns a CaptionHandle with methods to control timing. Only one caption can be active at a time — a second start()/until() call queues behind the first and waits for it to be ended.

Caption text appears word by word, with each word taking 0.5 seconds. Starting a caption emits a captionStart event; ending one emits a captionEnd event.

caption(text).start()

Resolves after all words have appeared (0.5s per word). The caption stays visible until caption.end() is called.

import { video, caption } from 'screenci'
video('Docs walkthrough', async ({ page }) => {
await caption('Navigating to the docs').start()
await page.goto('https://example.com/docs')
await caption.end()
await caption('Reading the introduction').start()
await page.waitForTimeout(3000)
await caption.end()
})

Awaiting is optional — if you don’t need to wait for the animation to finish before continuing, just call it without await.

caption(text).until(percent)

Resolves when the given percentage of the animation has elapsed. The full animation always runs to completion and the caption stays visible until caption.end() is called.

percent must be a Percentage string (\${number}%“). The editor will show a type error for anything else.

video('Feature walkthrough', async ({ page }) => {
// Resolves after 50% of words have appeared, then continues immediately
await caption('Clicking get started').until('50%')
await page.getByRole('link', { name: 'Get started' }).click()
await caption.end()
// Resolves immediately, caption animates in the background
caption('Loading dashboard').until('0%')
await page.waitForURL('**/dashboard')
await caption.end()
})
ValueResolves when
'0%'Immediately, before any word appears
'50%'After half the words have appeared
'100%'After all words have appeared (same as .start())

Throws at runtime if the value is not a finite number between 0 and 100.

caption.end()

Ends the currently active caption and emits a captionEnd event. Call it after every .start() or .until().

await caption('Step one').start()
await page.goto('https://example.com')
await caption.end()

Calling caption.end() when no caption is active is a no-op.

Queuing

A second caption call queues behind the first — its animation does not begin until the previous caption’s end() has been called:

caption('First caption').start() // begins immediately
caption('Second caption').start() // waits for first's end()
// ... do some work ...
await caption.end() // ends first, second begins
await vi.advanceTimersByTimeAsync(...)
await caption.end() // ends second

Behavior

  • One at a time: only one caption is active at a time. A second call queues rather than interrupting.
  • Word timing: each word takes 0.5 seconds to appear.
  • Awaiting is optional: all methods return promises but do not need to be awaited.

Types

RecordOptions

Full type details

type RecordOptions = {
resolution?: Resolution // default: '1080p'
fps?: FPS // default: 30
quality?: Quality // default: 'high'
}

RenderOptions

Full type details

Rendering options written as-is to data.json. Mirrors the renderOptions shape consumed by the rendering pipeline.

type RenderOptions = {
recording?: {
size?: number // 0-1: 1=one side touches background edge
roundness?: number // 0-1: 0=sharp corners, 1=shorter side is half circle
shape?: 'rounded' | 'squircle'
dropShadow?: string // CSS drop-shadow filter
}
voiceOvers?: {
size?: number // 0-1: 1=mask size equals shorter side of output
roundness?: number // 0-1: 0=square, 1=circle
shape?: 'rounded' | 'squircle'
dropShadow?: string // CSS drop-shadow filter
corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
padding?: number // 0-1
}
cursor?: {
size?: number // 0-1: 0=missing, 1=height of video
}
zooms?: {
easing?: string
duration?: number
}
output?: {
resolution?: '1280x720' | '1920x1080' | '3840x2160' | `${number}x${number}` // ...
background?: { assetPath: string } | { backgroundCss: string }
}
}

Resolution

type Resolution =
| '720p' // 1280×720
| '1080p' // 1920×1080
| '4k' // 3840×2160
| { width: number; height: number } // custom

FPS

Full type details

type FPS = 24 | 30 | 60
ValueUse case
24Cinematic look, smallest file size
30Standard — good balance of smoothness and size
60Smooth motion, best for fast interactions or scroll-heavy demos

Quality

Full type details

type Quality = 'low' | 'medium' | 'high'
ValueCRFDescription
'low'28Smaller files, lower visual fidelity
'medium'23Balanced
'high'18Best quality, larger files

Trace

Full type details

type Trace = 'on' | 'off' | 'retain-on-failure'

Controls Playwright trace recording. 'retain-on-failure' keeps traces only when tests fail.

ScreenCIConfig

Full type details

The config shape accepted by defineConfig. Extends Playwright’s config — the following fields are managed by ScreenCI and cannot be set directly: fullyParallel, workers, retries, testDir, testMatch.

type ScreenCIConfig = {
videoDir?: string
use?: {
videoOptions?: RecordOptions
trace?: Trace
sendTraces?: boolean
// ...all other Playwright use options
}
// ...all other Playwright config options
}