Blob Cursor

Three spring-animated circles follow the cursor and merge via an SVG feGaussianBlur goo filter. Change the fill color and blob shape with the live controls.

Generated using Grepped's AI UI component generator — created from scratch, not pulled from a library.

cursorspringblobsvg-filterinteractivegoo

Live Preview — customize or regenerate this in the workspace

Loading preview…

Design Intent

Three blobs with spring physics chase the cursor and are merged into a fluid goo shape using an SVG feGaussianBlur + feColorMatrix filter.

CSS

css
* { margin: 0; padding: 0; box-sizing: border-box; }
    html, body { width: 100%; height: 100%; overflow: hidden; background: #0f0f0f; cursor: none; }
    .hint { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); font-family: sans-serif; font-size: 16px; color: #ffffff; opacity: 0.45; pointer-events: none; user-select: none; letter-spacing: 0.04em; }
    .stage { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; filter: url(#goo); }
    .blob { position: absolute; background: #ec4899; border-radius: 50%; transform: translate(-50%,-50%); }

HTML

html
<svg style="position:absolute;width:0;height:0">
    <filter id="goo">
      <feGaussianBlur in="SourceGraphic" stdDeviation="12" result="blur"/>
      <feColorMatrix in="blur" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 28 -10"/>
    </filter>
  </svg>
  <p class="hint">Move your cursor here</p>
  <div class="stage">
    <div class="blob" id="b0" style="width:52px;height:52px"></div>
    <div class="blob" id="b1" style="width:38px;height:38px"></div>
    <div class="blob" id="b2" style="width:26px;height:26px"></div>
  </div>

Full Source

html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    html, body { width: 100%; height: 100%; overflow: hidden; background: #0f0f0f; cursor: none; }
    .hint { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); font-family: sans-serif; font-size: 16px; color: #ffffff; opacity: 0.45; pointer-events: none; user-select: none; letter-spacing: 0.04em; }
    .stage { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; filter: url(#goo); }
    .blob { position: absolute; background: #ec4899; border-radius: 50%; transform: translate(-50%,-50%); }
  </style>
</head>
<body>
  <svg style="position:absolute;width:0;height:0">
    <filter id="goo">
      <feGaussianBlur in="SourceGraphic" stdDeviation="12" result="blur"/>
      <feColorMatrix in="blur" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 28 -10"/>
    </filter>
  </svg>
  <p class="hint">Move your cursor here</p>
  <div class="stage">
    <div class="blob" id="b0" style="width:52px;height:52px"></div>
    <div class="blob" id="b1" style="width:38px;height:38px"></div>
    <div class="blob" id="b2" style="width:26px;height:26px"></div>
  </div>
  <script>
    var mouse = {x: -200, y: -200};
    var blobs = [
      {x: -200, y: -200, vx: 0, vy: 0},
      {x: -200, y: -200, vx: 0, vy: 0},
      {x: -200, y: -200, vx: 0, vy: 0}
    ];
    var els = [document.getElementById('b0'), document.getElementById('b1'), document.getElementById('b2')];
    var cfgs = [
      {tension: 1200, friction: 40,  mass: 1},
      {tension: 200,  friction: 50,  mass: 10},
      {tension: 200,  friction: 50,  mass: 10}
    ];
    var targets = [mouse, blobs[0], blobs[1]];
    var last = null;

    function tick(ts) {
      if (!last) last = ts;
      var dt = Math.min((ts - last) / 1000, 0.05);
      last = ts;
      blobs.forEach(function(b, i) {
        var t = targets[i], c = cfgs[i];
        b.vx += (c.tension * (t.x - b.x) - c.friction * b.vx) / c.mass * dt;
        b.vy += (c.tension * (t.y - b.y) - c.friction * b.vy) / c.mass * dt;
        b.x += b.vx * dt; b.y += b.vy * dt;
        els[i].style.left = b.x + 'px';
        els[i].style.top  = b.y + 'px';
      });
      requestAnimationFrame(tick);
    }

    document.addEventListener('mousemove', function(e) { mouse.x = e.clientX; mouse.y = e.clientY; });
    requestAnimationFrame(tick);
  </script>
</body>
</html>

More Cursor Effects Animations