CSS Masking
CSSmask-image defines which parts of an element are visible using pixel alpha values — unlike clip-path's hard edges. A radial gradient at the cursor position creates a soft blob that reveals the layer beneath as you move.
REVEAL
move cursor to unmask
REVEAL
move cursor to unmask
concepts
Defines a mask layer using an image, gradient, or SVG shape. Black pixels = fully visible; transparent pixels = hidden. Unlike clip-path (hard vector edge), masks use alpha values — enabling soft, anti-aliased falloffs.
A radial-gradient centered at the cursor position fills a circle with black (opaque) then fades to transparent. Only the element pixels beneath the black region are visible — everything else is hidden.
The gap between the inner stop (solid black) and outer stop (transparent) controls edge hardness. A tight gap gives a crisp cut; a wide gap creates a soft glowing blob that bleeds into the dark layer.
Instead of lerp (exponential decay toward target), a spring adds velocity each frame — vel += (target - pos) * stiffness — then damps it: vel *= damping. Damping < 1 means the blob overshoots and oscillates when it stops, which reads as liquid weight.
Two satellite circles are placed ahead of and behind the main blob, offset along the velocity vector. Their distance from center scales with speed — at low speed they collapse onto the main blob; at high speed the trio merges into a teardrop. Multiple mask-image layers union by default via mask-composite: add.
core pattern
// Spring physics — overshoots, oscillates, feels heavy
vel.x += (target.x - pos.x) * STIFFNESS; // 0.18
vel.y += (target.y - pos.y) * STIFFNESS;
vel.x *= DAMPING; // 0.80
vel.y *= DAMPING;
pos.x += vel.x;
pos.y += vel.y;
// Stretch along velocity vector
const speed = Math.hypot(vel.x, vel.y);
const stretch = Math.min(speed * 2.2, radius * 0.85);
const dx = vel.x / (speed || 1);
const dy = vel.y / (speed || 1);
// Three overlapping circles → one organic blob via mask-composite: add
el.style.maskImage = [
`radial-gradient(circle at ${pos.x}px ${pos.y}px, black ${r}px, transparent ${r+f}px)`,
`radial-gradient(circle at ${pos.x+dx*stretch*.6}px ${pos.y+dy*stretch*.6}px, black ${r*.62}px, transparent ${r*.62+f}px)`,
`radial-gradient(circle at ${pos.x-dx*stretch*.3}px ${pos.y-dy*stretch*.3}px, black ${r*.62}px, transparent ${r*.62+f}px)`,
].join(", ");