Pen Cursor

Physics-driven ribbon trail rendered on canvas — a chain of linked points follows the mouse with spring-damping inertia, width scales with speed, and colors interpolate head-to-tail. Inspired by obsidianassembly.com.

UniqueUI CLI

npx uniqueui add pen-cursor

shadcn CLI

npx shadcn@latest add https://uniqueui.com/r/pen-cursor.json -y

shadcn path expects @/lib/utils (run shadcn init first). Same source file is installed to components/ui/.

Preview

Ribbon trail

Move your mouse — a physics-driven ribbon trails the pointer.

Usage

"use client";
import { useRef } from "react";
import { PenCursor } from "@/components/ui/pen-cursor";

export default function Example() {
  const containerRef = useRef<HTMLDivElement>(null);
  return (
    <div
      ref={containerRef}
      className="h-[400px] w-full relative bg-neutral-950 overflow-hidden flex items-center justify-center"
    >
      <h3 className="text-white text-2xl font-bold uppercase tracking-widest pointer-events-none">
        Move your mouse
      </h3>
      <PenCursor
        containerRef={containerRef}
        trailLength={40}
        maxWidth={1}
        colorHead="159, 175, 155"
        colorTail="198, 167, 106"
        alphaHead={0.95}
        damping={0.55}
      />
    </div>
  );
}

Props

PropTypeDescription
trailLengthnumberNumber of chain-linked trail points. Default 60.
maxWidthnumberMaximum ribbon width in px (scales with speed). Default 1.
minWidthnumberMinimum ribbon width in px at the tail. Default 1.
dampingnumberSpring damping for the head point (0–1). Default 0.6.
speedInfluencenumberHow much mouse speed widens the ribbon (0–1). Default 0.9.
colorHeadstringRGB string for the ribbon head color. Default "159, 175, 155" (sage green).
colorTailstringRGB string for the ribbon tail color. Default "198, 167, 106" (warm gold).
alphaHeadnumberOpacity at the ribbon head (0–1). Default 0.9.
alphaTailnumberOpacity at the ribbon tail (0–1). Default 0.
hideSystemCursorbooleanHide the system cursor while mounted. Default false.
containerRefRefObject<HTMLElement | null>Optional. Limits drawing to this element; parent should be `relative`. Omit for full-window tracking.
classNamestringExtra classes on the canvas element.