Elastic Cursor

An elastic ball that follows the cursor with a smooth transition effect.

Example

Move your cursor around

Installation

Copy and paste the source code into your project.

"use client";
 
import React, { useEffect, useRef } from "react";
 
type Props = {
  speed?: number;
};
 
/**
 * ElasticCursor component.
 *
 * An elastic ball that follows the cursor with a smooth transition effect.
 *
 * @param {number} [speed=0.1] - Smoothing factor for cursor movement speed (0.1 = smooth, 1 = instant)
 */
export const ElasticCursor = ({ speed = 0.1 }: Props) => {
  const circleRef = useRef<HTMLDivElement>(null);
 
  useEffect(() => {
    const circleElement = circleRef.current;
 
    if (!circleElement) {
      return;
    }
 
    const mouse = { x: 0, y: 0 };
    const previousMouse = { x: 0, y: 0 };
    const circle = { x: 0, y: 0 };
 
    let currentScale = 0;
    let currentAngle = 0;
 
    const handleMouseMove = (e: MouseEvent) => {
      mouse.x = e.clientX;
      mouse.y = e.clientY;
    };
 
    window.addEventListener("mousemove", handleMouseMove);
 
    const tick = () => {
      if (!circleElement) return;
 
      const circleRect = circleElement.getBoundingClientRect();
      const circleRadius = circleRect.width / 2;
 
      circle.x += (mouse.x - circle.x) * speed;
      circle.y += (mouse.y - circle.y) * speed;
      const translateTransform = `translate(${circle.x - circleRadius}px, ${circle.y - circleRadius}px)`;
 
      const deltaMouseX = mouse.x - previousMouse.x;
      const deltaMouseY = mouse.y - previousMouse.y;
      previousMouse.x = mouse.x;
      previousMouse.y = mouse.y;
      const mouseVelocity = Math.min(
        Math.sqrt(deltaMouseX ** 2 + deltaMouseY ** 2) * 4,
        150
      );
      const scaleValue = (mouseVelocity / 150) * 0.5;
      currentScale += (scaleValue - currentScale) * speed;
      const scaleTransform = `scale(${1 + currentScale}, ${1 - currentScale})`;
 
      const angle = (Math.atan2(deltaMouseY, deltaMouseX) * 180) / Math.PI;
      if (mouseVelocity > 20) {
        currentAngle = angle;
      }
      const rotateTransform = `rotate(${currentAngle}deg)`;
 
      circleElement.style.transform = `${translateTransform} ${rotateTransform} ${scaleTransform}`;
 
      window.requestAnimationFrame(tick);
    };
 
    window.requestAnimationFrame(tick);
 
    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
    };
  }, [speed]);
 
  return (
    <div
      ref={circleRef}
      className="w-10 h-10 rounded-full fixed top-0 left-0 pointer-events-none border border-slate-500 z-50"
    ></div>
  );
};

    Update the import paths to match your project setup.

    Props

    PropTypeDefaultDescription
    speednumber0.1Smoothing factor for cursor movement speed (0.1 = smooth, 1 = instant)