With AI Canvas MCP, your AI knows every component we ship. Ask for one inside Claude Code, Codex, or Cursor and it installs the component you pick. Works with any AI Canvas account, free or premium.
Playful is kinetic typography that makes letters jump, drop, and rotate as your cursor approaches each one, like the headline is sneezing or hopping out of the way. Motion drives the per-letter physics and the staggered randomness so the effect feels organic rather than mechanical. It is the right pick for children-focused sites, game-UI headlines, festival posters, and any moment where typography should feel like a character with its own mood.
Built with
MotionTailwind CSS
Frequently asked questions
Is Playful free to use?
Yes. Playful is part of the free AI Canvas library and is open source under the MIT license, so you can use it in personal and commercial projects.
How do I install Playful?
One command: npx shadcn@latest add https://aicanvas.me/r/playful.json, free with a free AI Canvas account. Or connect the AI Canvas MCP server and ask your AI editor to install Playful for you. You can also copy the source straight from the Code tab, no account needed. It works in any React project with Tailwind CSS.
What is Playful built with?
Playful is built with React and TypeScript, using Motion and Tailwind CSS. It ships with both light and dark styling.
Where would I use Playful?
Common uses include Hero section, Game UI, and Children's site. Like every AI Canvas component, it is self-contained and drops into any React project.
Can I remix Playful with AI?
Yes. Playful ships with one comprehensive AI prompt written against the real source code. Open "Remix with AI" on this page to read and copy it into Claude, Cursor, ChatGPT, or any AI tool. Prompts are for remixing your own variation; for the exact component, install it with the one-command CLI.
One comprehensive prompt, written against the real source code. Works in Claude, Cursor, ChatGPT, or any AI tool you use.
This prompt is for remixing. Use it to build your own variation of Playful. Results depend on the model you use, and no prompt in the world is 100% exact.
Want the exact component?
One command installs it, pixel-perfect. Copy, paste into your project, done.
npx shadcn@latest add @aicanvas/playful
AI prompt for Playful
Create an interactive typography component where letters jump, drop, and rotate playfully based on cursor proximity.
## Requirements
### Setup
- Use Next.js 16 with TypeScript
- Verify Tailwind CSS v4 is installed (configured via @theme inline in globals.css)
- Install: framer-motion, next/font
- The component should be a client-side React component ('use client')
### Typography & Text
- Display the text as TWO rows:
- Row 1: "STAY"
- Row 2: "WEIRD"
- Use Science Gothic font from next/font/google with subsets: ['latin']
- Font size: clamp(1.6rem, 16vw, 9.6rem) — fluid, ~60-70% of viewport width
- Font weight: 700 (bold)
- Line height: 1
- Each letter is an inline-block element with select-none
### Theme Support (Dark & Light) — INVERTED
- Dark mode (default):
- Background: #1A1A19 (sand-950, near-black)
- Text color: #869631 (olive-600)
- Light mode:
- Background: #869631 (olive-600)
- Text color: #1A1A19 (sand-950)
- Detect theme via `closest('[data-card-theme]')` attribute first (for isolated card previews), falling back to `document.documentElement.classList.contains('dark')` for standalone usage
- Use a MutationObserver walking up the ancestor chain to react to theme changes
### Text Shadow (chunky sticker style, 2px offset, no blur)
- Dark mode: `2px 2px 0 rgba(0, 0, 0, 0.85)`
- Light mode: `2px 2px 0 rgba(0, 0, 0, 0.25)`
### Animation System
Each letter animates 3 properties based on cursor distance:
#### Influence Radius
- 600px from cursor center affects letters
- Distance-based influence: linear falloff from 1 to 0 across the radius
- Apply smoothstep easing: influence = influence² × (3 - 2 × influence)
#### Rotation (CSS custom property --rotate)
- Max: ±75°
- Direction depends on cursor angle relative to letter (creates natural tilt)
- Calculate angle from cursor to letter: angle = atan2(dy, dx)
- Map to rotation direction: rotateDirection = sin(angle)
- Interpolate: targetRotate = 75 × influence × rotateDirection
#### TranslateY (CSS custom property --translate-y) — ALTERNATING
- Max: ±100px
- **Even-indexed letters (0, 2, 4, ...) jump UP** (negative Y, translate -100px at max)
- **Odd-indexed letters (1, 3, 5, ...) drop DOWN** (positive Y, translate +100px at max)
- Index is counted continuously across both rows (Row 1 letters are 0-3, Row 2 letters are 4-8)
- Formula: direction = i % 2 === 0 ? -1 : 1; targetTranslateY = 100 × influence × direction
- Creates a wavy zigzag motion as cursor moves across the text
#### Scale (CSS custom property --scale)
- Min: 1, Max: 1.4
- At cursor: 1.4x scale (40% size boost)
- Far: 1x (normal)
- Interpolate: targetScale = 1 + (1.4 - 1) × influence
Apply all three via transform: `translateY(var(--translate-y, 0px)) rotate(var(--rotate, 0deg)) scale(var(--scale, 1))`
### Animation & Physics
#### Hover State (cursor active)
- Easing factor: 0.15 (smooth follow)
#### Exit State (cursor leaves)
- Easing factor: 0.05 (gentle elastic recovery)
- When cursor leaves (mouseRef.current = null), letters decay back to default state slowly
#### Animation Loop
- Use requestAnimationFrame to update all letters at 60fps
- For each letter (across both rows):
1. Get its bounding rect (getBoundingClientRect)
2. Calculate distance to cursor center
3. Calculate influence using smoothstep falloff
4. Calculate target rotation, translateY, and scale
5. Ease current state toward target using exponential interpolation
6. Update CSS custom properties on the element via setProperty()
### Component Structure
Create a LetterSpan component that:
1. Renders as a <span> with inline-block, select-none
2. Accepts props: letter, textColor, fontFamily, textShadow, forwardedRef
3. Exposes span via forwardedRef so parent can collect all letter refs
4. Styles: fontSize, fontWeight: 700, lineHeight: 1, textShadow, transform via CSS vars, willChange: 'transform'
Wrap with motion(LetterSpanComponent) to enable Framer Motion.
Main Playful component:
1. Manages containerRef, lettersRef array (indexed continuously across both rows), mouseRef, animIdRef, aliveRef
2. Tracks cursor position via onMouseMove + onTouchStart (for mobile)
3. Clears cursor on onMouseLeave + onTouchEnd (with 600ms delay so touch users can see effect)
4. Runs animation loop in useEffect that initializes state per letter and cleans up RAF on unmount
### Layout — Two rows stacked
- Outer container: flex flex-col items-center justify-center gap-2 sm:gap-4
- Each row: flex flex-nowrap items-center justify-center gap-1 sm:gap-2
- Row 1 maps letters with key index i, ref stored at lettersRef.current[i]
- Row 2 maps letters with key i + TEXT_ROW_1.length, ref stored at lettersRef.current[i + TEXT_ROW_1.length]
- This ensures the animation loop sees both rows as one continuous letter array
### Responsive Design
- Container: flex, min-h-screen, w-full, items-center, justify-center, px-6 sm:px-10
- Letter gaps: gap-1 (sm:gap-2) within each row
- Component must fill viewport and work on all screen sizes from 320px to 1200px+
- Touch support: onTouchStart captures first touch position, onTouchEnd clears after 600ms
### Code Structure
- Single file component exported as default function Playful
- All constants at module level (TEXT_ROW_1, TEXT_ROW_2, ALL_LETTERS, INFLUENCE_RADIUS, MAX_ROTATION, MAX_TRANSLATE_Y, MAX_SCALE, MIN_SCALE, EASE_ACTIVE, EASE_EXIT)
- Clean up all effects: cancel animIdRef on unmount, disconnect all MutationObservers
- No TypeScript errors or use of 'any'
Create this as a fully functional, copy-paste-ready component that showcases a playful kinetic typography effect.