Free Web Design Code & Scripts

Elastic Custom Cursor in Vanilla JavaScript

Elastic Custom Cursor In Vanilla Javascript
Code Snippet:Elastic Custom Cursor Following Mouse (Squeeze and Rotate) w/ JavaScript
Author: Denis Gusev
Published: 5 days ago
Last Updated: November 23, 2025
Downloads: 11
License: MIT
Edit Code online: View on CodePen
Read More

This tutorial will guide you through the process of creating an elastic custom cursor using vanilla JavaScript. This enhances user experience by providing a visually engaging and interactive cursor that responds dynamically to mouse movements, adding a unique touch to your web projects.

Setting Up the HTML Structure

First, we’ll establish the basic HTML structure needed for our custom cursor. This involves creating a simple div element that will represent our cursor.

<div class="circle"></div>
    <script  src="./script.js"></script>

Styling the Custom Cursor with CSS

Next, we’ll use CSS to style our custom cursor, defining its appearance and behavior. This includes setting its size, color, and position on the screen.

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

body {
  min-height: 100lvh;
  background-color: #050908;
}

.circle {
  --circle-size: 40px;
  position: fixed;
  height: var(--circle-size);
  width: var(--circle-size);
  border: 1px solid white;
  border-radius: 100%;
  top: calc(var(--circle-size) / 2 * -1);
  left: calc(var(--circle-size) / 2 * -1);
  pointer-events: none;
}

Implementing the Elastic Behavior with JavaScript

Now, we’ll use JavaScript to create the elastic effect. This involves tracking the mouse position and dynamically updating the cursor’s position, scale, and rotation based on the mouse’s movement.

/**
 * YouTube Tutorial:
 * https://youtu.be/wG_5453Vq98
 */

console.clear();

// Select the circle element
const circleElement = document.querySelector('.circle');

// Create objects to track mouse position and custom cursor position
const mouse = { x: 0, y: 0 }; // Track current mouse position
const previousMouse = { x: 0, y: 0 } // Store the previous mouse position
const circle = { x: 0, y: 0 }; // Track the circle position

// Initialize variables to track scaling and rotation
let currentScale = 0; // Track current scale value
let currentAngle = 0; // Track current angle value

// Update mouse position on the 'mousemove' event
window.addEventListener('mousemove', (e) => {
  mouse.x = e.x;
  mouse.y = e.y;
});

// Smoothing factor for cursor movement speed (0 = smoother, 1 = instant)
const speed = 0.17;

// Start animation
const tick = () => {
  // MOVE
  // Calculate circle movement based on mouse position and smoothing
  circle.x += (mouse.x - circle.x) * speed;
  circle.y += (mouse.y - circle.y) * speed;
  // Create a transformation string for cursor translation
  const translateTransform = `translate(${circle.x}px, ${circle.y}px)`;

  // SQUEEZE
  // 1. Calculate the change in mouse position (deltaMouse)
  const deltaMouseX = mouse.x - previousMouse.x;
  const deltaMouseY = mouse.y - previousMouse.y;
  // Update previous mouse position for the next frame
  previousMouse.x = mouse.x;
  previousMouse.y = mouse.y;
  // 2. Calculate mouse velocity using Pythagorean theorem and adjust speed
  const mouseVelocity = Math.min(Math.sqrt(deltaMouseX**2 + deltaMouseY**2) * 4, 150); 
  // 3. Convert mouse velocity to a value in the range [0, 0.5]
  const scaleValue = (mouseVelocity / 150) * 0.5;
  // 4. Smoothly update the current scale
  currentScale += (scaleValue - currentScale) * speed;
  // 5. Create a transformation string for scaling
  const scaleTransform = `scale(${1 + currentScale}, ${1 - currentScale})`;

  // ROTATE
  // 1. Calculate the angle using the atan2 function
  const angle = Math.atan2(deltaMouseY, deltaMouseX) * 180 / Math.PI;
  // 2. Check for a threshold to reduce shakiness at low mouse velocity
  if (mouseVelocity > 20) {
    currentAngle = angle;
  }
  // 3. Create a transformation string for rotation
  const rotateTransform = `rotate(${currentAngle}deg)`;

  // Apply all transformations to the circle element in a specific order: translate -> rotate -> scale
  circleElement.style.transform = `${translateTransform} ${rotateTransform} ${scaleTransform}`;

  // Request the next frame to continue the animation
  window.requestAnimationFrame(tick);
}

// Start the animation loop
tick();

That’s all! You’ve successfully created an elastic custom cursor using vanilla JavaScript. This effect can significantly enhance the user experience of your website by providing interactive feedback to user actions.

Loading... ...

Loading preview...

Device: Desktop
Dimensions: 1200x800
Lines: 0 Characters: 0 Ln 1, Ch 1

Leave a Comment

About W3Frontend

W3Frontend provides free, open-source web design code and scripts to help developers and designers build faster. Every snippet is reviewed before publishing for quality. Learn more.