Playful little interactions I built because they make me smile. Hover, drag and click: every one of them works. Want one? Open “Prompt to build it”, copy the prompt and paste it into your favourite AI tool.
01 / Magnetic buttonlive
Magnetic button
The button leans toward your cursor, inviting the click before you make it.
Pull
30%
Return
600ms
Try
Hover near it
Prompt to build it
Build a React + Tailwind "magnetic button" component. A pill button (violet #d343f7, white semibold text "Hire me ✦", px-8 py-4, soft violet shadow) sits centred in a 400×220px area. When the mouse moves anywhere inside that area, translate the button toward the cursor by 30% of its distance from the button's centre, and translate the label inside it by 15% for a parallax/depth effect. Use refs and set style.transform directly (no re-renders). While hovering use a fast 150ms transition; on mouse leave, ease both back to 0 over 600ms with cubic-bezier(0.23, 1, 0.32, 1). Ignore touch pointers (pointerType !== "mouse") and respect prefers-reduced-motion.
02 / Googly eyeslive
Googly eyes
They watch your cursor anywhere on the page. Click them and they blink.
Tracks
Whole page
Blink
180ms
Try
Move & click
Prompt to build it
Build a React + Tailwind "googly eyes" component: a yellow (#ffd23f) pill containing two white circular eyes (62px, 3px dark outline), each with a dark 26px pupil and a small white highlight. The pupils follow the mouse anywhere on the page: listen to pointermove on window, throttle with requestAnimationFrame, and for each eye use Math.atan2 from the eye's centre to the pointer, then offset the pupil by min(22% of the eye width, distance / 6) in that direction via CSS variables with a 120ms ease-out. Clicking makes both eyes blink: a yellow eyelid overlay that animates scaleY 0 → 1 → 0 over 360ms (restart it by changing a React key). Make the whole thing a <button> with an aria-label.
Tilt it and the light slides across the foil, like a rare trading card.
Tilt
±16°
Shine
Follows pointer
Try
Hover or drag
Prompt to build it
Build a React + Tailwind holographic trading card. Card 140×186px, rounded-2xl, dark gradient (#2b1640 → #121226 → #0b2a3a), white text: a small "✦ Rare" tag, the name "Topsy" in large bold type at the bottom, a subtitle and three tiny stat chips. Wrap it in a container with perspective: 700px. On pointer move, get the pointer position as 0–1 within the card and set CSS variables: --rx = (0.5 - y) * 32deg, --ry = (x - 0.5) * 32deg, --mx / --my = pointer position in %. The card uses transform: rotateX(var(--rx)) rotateY(var(--ry)). Add two overlays: a rainbow foil (linear-gradient 115deg with pink, yellow, cyan and purple stops, background-size 250%, background-position var(--mx) var(--my), mix-blend-mode: color-dodge) and a white radial shine centred at var(--mx) var(--my) with mix-blend-mode: overlay. Both fade in while hovering. On leave, ease the tilt back to 0 over 600ms. Support touch drag with touch-action: none.
04 / Gooey blobslive
Gooey blobs
A family of blobs chases your cursor and melts together like lava lamp wax.
Blobs
5
Filter
SVG goo
Try
Move inside
Prompt to build it
Build a React + Tailwind "gooey blobs" component: a 400×220px rounded area where five violet gradient circles (56, 46, 38, 30 and 24px) chase the pointer and melt together. Add an inline SVG filter: feGaussianBlur stdDeviation="10", then feColorMatrix with the alpha row "0 0 0 22 -9", and apply it to the blobs' container to get the goo effect. In a requestAnimationFrame loop, each blob eases toward the pointer at a different speed (0.22, 0.185, 0.15, 0.115, 0.08) so they stretch apart and merge. When the pointer is outside, move the target in a slow Lissajous orbit (cos(t / 900), sin(t / 650)) so it always looks alive. Only run the loop while the component is on screen (IntersectionObserver) and hide the cursor inside the area.
05 / Text scramblelive
Text scramble
Words decode themselves letter by letter, like a film hacker screen.
Resolve
700ms
Words
3
Try
Hover or tap
Prompt to build it
Build a React + Tailwind text scramble button: a dark (#0f1411) rounded box with glowing green (#58f28c with a green text-shadow) monospace text at 30px with wide letter spacing. It cycles through the words "DESIGN", "BUILD" and "SHIP IT" on hover and on click. When the word changes, run a 700ms requestAnimationFrame animation where letters lock in from left to right: at progress p, the first floor(p * length) characters show the real word and the rest show random characters from "ABCDEFGHIJKLMNOPQRSTUVWXYZ#%&*+=?/<>01" (keep spaces as spaces). Put the real word in an aria-label and hide the scrambling text from screen readers. With prefers-reduced-motion, swap the word instantly.
06 / Pluck the stringlive
Pluck the string
Pull the string and let go: it twangs back and wobbles to rest.
Physics
Spring
Damping
0.88
Try
Drag & release
Prompt to build it
Build a React + Tailwind "pluck the string" component with an SVG (viewBox 0 0 260 180). Two dark pegs at (10, 90) and (250, 90), joined by a violet, 3px, round-capped string drawn as a quadratic curve "M 10 90 Q cx cy 250 90", where the control point's y is 2 * knotY - 90 so the curve passes through the knot. A white knot circle with a violet stroke sits at the pull point. On pointer down, capture the pointer and drag the knot (clamped inside the SVG). On release, run a spring in requestAnimationFrame: velocity = (velocity + (rest - position) * 0.18) * 0.88 for both x and y, so the string twangs and wobbles back to rest. Add a small "pull me" label, touch-action: none and a grab cursor.
07 / Confetti cannonlive
Confetti cannon
Because some moments deserve a party. Every click fires a new burst.
Pieces
90
Gravity
0.35
Try
Click
Prompt to build it
Build a React + Tailwind confetti cannon: a full-size <canvas> behind a black pill button "Celebrate 🎉" at the bottom centre. On each click, spawn 90 paper pieces at the button with a random angle within ±0.7 rad of straight up and a speed of 7–15, random rotation and spin, sizes 6–11 × 9–15px, in #d343f7, #ff3b5c, #ffb020, #2bb24c, #0d99ff and #ff7a1a. Each frame: add 0.35 gravity to vy, multiply velocity by 0.985 for drag, add the spin, and draw a rotated rectangle whose height is scaled by |cos(rotation * 2)| so it looks like flipping paper. Remove pieces that leave the canvas and only keep the requestAnimationFrame loop running while pieces exist. Size the canvas for devicePixelRatio (max 2).
08 / Dot fieldlive
Dot field
A grid of dots parts around your cursor like grass in the wind.
Dots
~300
Radius
80px
Try
Move inside
Prompt to build it
Build a React + Tailwind interactive dot field on a <canvas> (400×220, devicePixelRatio aware). Lay out a grid of dots every 18px. Each frame: dots within 80px of the pointer get pushed away with force (1 - distance / 80) * 2.2; every dot is pulled back home by a spring (velocity = (velocity + (home - position) * 0.08) * 0.82); and dots near the pointer grow (radius 1.6 → 3.8) and turn violet rgba(211, 67, 247), while the rest stay grey #b9b6af. When the pointer isn't over the canvas, move a "ghost" pointer along a slow Lissajous path so the field keeps rippling. Run the requestAnimationFrame loop only while the canvas is visible (IntersectionObserver).
09 / Don't click melive
Don't click me
A button with trust issues. It dodges you, but gives up eventually.
Dodges
6
Move
350ms
Try
Catch it
Prompt to build it
Build a React + Tailwind "Don't click me" button that runs away. A red (#ff3b5c) pill button centred in a 400×220px area. When the mouse comes within about 70% of the button's size from where the button is heading, move it to a random spot inside the area, choosing the best of 12 random candidates that is furthest from the pointer, animated with a 350ms ease transform. Measure distance from the button's target position (not its mid-animation position) and add a 450ms cooldown so each approach counts as exactly one escape. The label changes each time: "Don't click me", "Nope 😜", "Too slow!", "Over here!", "Missed!", "Almost…", "Okay, okay…". Show a small "escapes: n/6" counter. After 6 escapes it stops moving, and clicking it turns it green with "Fine. You win 🏆", then it resets after 2.6s. On touch screens, tapping makes it jump away (preventDefault) until it gets tired.