Trail canvas
A continuous smear drawn on one canvas from a fixed particle pool. The pool is allocated at mount and the render loop only mutates it, so there is nothing for the garbage collector to reclaim mid-drag.
Live props — bound to the real component
Install
npx shadcn@latest add @pointerui/trail-canvas# Copy three folders out of the repo
src/lib/engine/ -> lib/pointerui/engine/
src/registry/provider/ -> components/pointerui/provider/
src/registry/variants/trail-canvas/-> components/pointerui/trail-canvas/
# Then fix the two aliases the CLI would have rewritten
# @/lib/engine -> @/lib/pointerui/engine
# ../../provider -> @/components/pointerui/providerimport { createEngine } from "./lib/pointerui/engine";
// The engine has no React imports at all, so it runs anywhere.
const engine = createEngine();
const el = document.createElement("div");
document.body.append(el);
const layer = engine.registerLayer(el, { duration: 0.15, ease: "power3" });
const stop = engine.onTick((state) => {
layer.setX(state.x);
layer.setY(state.y);
});
// TrailCanvas's own layer setup is the same call with its own options.Usage
import { CursorProvider } from "@/components/pointerui/provider";
import { TrailCanvas } from "@/components/pointerui/trail-canvas";
export default function Page() {
return (
<CursorProvider>
<TrailCanvas />
<button data-cursor="grow">Hover me</button>
</CursorProvider>
);
}Props
Generated from trail-canvas/types.ts and the component's own defaults, so this table cannot drift from the code you are about to copy.
| Prop | Type | Default | Description |
|---|---|---|---|
| count | number | 24 | Particle pool size. Allocated once at mount — the render loop never creates an object, so there is nothing for the GC to collect mid-drag. |
| decay | number | 0.92 | Per-frame life multiplier, 0–1. Lower decays the trail faster. |
| radius | number | 11 | Radius of a particle at full life, in px. |
| color | string | "var(--cursor-color, currentColor)" | Any CSS colour. Defaults to |
| blendMode | GlobalCompositeOperation | "lighter" | Canvas compositing mode, and the one prop you must set for light backgrounds.
|
| alpha | number | 0.55 | Peak particle alpha. Light backgrounds usually want this higher. |
Data attributes
Mark any element with one of these and this variant responds when the pointer enters it. Values it does not understand are ignored.
- data-cursor="hide"
- Fades the canvas out.
Notes & caveats
blendModeis the one prop you must set for light backgrounds.lighteradds colour, which glows on dark surfaces and is invisible on white — adding to white stays white.- The backing store is sized to
devicePixelRatio. Skipping that is the usual reason a canvas trail looks soft on a retina display.