Help us improve
Share bugs, ideas, or general feedback.
Creates animated backgrounds with gradient flows, particle systems, mesh gradients, aurora effects, wave patterns, and dynamic textures for hero sections, landing pages, and interfaces.
npx claudepluginhub markus41/claude --plugin react-animation-studioHow this skill is triggered — by the user, by Claude, or both
Slash command
/react-animation-studio:background-animationsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Expert knowledge for creating stunning animated backgrounds - gradient flows, particle systems, mesh gradients, aurora effects, wave patterns, and dynamic textures that bring depth and life to your interfaces.
Provides code examples and techniques for artistic animations like liquid buttons, morphing blobs, glitch effects, and distortion using Framer Motion in React TSX components. Activates for creative visuals and transitions.
Provides pre-built animated React components from Magic UI (Tailwind/Framer Motion/shadcn-ui) and React Bits for landing pages, dashboards, and interactive UIs.
Copy-paste animation patterns for React/Next.js: buttons, modals, toasts, stagger, page transitions, exit animations, scroll reveals, and layout transitions, built on motion-foundations tokens and springs.
Share bugs, ideas, or general feedback.
Expert knowledge for creating stunning animated backgrounds - gradient flows, particle systems, mesh gradients, aurora effects, wave patterns, and dynamic textures that bring depth and life to your interfaces.
Activate this skill when:
**/*.tsx with background components**/components/Background*.tsx**/components/Hero*.tsximport { motion } from 'framer-motion';
export function FlowingGradient() {
return (
<motion.div
className="absolute inset-0 -z-10"
style={{
background: 'linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab)',
backgroundSize: '400% 400%',
}}
animate={{
backgroundPosition: ['0% 50%', '100% 50%', '0% 50%'],
}}
transition={{
duration: 15,
repeat: Infinity,
ease: 'linear',
}}
/>
);
}
export function RadialPulse() {
return (
<motion.div
className="absolute inset-0 -z-10"
animate={{
background: [
'radial-gradient(circle at 50% 50%, #667eea 0%, transparent 50%)',
'radial-gradient(circle at 50% 50%, #764ba2 0%, transparent 70%)',
'radial-gradient(circle at 50% 50%, #667eea 0%, transparent 50%)',
],
}}
transition={{
duration: 4,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
);
}
export function MeshGradient() {
return (
<div className="absolute inset-0 -z-10 overflow-hidden">
<motion.div
className="absolute w-[150%] h-[150%] -left-[25%] -top-[25%]"
style={{
background: `
radial-gradient(at 40% 20%, #ff6b6b 0px, transparent 50%),
radial-gradient(at 80% 0%, #feca57 0px, transparent 50%),
radial-gradient(at 0% 50%, #48dbfb 0px, transparent 50%),
radial-gradient(at 80% 50%, #ff9ff3 0px, transparent 50%),
radial-gradient(at 0% 100%, #54a0ff 0px, transparent 50%),
radial-gradient(at 80% 100%, #5f27cd 0px, transparent 50%)
`,
}}
animate={{
rotate: [0, 360],
scale: [1, 1.1, 1],
}}
transition={{
rotate: { duration: 60, repeat: Infinity, ease: 'linear' },
scale: { duration: 10, repeat: Infinity, ease: 'easeInOut' },
}}
/>
</div>
);
}
import { motion } from 'framer-motion';
import { useMemo } from 'react';
interface Particle {
id: number;
x: number;
y: number;
size: number;
duration: number;
delay: number;
}
export function FloatingParticles({ count = 50 }: { count?: number }) {
const particles = useMemo<Particle[]>(() =>
Array.from({ length: count }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 4 + 2,
duration: Math.random() * 20 + 10,
delay: Math.random() * 5,
})),
[count]
);
return (
<div className="absolute inset-0 -z-10 overflow-hidden">
{particles.map((particle) => (
<motion.div
key={particle.id}
className="absolute rounded-full bg-white/20"
style={{
left: `${particle.x}%`,
top: `${particle.y}%`,
width: particle.size,
height: particle.size,
}}
animate={{
y: [0, -100, 0],
x: [0, Math.random() * 50 - 25, 0],
opacity: [0, 1, 0],
}}
transition={{
duration: particle.duration,
delay: particle.delay,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
))}
</div>
);
}
export function Starfield({ count = 200 }: { count?: number }) {
const stars = useMemo(() =>
Array.from({ length: count }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 2 + 0.5,
twinkleDuration: Math.random() * 3 + 2,
delay: Math.random() * 3,
})),
[count]
);
return (
<div className="absolute inset-0 -z-10 bg-slate-950 overflow-hidden">
{stars.map((star) => (
<motion.div
key={star.id}
className="absolute rounded-full bg-white"
style={{
left: `${star.x}%`,
top: `${star.y}%`,
width: star.size,
height: star.size,
}}
animate={{
opacity: [0.3, 1, 0.3],
scale: [1, 1.2, 1],
}}
transition={{
duration: star.twinkleDuration,
delay: star.delay,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
))}
</div>
);
}
export function AnimatedWaves() {
return (
<div className="absolute bottom-0 left-0 right-0 -z-10 h-64 overflow-hidden">
<svg
viewBox="0 0 1440 320"
className="absolute bottom-0 w-full"
preserveAspectRatio="none"
>
{/* Wave 1 - Back */}
<motion.path
fill="rgba(99, 102, 241, 0.3)"
d="M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"
animate={{
d: [
"M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
"M0,128L48,154.7C96,181,192,235,288,234.7C384,235,480,181,576,181.3C672,181,768,235,864,250.7C960,267,1056,245,1152,229.3C1248,213,1344,203,1392,197.3L1440,192L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
],
}}
transition={{
duration: 8,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
}}
/>
{/* Wave 2 - Middle */}
<motion.path
fill="rgba(99, 102, 241, 0.5)"
d="M0,256L48,261.3C96,267,192,277,288,266.7C384,256,480,224,576,213.3C672,203,768,213,864,224C960,235,1056,245,1152,234.7C1248,224,1344,192,1392,176L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"
animate={{
d: [
"M0,256L48,261.3C96,267,192,277,288,266.7C384,256,480,224,576,213.3C672,203,768,213,864,224C960,235,1056,245,1152,234.7C1248,224,1344,192,1392,176L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
"M0,160L48,176C96,192,192,224,288,234.7C384,245,480,235,576,224C672,213,768,203,864,213.3C960,224,1056,256,1152,266.7C1248,277,1344,267,1392,261.3L1440,256L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
],
}}
transition={{
duration: 6,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
}}
/>
{/* Wave 3 - Front */}
<motion.path
fill="rgba(99, 102, 241, 0.8)"
d="M0,288L48,282.7C96,277,192,267,288,272C384,277,480,299,576,293.3C672,288,768,256,864,250.7C960,245,1056,267,1152,272C1248,277,1344,267,1392,261.3L1440,256L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"
animate={{
d: [
"M0,288L48,282.7C96,277,192,267,288,272C384,277,480,299,576,293.3C672,288,768,256,864,250.7C960,245,1056,267,1152,272C1248,277,1344,267,1392,261.3L1440,256L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
"M0,256L48,261.3C96,267,192,277,288,272C384,267,480,245,576,250.7C672,256,768,288,864,293.3C960,299,1056,277,1152,272C1248,267,1344,277,1392,282.7L1440,288L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
],
}}
transition={{
duration: 4,
repeat: Infinity,
repeatType: 'reverse',
ease: 'easeInOut',
}}
/>
</svg>
</div>
);
}
export function AuroraBackground() {
return (
<div className="absolute inset-0 -z-10 overflow-hidden bg-slate-950">
{/* Aurora Layer 1 */}
<motion.div
className="absolute inset-0 opacity-50"
style={{
background: 'linear-gradient(180deg, transparent 0%, #00ff8855 30%, #00ffff33 50%, transparent 70%)',
filter: 'blur(60px)',
}}
animate={{
x: ['-20%', '20%', '-20%'],
skewX: ['-5deg', '5deg', '-5deg'],
}}
transition={{
duration: 15,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
{/* Aurora Layer 2 */}
<motion.div
className="absolute inset-0 opacity-40"
style={{
background: 'linear-gradient(180deg, transparent 0%, #ff00ff44 40%, #00ffff22 60%, transparent 80%)',
filter: 'blur(80px)',
}}
animate={{
x: ['20%', '-20%', '20%'],
skewX: ['5deg', '-5deg', '5deg'],
}}
transition={{
duration: 20,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
{/* Aurora Layer 3 */}
<motion.div
className="absolute inset-0 opacity-30"
style={{
background: 'linear-gradient(180deg, transparent 0%, #00ff0033 35%, #ff880022 55%, transparent 75%)',
filter: 'blur(100px)',
}}
animate={{
x: ['-10%', '10%', '-10%'],
y: ['-5%', '5%', '-5%'],
}}
transition={{
duration: 25,
repeat: Infinity,
ease: 'easeInOut',
}}
/>
</div>
);
}
export function GrainOverlay() {
return (
<motion.div
className="pointer-events-none fixed inset-0 z-50 opacity-20"
style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
}}
animate={{
x: [0, -10, 0],
y: [0, -10, 0],
}}
transition={{
duration: 0.2,
repeat: Infinity,
}}
/>
);
}
export function AnimatedGrid() {
return (
<div className="absolute inset-0 -z-10 overflow-hidden">
<motion.div
className="absolute inset-0"
style={{
backgroundImage: `
linear-gradient(rgba(99, 102, 241, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(99, 102, 241, 0.1) 1px, transparent 1px)
`,
backgroundSize: '50px 50px',
}}
animate={{
backgroundPosition: ['0px 0px', '50px 50px'],
}}
transition={{
duration: 20,
repeat: Infinity,
ease: 'linear',
}}
/>
{/* Glow effect at center */}
<div
className="absolute inset-0"
style={{
background: 'radial-gradient(circle at 50% 50%, transparent 0%, rgba(0,0,0,0.8) 100%)',
}}
/>
</div>
);
}
export function MorphingBlobs() {
const blobVariants = {
animate: {
d: [
"M440,320Q410,390,340,410Q270,430,200,410Q130,390,100,320Q70,250,100,180Q130,110,200,90Q270,70,340,90Q410,110,440,180Q470,250,440,320Z",
"M440,320Q440,390,370,420Q300,450,230,420Q160,390,120,320Q80,250,120,180Q160,110,230,80Q300,50,370,80Q440,110,440,180Q440,250,440,320Z",
"M440,320Q410,390,340,410Q270,430,200,410Q130,390,100,320Q70,250,100,180Q130,110,200,90Q270,70,340,90Q410,110,440,180Q470,250,440,320Z",
],
},
};
return (
<div className="absolute inset-0 -z-10 overflow-hidden">
<svg viewBox="0 0 500 500" className="absolute w-full h-full">
<defs>
<linearGradient id="blob-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#667eea" />
<stop offset="100%" stopColor="#764ba2" />
</linearGradient>
</defs>
<motion.path
fill="url(#blob-gradient)"
variants={blobVariants}
animate="animate"
transition={{
duration: 10,
repeat: Infinity,
ease: 'easeInOut',
}}
style={{ filter: 'blur(40px)', opacity: 0.6 }}
/>
</svg>
</div>
);
}
will-change: transform for moving elementsfilter: blur() sparingly - it's expensiverequestAnimationFrame for canvas-based effectsCreated by Brookside BI as part of React Animation Studio