Draw On Element with Cursor

Category: Cursor Animations. Last updated: Aug 20, 2025

Setup — External Scripts

Setup: External Scripts
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>

Code

index.html
html
<div class="draw-section">
  <div data-draw="" class="draw-section-bg">
    <img src="images/bg-image.avif" loading="lazy" alt="" class="section-bg-image">
   </div>
  <div class="draw-section-overlay">
    <h1 class="draw-heading">draw with</h1>
    <h1 class="draw-heading align-right">your cursor</h1>
  </div>
</div>
styles.css
css
.draw-section {
  width: 100%;
  height: 100vh;
  margin-bottom: 50vh;
  position: relative;
}

.draw-heading {
  text-transform: uppercase;
  margin-top: 0;
  margin-bottom: 0;
  font-family: PP Neue Corp Tight, Arial, sans-serif;
  font-size: 11.25em;
  font-weight: 700;
  line-height: 1;
}

.draw-heading.align-right {
  text-align: right;
}

.draw-section-bg {
  z-index: 0;
  width: 100%;
  height: 100%;
}

.section-bg-image {
  object-fit: cover;
  width: 100%;
  height: 100%;
}

.draw-section-overlay {
  z-index: 2;
  pointer-events: none;
  flex-flow: column;
  justify-content: space-between;
  align-items: stretch;
  padding: 2em;
  display: flex;
  position: absolute;
  inset: 0%;
}

[data-draw]{
  position: relative;
}

svg.cursor-svg {
  position: absolute;
  z-index: 10;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  pointer-events: none;
  mix-blend-mode: difference;
}

svg.cursor-svg line {
  stroke: #FF9900;
  stroke-width: 15;
  stroke-linecap: round;
  stroke-linejoin: round;
}

[data-draw]{
  position: relative;
}

svg.cursor-svg {
  position: absolute;
  z-index: 10;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  pointer-events: none;
  mix-blend-mode: difference;
}

svg.cursor-svg line {
  stroke: #FF9900;
  stroke-width: 15;
  stroke-linecap: round;
  stroke-linejoin: round;
}
script.js
javascript
function supportsTouch() {
  return (('ontouchstart' in window) ||
     (navigator.maxTouchPoints > 0) ||
     (navigator.msMaxTouchPoints > 0));
}

function initDrawing() {
  if (supportsTouch()) { return; } // Exit function if it's a touch device
  
  let svgns = "http://www.w3.org/2000/svg";
  
  // Function that creates an SVG Line
  function createLine(root, x, y) {
    let line = document.createElementNS(svgns, "line");
    line.setAttribute("x1", x);
    line.setAttribute("y1", y);
    line.setAttribute("x2", x);
    line.setAttribute("y2", y);
    root.appendChild(line);
    return line;
  }
  
  function handleDrawing(element) {
    // Create an empty SVG inside the [data-draw] wrapper. This is where we will render the lines in
    let svgRoot = document.createElementNS(svgns, "svg");
    svgRoot.classList.add("cursor-svg");
    element.appendChild(svgRoot);
    
    let currentLine = null;
    
    // Place first coordinate on mouseenter of [data-draw] element
    element.addEventListener("mouseenter", (event) => {
      currentLine = createLine(svgRoot, event.offsetX, event.offsetY);
    });
    
    // Create lines on every mousemove
    element.addEventListener("mousemove", (event) => {
      if (currentLine) {
        currentLine.setAttribute("x2", event.offsetX);
        currentLine.setAttribute("y2", event.offsetY);
        currentLine = createLine(svgRoot, event.offsetX, event.offsetY);
      }
    });
    
    // On mouseleave of the [data-draw] wrap, smoothly remove all lines
    element.addEventListener("mouseleave", () => {
      if (currentLine) {
        gsap.to(svgRoot.querySelectorAll("line"), {
          strokeOpacity: 0,
          duration: 0.01,
          stagger: 0.01,
          ease: "none",
          onComplete: () => {
            svgRoot
              .querySelectorAll("line")
              .forEach((line) => svgRoot.removeChild(line));
          },
        });
        currentLine = null;
      }
    });
  }
  document.querySelectorAll("[data-draw]").forEach(handleDrawing);
}

// Initialize Draw on Element with Cursor
document.addEventListener('DOMContentLoaded', () => {
  initDrawing();
});

Guide

Implementation

Add a div or other HTML element anywhere on your site with an attribute of data-draw and make sure it has any position that is not static. The JS will add an SVG element in this data-draw wrapper. So you can get creative with adding this over images, videos, or simply coloured blocks.

Customising

Play around with the styling of the SVG line element in the CSS to determine what the drawn line looks like. For an extra effect, we added a blend-mode of difference to the SVG element.