Pointer trail

← back to next-sketch
Move the pointer to draw, click to burst a ring — demonstrates onPointerMove/onPointerDown.
View the equivalent next-sketch (React) code
'use client';

import { useRef } from 'react';
import { CanvasSketch, clamp, lerpColor } from 'next-sketch';

interface Point { x: number; y: number; age: number; burst?: boolean }

const COOL: [number, number, number] = [99, 102, 241];
const WARM: [number, number, number] = [239, 68, 68];

export default function PointerTrail() {
  const points = useRef<Point[]>([]);

  return (
    <CanvasSketch
      style={{ width: '100%', height: 400 }}
      setup={({ ctx, width, height }) => {
        ctx.fillStyle = '#0f172a';
        ctx.fillRect(0, 0, width, height);
      }}
      onPointerMove={({ x, y }) => {
        points.current.push({ x, y, age: 0 });
      }}
      onPointerDown={({ x, y }) => {
        points.current.push({ x, y, age: 0, burst: true });
      }}
      draw={({ ctx, width, height, dt }) => {
        ctx.fillStyle = 'rgba(15, 23, 42, 0.25)';
        ctx.fillRect(0, 0, width, height);

        points.current = points.current.filter((p) => p.age < 1);
        for (const p of points.current) {
          p.age += dt * (p.burst ? 0.6 : 1.4);
          const t = clamp(p.age, 0, 1);
          ctx.strokeStyle = lerpColor(COOL, WARM, t);
          ctx.lineWidth = p.burst ? 2 : 1;
          ctx.beginPath();
          ctx.arc(p.x, p.y, p.burst ? t * 60 : 3 + t * 10, 0, Math.PI * 2);
          ctx.stroke();
        }
      }}
    />
  );
}