A cursor
that flows.
Replace the pointer with a gooey blob that trails your movement on spring physics. One script tag. Every knob is live — move your mouse, that's the demo.
You're on a touch screen, so the blob is wandering on its own up here. Open this page on a desktop with a mouse or trackpad to drive it.
Try it on things.
Links and buttons grow the blob automatically. Three data attributes cover everything else — no JavaScript needed on the page.
Links and buttons grow it
Anything clickable scales the lead blob by hoverScale. Press and hold to squish it.
Hover me: 3.2×
Any number works. Below 1 shrinks it — handy over text.
Recolor on hover
The blob takes the element's color while you're over it and snaps back when you leave.
Hide it here
For video players, canvases, maps — anywhere the native pointer should win.
Reading something long? Shrink the blob over prose so it stops sitting on the words. Set the attribute once on the article wrapper and every paragraph inside inherits it.
Respects the OS
Reduced motion drops the trail and stretch and snaps the blob to the pointer. Touch devices get an idle autopilot instead of nothing.
One tag. Paste it before </body>.
The snippet below tracks the controls panel, so dial in a look you like and copy exactly that.
Copy the snippet
It's a single <script> tag that loads an 11 KB file (4 KB gzipped) from this site. Self-host it if you'd rather — download fluid-cursor.min.js (or the readable source) and change the src.
Paste it where your site takes custom code
</body></body>Tag a few elements (optional)
Add data-cursor-scale, data-cursor-color or data-cursor="hide" to anything. Links and buttons already react.
Options
| data-attribute | default | what it does |
|---|---|---|
data-size | 60 | Diameter of the lead blob in px. Trail blobs shrink from there. |
data-color | #000000 | Any CSS color. |
data-opacity | 1 | 0–1. Applied after the goo pass, so it really fades (most goo cursors get this wrong). |
data-trail | 2 | Total blobs including the lead. 1 = no trail. Up to 12. |
data-speed | 6 | 1–10. How hard the spring pulls toward the pointer. |
data-elasticity | 0.5 | 0–1. Higher = less damping = more overshoot and wobble. |
data-goo | 16 | Blur radius feeding the metaball threshold. 0 turns the goo off (plain circles). |
data-stretch | 1 | 0–2. Squash & stretch along the direction of travel. |
data-hover-scale | 1.6 | Lead blob scale over links and buttons. |
data-highlight | true | Glossy inner highlight. |
data-shadow | true | Soft drop shadow under the merged shape. |
data-hide-cursor | true | Hide the native pointer (mouse/trackpad only). |
data-blend | normal | difference with a white blob inverts whatever it passes over. Also multiply, screen, exclusion. |
data-autopilot | touch | touch wanders on touch screens only; always also takes over on desktop once the mouse idles for 2.5 s (data-idle-after, ms); never. |
Element hooks
| attribute | effect while hovering |
|---|---|
data-cursor-scale="2" | Scale the lead blob. Values under 1 shrink it. |
data-cursor-color="#f43" | Recolor every blob, with a 250 ms crossfade. |
data-cursor="hide" | Collapse the blob to nothing over this element. |
JavaScript API
new FluidCursor(opts) | Same keys as the data attributes, camelCased (hoverScale, hideCursor). |
cursor.update({ ... }) | Change anything live. Size, trail and highlight rebuild the blobs in place. |
cursor.setBounds(el) | Keep autopilot wandering inside an element. |
cursor.destroy() | Remove everything and restore the native pointer. |
window.fluidCursor | The instance the auto-init snippet creates. |
Notes: no hit-testing on the blob (it's pointer-events: none), one requestAnimationFrame loop that sleeps when the springs settle, and the only heavy thing on screen is a single SVG blur — same cost as one backdrop-filter.