Fluid Cursor
Drop-in cursor · zero dependencies · 4 KB gzipped

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.

Get the snippet

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.

data-cursor-scale grows it on hover · data-cursor-color recolors it · data-cursor="hide" hides it
Playground

Try it on things.

Links and buttons grow the blob automatically. Three data attributes cover everything else — no JavaScript needed on the page.

a, button — automatic

Links and buttons grow it

Anything clickable scales the lead blob by hoverScale. Press and hold to squish it.

A link
data-cursor-scale="3.2"

Hover me: 3.2×

Any number works. Below 1 shrinks it — handy over text.

The whole tile is the target.
data-cursor-color

Recolor on hover

The blob takes the element's color while you're over it and snaps back when you leave.

#ff5a3c
#2563eb
#16a34a
#d946ef
#f59e0b
data-cursor="hide"

Hide it here

For video players, canvases, maps — anywhere the native pointer should win.

native cursor zone
data-cursor-scale="0.4"

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.

prefers-reduced-motion

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.

No config needed.
Install

One tag. Paste it before </body>.

The snippet below tracks the controls panel, so dial in a look you like and copy exactly that.

index.html — before </body>
or control it from JavaScript
1

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.

2

Paste it where your site takes custom code

FramerSite Settings → General → Custom Code → End of <body> tag
WebflowProject Settings → Custom Code → Footer Code
Shopifytheme.liquid, right above </body>
WordPressany “header & footer scripts” plugin → footer
Plain HTMLbefore </body>
3

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-attributedefaultwhat it does
data-size60Diameter of the lead blob in px. Trail blobs shrink from there.
data-color#000000Any CSS color.
data-opacity10–1. Applied after the goo pass, so it really fades (most goo cursors get this wrong).
data-trail2Total blobs including the lead. 1 = no trail. Up to 12.
data-speed61–10. How hard the spring pulls toward the pointer.
data-elasticity0.50–1. Higher = less damping = more overshoot and wobble.
data-goo16Blur radius feeding the metaball threshold. 0 turns the goo off (plain circles).
data-stretch10–2. Squash & stretch along the direction of travel.
data-hover-scale1.6Lead blob scale over links and buttons.
data-highlighttrueGlossy inner highlight.
data-shadowtrueSoft drop shadow under the merged shape.
data-hide-cursortrueHide the native pointer (mouse/trackpad only).
data-blendnormaldifference with a white blob inverts whatever it passes over. Also multiply, screen, exclusion.
data-autopilottouchtouch 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

attributeeffect 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.fluidCursorThe 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.