Ink Arrow

A hand-drawn arrow that draws itself from one point to another in stop-motion poses

Installation
$ pnpm dlx shadcn@latest add @remocn/ink-arrow

Usage

from and to are in the arrow's own local space — you place the component, the arrow draws inside it. The SVG lets its contents overflow, so the bow and the head are never clipped.

tsx
// src/Root.tsx
import { AbsoluteFill, Composition } from "remotion";
import { InkArrow } from "@/components/remocn/ink-arrow";
 
const InkArrowScene = () => (
  <AbsoluteFill style={{ background: "#f1eee7" }}>
    <h1 style={{ position: "absolute", left: 200, top: 150 }}>Start here</h1>
 
    <div style={{ position: "absolute", left: 300, top: 210 }}>
      <InkArrow from={{ x: 20, y: 20 }} to={{ x: 300, y: 210 }} />
    </div>
 
    <div style={{ position: "absolute", left: 600, top: 400 }}>
      <YourCard />
    </div>
  </AbsoluteFill>
);
 
export const RemotionRoot = () => (
  <Composition
    id="InkArrow"
    component={InkArrowScene}
    durationInFrames={120}
    fps={30}
    width={1280}
    height={720}
  />
);

The pen drags the curve over drawDur frames, quantized to the stop-motion clock. When the line lands, the head is drawn rather than dropped in: one stroke out from the tip, then the other, over headDur frames split evenly between them.

curvature is the perpendicular bow as a fraction of the distance between the two points, so the arc keeps its shape as the arrow gets longer. Flip its sign to bow the other way. Each control point also carries a small per-seed wobble, which is what stops the curve looking machine-drawn; the same seed always gives the same hand.

Props14

PropTypeDefault
fromrequired
{ x: number; y: number }—

Start point, in the component's local space

torequired
{ x: number; y: number }—

End point — where the head lands

curvature
number0.35

Perpendicular bow as a fraction of the distance; negative bows the other way

color
string"#26242c"

Ink colour (any valid CSS color)

strokeWidth
number8

Brush width in pixels at full pressure

pressure
number0.2

Weight the brush opens on, as a fraction of strokeWidth

release
number1

Weight the brush reaches at the head; above pressure the shaft thickens as it travels

grain
number1

Edge roughness; breaks the thin opening into dry-brush flecks, 0 gives clean edges

delay
number0

Frames on the local clock before the drag begins

drawDur
number36

Frames the contour takes to draw

headSize
number24

Length of each head stroke in pixels; a floor, never shrinking below what the brush width needs

headDur
numberstep * 4

Frames to draw both head strokes, split evenly between them

seed
string"arrow"

Seed for the curve wobble — same seed, same arrow

step
number3

Frames per stop-motion pose — the tempo control

  • fromrequired{ x: number; y: number }

    Start point, in the component's local space

  • torequired{ x: number; y: number }

    End point — where the head lands

  • curvaturenumber

    Perpendicular bow as a fraction of the distance; negative bows the other way

    Default0.35

  • colorstring

    Ink colour (any valid CSS color)

    Default"#26242c"

  • strokeWidthnumber

    Brush width in pixels at full pressure

    Default8

  • pressurenumber

    Weight the brush opens on, as a fraction of strokeWidth

    Default0.2

  • releasenumber

    Weight the brush reaches at the head; above pressure the shaft thickens as it travels

    Default1

  • grainnumber

    Edge roughness; breaks the thin opening into dry-brush flecks, 0 gives clean edges

    Default1

  • delaynumber

    Frames on the local clock before the drag begins

    Default0

  • drawDurnumber

    Frames the contour takes to draw

    Default36

  • headSizenumber

    Length of each head stroke in pixels; a floor, never shrinking below what the brush width needs

    Default24

  • headDurnumber

    Frames to draw both head strokes, split evenly between them

    Defaultstep * 4

  • seedstring

    Seed for the curve wobble — same seed, same arrow

    Default"arrow"

  • stepnumber

    Frames per stop-motion pose — the tempo control

    Default3