About this snippet

Add a modern liquid bubble cursor to your site. We combined CSS SVG filters and JavaScript to make trailing blobs that merge together beautifully.

Size
2.8 KB
Uses
HTML, CSS, JavaScript
Loads
Nothing external
Views
225
<div class="gooey-wrapper">
  <div class="content-text">
    <h1>Liquid Cursor</h1>
    <p>Move your mouse around to see the gooey effect merge dynamically.</p>
  </div>
  
  <svg style="display: none;">
    <defs>
      <filter id="goo">
        <feGaussianBlur in="SourceGraphic" stdDeviation="15" result="blur"></feGaussianBlur>
        <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 25 -10" result="goo"></feColorMatrix>
        <feBlend in="SourceGraphic" in2="goo"></feBlend>
      </filter>
    </defs>
  </svg>
  
  <div class="cursor-container" id="cursor-container">
    <!-- Blobs will be injected instantly by Javascript -->
  </div>
</div>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  margin: 0;
  background: #fafafa;
  min-height: 100vh;
  overflow: hidden;
  cursor: none;
}

.gooey-wrapper {
  width: 100%;
  height: 100vh;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}

.content-text {
  text-align: center;
  color: #111827;
  z-index: 10;
  pointer-events: none;
}

.content-text h1 {
  font-size: clamp(3rem, 8vw, 5rem);
  margin: 0 0 10px 0;
  letter-spacing: -1px;
}

.content-text p {
  font-size: 1.25rem;
  color: #4b5563;
  margin: 0;
}

.cursor-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 100;
  filter: url("#goo");
}

.blob {
  position: absolute;
  border-radius: 50%;
  background: #3b82f6;
  transform: translate(-50%, -50%);
  will-change: left, top, width, height;
}
const container = document.getElementById("cursor-container");

const blobs = new Array();
const numBlobs = 6;

let mouseX = window.innerWidth / 2;
let mouseY = window.innerHeight / 2;

window.addEventListener("mousemove", function(e) {
  mouseX = e.clientX;
  mouseY = e.clientY;
});

// Setup the trailing blobs
for (let i = 0; i < numBlobs; i++) {
  const el = document.createElement("div");
  el.className = "blob";
  container.appendChild(el);
  
  const blobObj = new Object();
  blobObj.el = el;
  blobObj.x = mouseX;
  blobObj.y = mouseY;
  
  const size = 60 - (i * 8);
  el.style.width = size + "px";
  el.style.height = size + "px";
  
  blobs.push(blobObj);
}

function animateGoo() {
  let targetX = mouseX;
  let targetY = mouseY;
  
  blobs.forEach(function(blobObj) {
    // Interpolation makes the followers lag gently behind
    blobObj.x += (targetX - blobObj.x) * 0.3;
    blobObj.y += (targetY - blobObj.y) * 0.3;
    
    blobObj.el.style.left = blobObj.x + "px";
    blobObj.el.style.top = blobObj.y + "px";
    
    targetX = blobObj.x;
    targetY = blobObj.y;
  });
  
  window.requestAnimationFrame(animateGoo);
}

animateGoo();
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save