Pipeline Stages

Cursor Overlay

Add an animated cursor that moves to click positions in the video.

The .cursorOverlay() stage renders an animated cursor that appears before each click, moves to the click position with an ease-out animation, then disappears. It gives viewers a clear visual indicator of where interactions happen.

Basic usage

await Recast
  .from('./traces')
  .parse()
  .cursorOverlay()
  .render({ format: 'mp4' })
  .toFile('demo.mp4')

Configuration options

OptionTypeDefaultDescription
imagestringbundled arrow cursorPath to a custom cursor image (PNG)
sizenumber30Cursor width in pixels (relative to 1080p)
approachMsnumber500Hold (freeze) duration for marker-driven clicks — see Held cursor approach

Custom cursor image

.cursorOverlay({
  image: './assets/custom-cursor.png',
  size: 40,
})

The bundled default is a 30x44 arrow cursor PNG. Provide your own image for branded or stylized cursors.

approachMs — held cursor approach

By default the cursor's approach is timed off each click action detected in the trace. For clicks that wait on a page load, that approach can glide over a still-loading screen before the target paints — "the mouse moves before there's anything to click on."

The click() / markClick() test helpers fix this by writing an explicit click marker. For each marker, the renderer holds (freezes) the painted frame at the click for approachMs and lets the cursor play its full glide over it, then resumes into the click's result. approachMs defaults to 500 ms (it must be at least the cursor's pre-click span so the glide fits inside the hold).

.cursorOverlay({ approachMs: 600 })

approachMs only affects marker-driven clicks; plain auto-detected clicks are unaffected. With a .voiceover() stage, the hold also extends the audio and subtitles in lockstep so narration stays in sync.

How it works

For each click action detected in the trace:

  1. The cursor appears near the click target
  2. It moves to the exact click position with an ease-out animation
  3. It disappears after the click

The animation uses ffmpeg movie + overlay filters with per-click enable expressions and ease-out movement calculated via st()/ld() temp variables.

CLI equivalent

# Enable with defaults
npx playwright-recast -i ./traces --cursor-overlay

# With custom config
npx playwright-recast -i ./traces --cursor-overlay-config cursor.json

Tips

  • Cursor overlay works well combined with Click Effect — the cursor moves into position, then the ripple appears on click.
  • Use the click() helper in your test for the clicks you want to emphasise — the renderer gives those a held, deliberate approach (approachMs) over the painted target.
  • Cursor positions are remapped through speed processing, so the animation timing stays accurate after speed changes.
  • Only actions from the recording context are processed — setup/background actions are filtered out automatically.
  • The overlay is applied before zoom cropping, so cursor positions stay accurate during zoomed segments.

On this page