# Simulated Cursor
URL: https://remocn.dev/docs/effects/simulated-cursor

A synthetic mouse cursor that travels between waypoints and fires a click ripple, for narrating a UI on screen

- Install: `npx shadcn@latest add @remocn/simulated-cursor`
- Vibe: clean
- Natural length: 150f @ 30fps

## Installation

```bash
npx shadcn@latest add @remocn/simulated-cursor
```

## Usage

The cursor walks the `points` array in order. Each leg takes a fixed 24 frames of travel, then rests
for that point's `hold` (default `15`). A point marked `click` fires a ripple when the cursor
arrives.

```tsx
// src/Root.tsx
import { Composition } from "remotion";
import { SimulatedCursor } from "@/components/remocn/simulated-cursor";

const CursorScene = () => (
  <SimulatedCursor
    points={[
      { x: 200, y: 150, hold: 20 },
      { x: 800, y: 360, hold: 25, click: true },
      { x: 1050, y: 560, hold: 20 },
    ]}
    color="#171717"
    size={28}
  />
);

export const RemotionRoot = () => (
  <Composition
    id="SimulatedCursor"
    component={CursorScene}
    durationInFrames={150}
    fps={30}
    width={1280}
    height={720}
  />
);
```

> **Coordinates are canvas pixels**
>
> `x` and `y` are absolute pixels on the composition canvas, not normalized `0–1` values. On the
>   standard `1280×720` canvas, `{ x: 640, y: 360 }` is dead center. Change the canvas size and the
>   waypoints move with it.

### Over a screenshot

The component renders transparent, so layer it above whatever it is pointing at.

```tsx
import { AbsoluteFill, Img, staticFile } from "remotion";
import { SimulatedCursor } from "@/components/remocn/simulated-cursor";

const DemoScene = () => (
  <AbsoluteFill>
    <Img src={staticFile("dashboard.png")} style={{ width: "100%", height: "100%" }} />
    <SimulatedCursor
      points={[
        { x: 320, y: 210, hold: 18 },
        { x: 760, y: 420, hold: 24, click: true },
      ]}
      color="#171717"
    />
  </AbsoluteFill>
);
```

## Timing

Budget the `Sequence` from the waypoints: `24` frames per leg plus each point's `hold`. The default
three-point path runs about `93` frames; leave headroom for the click ripple to finish.

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `points` | `CursorPoint[]` | `DEFAULT_POINTS` | Waypoints the cursor travels through, in order. Each is { x, y, hold?, click? } in absolute canvas pixels. |
| `color` | `string` | `"#ffffff"` | Fill color of the cursor arrow. Use a dark value over light UI screenshots. |
| `size` | `number` | `32` | Height of the cursor arrow in pixels. |
| `speed` | `number` | `1` | Time multiplier applied as frame × speed. Above 1 the cursor completes its path sooner. |
| `className` | `string` | — | Optional className passed to the wrapper element. |

### CursorPoint

| Prop | Type | Default | Description |
|---|---|---|---|
| `x` (required) | `number` | — | Horizontal position in canvas pixels. |
| `y` (required) | `number` | — | Vertical position in canvas pixels. |
| `hold` | `number` | `15` | Frames the cursor rests at this point after arriving, before starting the next leg. |
| `click` | `boolean` | `false` | Fires a click ripple when the cursor arrives at this point. |

## Use when
- You are narrating a screen demo and need a synthetic cursor that moves predictably over a UI screenshot or recording
- You want to direct attention to a specific element by having the cursor travel to it and click
- You are pairing it with `typewriter` to simulate a user typing into a field the cursor just landed on

## Don't use when
- You need a real screen recording — this draws a synthetic overlay, not a capture of actual mouse movement
- The video is abstract or non-UI (motion graphics, product reveal, pure text) — a floating arrow reads as a tutorial and breaks the mood
- The underlying layout shifts between scenes — waypoints are absolute canvas pixels, so the cursor will land in the wrong place