Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    How to make Animated Download Button in HTML CSS & JS

    21 November 2025

    How to make Password Input Light in HTML CSS & JavaScript

    18 November 2025

    How to make I love you heart card Animation in HTML CSS & JavaScript

    14 November 2025
    Facebook X (Twitter) Instagram YouTube Telegram Threads
    Coding StellaCoding Stella
    • Home
    • Blog
    • HTML & CSS
      • Login Form
    • JavaScript
    • Hire us!
    Coding StellaCoding Stella
    Home - JavaScript - How to create Cursor Trail Image Animation using HTML CSS and JS
    JavaScript

    How to create Cursor Trail Image Animation using HTML CSS and JS

    Coding StellaBy Coding Stella31 October 2025Updated:31 October 2025No Comments7 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Email WhatsApp Copy Link

    Let’s create a Cursor Trail Image Animation using HTML, CSS, and JavaScript. This fun and interactive project makes your cursor come alive with trailing images that follow your mouse movement, adding a stylish and dynamic effect to your website.

    We’ll use:

    • HTML to structure the page.
    • CSS to style and position the images.
    • JavaScript to handle the movement and create the smooth trailing animation.

    This project is a great way to learn how to combine animation and interactivity for a unique visual experience. Perfect for adding creativity to your web designs or personal portfolio! ✨🖱️

    HTML :

    This HTML code creates a simple web page with a title “Cursor Trail @coding.stella.” It links to external CSS for styling (style.css) and GSAP (gsap.min.js) for animations, along with a custom JavaScript file (script.js) to control the cursor trail effect. The main content is inside a section called “hero-section,” displaying the heading in the center of the page.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8" />
      <title>Cursor Trail Image Animation | @coding.stella</title>
      <link rel="stylesheet" href="https://public.codepenassets.com/css/normalize-5.0.0.min.css" />
      <link rel="stylesheet" href="./style.css" />
    </head>
    
    <body>
      <section class="hero-section">
        <h1 class="hero-title">Cursor Trail<br>@coding.stella</h1>
      </section>
    
      <script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
      <script src="./script.js"></script>
    </body>
    
    </html>

    CSS :

    This CSS code styles a full-screen dark webpage with a centered glowing title using the “Tilt Neon” font. It removes default margins and paddings, sets a black background, and centers the text in the middle of the screen with a blue neon glow. The .trail-img and .trail-image classes are for animated cursor trail images — positioned absolutely, not clickable, and optimized for smooth movement and transformations.

    @import url("https://fonts.googleapis.com/css2?family=Tilt+Neon:wght@400;700&display=swap");
    
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    html,
    body {
      height: 100%;
      overflow-x: hidden;
    }
    
    body {
      font-family: "Tilt Neon", sans-serif;
      background: #000;
      touch-action: pan-y;
      
    }
    
    .hero-section {
      position: relative;
      width: 100vw;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      background: #111;
      text-shadow: 0 0 10px #00b3ff, 0 0 20px #00b3ff;
      overflow: hidden;
    }
    
    .hero-section h1 {
      font: 40px "Tilt Neon", sans-serif; 
      color: #fff;
    }
    
    .trail-img {
      position: absolute;
      object-fit: cover;
      transform-origin: center;
      pointer-events: none;
      will-change: transform;
      z-index: 12;
    }
    
    .trail-image {
      position: absolute;
      overflow: hidden;
      will-change: transform;
      transform-origin: center;
      backface-visibility: hidden;
      z-index: 12;
    }

    JavaScript :

    This JavaScript code creates a flame-like cursor trail animation. When you move your mouse (or touch/scroll on mobile), it spawns small glowing flame images that follow the cursor and fade away smoothly. It detects mouse, touch, and scroll movements, calculates speed to adjust flame size and rotation, and removes old images to keep performance smooth. The code also works on both desktop and mobile devices using customizable animation settings.

    document.addEventListener("DOMContentLoaded", () => {
      // Minimal GSAP intro; delete this whole function + CDN if not needed
      const animateTextColumns = () => {
        const tl = gsap.timeline({ defaults: { duration: 0.8, ease: "power2.out" } });
        tl.to(".text-item", {
          opacity: 1,
          y: 0,
          filter: "blur(0px)",
          stagger: { amount: 3, from: "start" }
        }).to(".rotated-item", { opacity: 1, filter: "blur(0px)", stagger: 0.2 }, "-=2");
      };
      setTimeout(animateTextColumns, 200);
    
      const container = document.querySelector(".hero-section");
      if (!container) return;
    
      const isMobile =
        /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) || window.innerWidth <= 768;
    
      const config = {
        imageLifespan: 600,
        removalDelay: 16,
        mouseThreshold: isMobile ? 20 : 40,
        scrollThreshold: 50,
        inDuration: 600,
        outDuration: 800,
        inEasing: "cubic-bezier(.07,.5,.5,1)",
        outEasing: "cubic-bezier(.87, 0, .13, 1)",
        touchImageInterval: 40,
        minMovementForImage: isMobile ? 3 : 5,
        minImageSize: isMobile ? 120 : 160,
        maxImageSize: isMobile ? 260 : 340,
        baseRotation: 30,
        maxRotationFactor: 3,
        speedSmoothingFactor: 0.25
      };
    
      const images = [
        "Image/image1.png",
        "Image/image2.png",
        "Image/image3.png",
        "Image/image4.png",
        "Image/image5.png",
        "Image/image6.png",
        "Image/image7.png",
        "Image/image8.png",
        "Image/image9.png",
        "Image/image10.png",
        "Image/image11.png",
        "Image/image12.png",
        "Image/image13.png",
        "Image/image14.png"
    ];
    
      const trail = [];
      let mouseX = 0, mouseY = 0, lastMouseX = 0, lastMouseY = 0, prevMouseX = 0, prevMouseY = 0;
      let isMoving = false, isCursorInContainer = false, isTouching = false, isScrolling = false;
      let lastRemovalTime = 0, lastTouchImageTime = 0, lastScrollTime = 0, lastMoveTime = Date.now();
      let smoothedSpeed = 0, maxSpeed = 0, imageIndex = 0;
    
      const rectOf = () => container.getBoundingClientRect();
    
      const isInContainer = (x, y) => {
        const r = rectOf();
        return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
      };
    
      document.addEventListener("mouseover", function initPos(e) {
        mouseX = lastMouseX = prevMouseX = e.clientX;
        mouseY = lastMouseY = prevMouseY = e.clientY;
        isCursorInContainer = isInContainer(mouseX, mouseY);
        document.removeEventListener("mouseover", initPos);
      });
    
      const movedBeyond = (th) => {
        const dx = mouseX - lastMouseX, dy = mouseY - lastMouseY;
        return Math.hypot(dx, dy) > th;
      };
    
      const movedAtAll = (min) => {
        const dx = mouseX - prevMouseX, dy = mouseY - prevMouseY;
        return Math.hypot(dx, dy) > min;
      };
    
      const calculateSpeed = () => {
        const now = Date.now();
        const dt = now - lastMoveTime;
        if (dt <= 0) return 0;
        const dist = Math.hypot(mouseX - prevMouseX, mouseY - prevMouseY);
        const raw = dist / dt;
        maxSpeed = Math.max(maxSpeed, raw || 0.5);
        const norm = Math.min(raw / (maxSpeed || 0.5), 1);
        smoothedSpeed = smoothedSpeed * (1 - config.speedSmoothingFactor) + norm * config.speedSmoothingFactor;
        lastMoveTime = now;
        return smoothedSpeed;
      };
    
      const createFlameImage = (speed) => {
        const imageSrc = images[imageIndex];
        imageIndex = (imageIndex + 1) % images.length;
    
        const size = config.minImageSize + (config.maxImageSize - config.minImageSize) * speed;
        const rotFactor = 1 + speed * (config.maxRotationFactor - 1);
        const rot = (Math.random() - 0.5) * config.baseRotation * rotFactor;
    
        const img = document.createElement("img");
        img.className = "trail-img";
        img.src = imageSrc;
        img.width = img.height = size;
    
        const r = rectOf();
        const x = mouseX - r.left, y = mouseY - r.top;
    
        img.style.left = `${x}px`;
        img.style.top = `${y}px`;
        img.style.transform = `translate(-50%, -50%) rotate(${rot}deg) scale(0)`;
        img.style.transition = `transform ${config.inDuration}ms ${config.inEasing}`;
        container.appendChild(img);
    
        requestAnimationFrame(() => {
          img.style.transform = `translate(-50%, -50%) rotate(${rot}deg) scale(1)`;
        });
    
        trail.push({
          element: img,
          rotation: rot,
          removeTime: Date.now() + config.imageLifespan
        });
      };
    
      const tryCreateTrail = () => {
        if (!isCursorInContainer) return;
        if ((isMoving || isTouching || isScrolling) && movedBeyond(config.mouseThreshold) && movedAtAll(config.minMovementForImage)) {
          lastMouseX = mouseX;
          lastMouseY = mouseY;
          const speed = calculateSpeed();
          createFlameImage(speed);
          prevMouseX = mouseX;
          prevMouseY = mouseY;
        }
      };
    
      const tryCreateTouchTrail = () => {
        if (!isCursorInContainer || !isTouching || !movedAtAll(config.minMovementForImage)) return;
        const now = Date.now();
        if (now - lastTouchImageTime < config.touchImageInterval) return;
        lastTouchImageTime = now;
        const speed = calculateSpeed();
        createFlameImage(speed);
        prevMouseX = mouseX;
        prevMouseY = mouseY;
      };
    
      const tryCreateScrollTrail = () => {
        if (!isCursorInContainer || !isScrolling) return;
        lastMouseX += (config.mouseThreshold + 10) * (Math.random() > 0.5 ? 1 : -1);
        lastMouseY += (config.mouseThreshold + 10) * (Math.random() > 0.5 ? 1 : -1);
        createFlameImage(0.5);
        lastMouseX = mouseX;
        lastMouseY = mouseY;
      };
    
      const removeOldImages = () => {
        const now = Date.now();
        if (now - lastRemovalTime < config.removalDelay || !trail.length) return;
        if (now >= trail[0].removeTime) {
          const imgObj = trail.shift();
          const el = imgObj.element;
          el.style.transition = `transform ${config.outDuration}ms ${config.outEasing}`;
          el.style.transform = `translate(-50%, -50%) rotate(${imgObj.rotation + 360}deg) scale(0)`;
          setTimeout(() => el.remove(), config.outDuration);
          lastRemovalTime = now;
        }
      };
    
      document.addEventListener("mousemove", (e) => {
        prevMouseX = mouseX;
        prevMouseY = mouseY;
        mouseX = e.clientX;
        mouseY = e.clientY;
        isCursorInContainer = isInContainer(mouseX, mouseY);
        if (isCursorInContainer && movedAtAll(config.minMovementForImage)) {
          isMoving = true;
          clearTimeout(window.moveTimeout);
          window.moveTimeout = setTimeout(() => (isMoving = false), 100);
        }
      }, { passive: true });
    
      container.addEventListener("touchstart", (e) => {
        const t = e.touches[0];
        prevMouseX = mouseX = t.clientX;
        prevMouseY = mouseY = t.clientY;
        lastMouseX = mouseX;
        lastMouseY = mouseY;
        isCursorInContainer = true;
        isTouching = true;
        lastMoveTime = Date.now();
      }, { passive: true });
    
      container.addEventListener("touchmove", (e) => {
        const t = e.touches[0];
        const dx = Math.abs(t.clientX - prevMouseX);
        const dy = Math.abs(t.clientY - prevMouseY);
        prevMouseX = mouseX;
        prevMouseY = mouseY;
        mouseX = t.clientX;
        mouseY = t.clientY;
        isCursorInContainer = true;
        if (dy > dx) return;
        tryCreateTouchTrail();
      }, { passive: true });
    
      container.addEventListener("touchend", () => { isTouching = false; }, { passive: true });
    
      document.addEventListener("touchstart", (e) => {
        const t = e.touches[0];
        if (!isInContainer(t.clientX, t.clientY)) {
          isCursorInContainer = false;
          isTouching = false;
        }
      }, { passive: true });
    
      window.addEventListener("scroll", () => {
        isCursorInContainer = isInContainer(mouseX, mouseY);
        if (!isCursorInContainer) return;
    
        const now = Date.now();
        if (now - lastScrollTime < config.scrollThreshold) return;
        lastScrollTime = now;
    
        isScrolling = true;
        clearTimeout(window.scrollTimeout);
        window.scrollTimeout = setTimeout(() => (isScrolling = false), 100);
    
        requestAnimationFrame(() => { if (isScrolling) tryCreateScrollTrail(); });
      }, { passive: true });
    
      (function loop() {
        if (isMoving || isTouching || isScrolling) tryCreateTrail();
        removeOldImages();
        requestAnimationFrame(loop);
      })();
    });
    

    In short, building a Cursor Trail Image Animation with HTML, CSS, and JavaScript is a fun way to make your website more engaging. It’s simple to create yet adds a visually stunning effect that instantly catches attention.

    If your project has problems, don’t worry. Just click to download the source code and face your coding challenges with excitement. Have fun coding!

    Animation space
    Share. Copy Link Twitter Facebook LinkedIn Email WhatsApp
    Previous ArticleHow to make Heart Beat Animation using HTML and CSS
    Next Article How to make Magic Navigation Tabs Menu using HTML CSS & JavaScript
    Coding Stella
    • Website

    Related Posts

    JavaScript

    How to make Animated Download Button in HTML CSS & JS

    21 November 2025
    JavaScript

    How to make Password Input Light in HTML CSS & JavaScript

    18 November 2025
    JavaScript

    How to make I love you heart card Animation in HTML CSS & JavaScript

    14 November 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Trending Post

    Master Frontend in 100 Days Ebook

    2 March 202431K Views

    How to make Modern Login Form using HTML & CSS | Glassmorphism

    11 January 202428K Views

    How to make I love you Animation in HTML CSS & JavaScript

    14 February 202422K Views

    How to make Valentine’s Day Card using HTML & CSS

    13 February 202414K Views
    Follow Us
    • Instagram
    • Facebook
    • YouTube
    • Twitter
    ads
    Featured Post

    How to make QR Code Generator using HTML CSS & JavaScript

    14 January 2024

    How to Make Social Media Icons Popups in HTML and CSS

    12 December 2023

    How to create Order Button Animation using HTML CSS and JS

    22 September 2025

    Is a Career in Web Development Worth It in 2025? Let’s Find Out!

    29 January 2025
    Latest Post

    How to make Animated Download Button in HTML CSS & JS

    21 November 2025

    How to make Password Input Light in HTML CSS & JavaScript

    18 November 2025

    How to make I love you heart card Animation in HTML CSS & JavaScript

    14 November 2025

    How to make Husky Dog Animation using HTML and CSS

    10 November 2025
    Facebook X (Twitter) Instagram YouTube
    • About Us
    • Privacy Policy
    • Return and Refund Policy
    • Terms and Conditions
    • Contact Us
    • Buy me a coffee
    © 2025 Coding Stella. Made with 💙 by @coding.stella

    Type above and press Enter to search. Press Esc to cancel.

    Ad Blocker Enabled!
    Ad Blocker Enabled!
    Looks like you're using an ad blocker. We rely on advertising to help fund our site.
    Okay! I understood