Video Script Basics
ScreenCI videos use the same syntax, guides, and general best practices as Playwright tests. The main difference is that video scripts usually do not need assertions, and instead focus on viewer-facing behavior such as narration and camera movement.
Need a faster way to get a first draft? Start with Generating Videos, then come back here to refine the script structure and ScreenCI-specific APIs.
You will learn
- how a ScreenCI video differs from a Playwright test
- how to configure ScreenCI
- which core ScreenCI APIs to use
ScreenCI video vs Playwright test
import { autoZoom, createNarration, hide, video, voices } from 'screenci'
// Define narration lines, including localized variants.const narration = createNarration({ voice: { name: voices.Sophie }, en: { docs: 'Here is where to find ScreenCI [pronounce: screen see eye] docs.', }, es: { docs: 'Aqui es donde encontrar la documentacion de ScreenCI [pronounce: screen see eye].', },})
video('How to find docs', async ({ page }) => { // Run setup without showing these actions in the final recording. await hide(async () => { await page.goto('https://screenci.com/') await page.waitForLoadState('networkidle') })
// Play the matching narration line for this step. await narration.docs()
// Automatically zoom into interactions so they are easier to follow. await autoZoom(async () => { await page.getByRole('link', { name: 'View Documentation' }).click() })})You can define multiple video() calls in the same file, or create multiple
.video.ts files under videos/.
Configure ScreenCI
Project-wide defaults such as projectName, videoDir, baseURL, and shared
recording or rendering options live in screenci.config.ts.
import { defineConfig } from 'screenci'
export default defineConfig({ // Used to identify this project in ScreenCI. projectName: 'my-product',})See Configuration.
Core ScreenCI APIs
hide()
Use hide() to keep setup out of the visible recording. This is useful for
steps the viewer does not need to watch, such as navigating to the right page,
signing in, or dismissing a cookie banner before the real flow begins. See
Setup vs visible sequence and Generating
Videos.
hide() takes just the function to run. It does not have separate timing or
camera options.
await hide(async () => { await page.goto('/settings') await page.getByRole('button', { name: 'Accept all cookies' }).click() await page.getByRole('button', { name: 'Open billing' }).click()})API reference: hide()
autoZoom()
Use autoZoom() when the camera should follow a visible interaction
automatically. See Camera and Zooming.
Common options:
durationto control how fast the zoom moveseasingto control motion feelamountto control how tightly ScreenCI zooms inpaddingto keep more space around the target areacenteringto bias framing within the viewportpreZoomDelayandpostZoomDelayto add breathing room before or after the zoomed sequence
await autoZoom(async () => { await page.getByRole('button', { name: 'Create project' }).click()})API reference: autoZoom()
zoomTo()
Use zoomTo() when you want exact manual framing, and resetZoom() when you
want to return to the default view afterward. See Camera and
Zooming.
zoomTo() accepts either:
- a locator, when you want framing tied to a real UI element
{ x, y }, when you want to frame an exact point manually
Common options for zoomTo() and resetZoom():
durationeasingamountpaddingpreZoomDelaypostZoomDelay
await zoomTo(page.getByTestId('pricing-card-pro'))await page.getByRole('button', { name: 'Upgrade' }).click()await resetZoom()API reference: zoomTo(), resetZoom()
createNarration()
Use createNarration() to define narration cues and language variants. See
Narration and Localization.
Common options:
- top-level
voicefor the default voice configuration - language keys such as
en,es, orfi - per-language
voiceoverrides when one language needs a different voice - cue entries as text or file-based entries, depending on how you want to source narration
const narration = createNarration({ // Define one cue key and provide the matching text for each language. voice: { name: voices.Sophie }, en: { intro: 'Open settings and review the billing details.', }, es: { intro: 'Abre la configuracion y revisa los detalles de facturacion.', },})
video('Billing walkthrough', async ({ page }) => { // Play the full cue before continuing. await narration.intro()
// Or use start/end when narration should overlap with the visible actions. await narration.intro.start() await page.goto('/settings') await page.getByRole('button', { name: 'Open billing' }).click() await narration.intro.end()})API reference: createNarration(), voices