awwwards-animations

Premium React animations at Awwwards/FWA quality—60fps, scroll-driven, and award-worthy. Covers four core libraries: GSAP + ScrollTrigger for scroll-driven animations, Lenis for smooth scrolling, Motion (Framer Motion) for React-native effects, and Anime.js for lightweight tweens Includes decision matrix routing tasks to the right tool: magnetic cursors, parallax, text reveals, glitch effects, and page transitions with working React examples Supports advanced creative coding: fractals, L-systems, flow fields, tessellations, geometric dissections, kinetic typography, and generative art via Canvas, SVG, p5.js, and Zdog Provides design philosophy guidance for brutalist, minimalist, and neo-brutalist aesthetics with easing reference, timing rules, and performance constraints (transform/opacity only, proper cleanup, ScrollTrigger integration) Built-in accessibility patterns (prefers-reduced-motion), testing checklist, and common pitfalls to avoid in Next.js App Router

INSTALLATION
npx skills add https://github.com/devmartinese/awwwards-animations-skill --skill awwwards-animations
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Awwwards Animations

Create premium web animations at Awwwards/FWA quality level. React-first approach. 60fps non-negotiable.

Decision Matrix

Task

Library

Why

Scroll-driven animations

GSAP + ScrollTrigger + useGSAP

Industry standard, best control

Smooth scroll

Lenis + ReactLenis

Best performance, works with ScrollTrigger

React-native animations

Motion (Framer Motion)

Native React, useScroll/useTransform

Simple/lightweight effects

Anime.js 4.0

Small footprint, clean API

Complex timelines

GSAP

Unmatched timeline control

SVG morphing

GSAP MorphSVG or Anime.js

Both excellent

3D + animation

Three.js + GSAP

GSAP controls Three.js objects

Page transitions

AnimatePresence or GSAP

Motion for React, GSAP for complex

Geometric shapes (vector)

SVG + GSAP/Motion

Native, animable

Geometric shapes (canvas)

Canvas 2D API

Programmatic, performant

Pseudo-3D shapes

Zdog

Flat design 3D, ~2kb

Creative coding/generative

p5.js

Rich ecosystem

Audio reactive

Tone.js

Web Audio, synths, effects

Physics 2D

Matter.js

Gravity, collisions, constraints

Algorithmic/generative art

Canvas 2D + p5.js

Math-driven visuals

Fractals/L-systems

Canvas 2D recursivo

Recursive rendering

Tessellations/geometric puzzles

SVG + GSAP

Precise animated transforms

Kinetic typography advanced

GSAP SplitText + Canvas

Per-char control

Glitch effects

CSS + GSAP

Layered RGB split, clip-path

Brutalist animation

CSS raw + Motion

Hard cuts, no easing

Minimalist animation

Motion springs

Subtle, purposeful motion

Installation (Latest Stable - 2025)

# GSAP + React hook (v3.14.1)

npm install gsap @gsap/react

# Lenis (v1.3.17) - includes React components

npm install lenis

# Motion (Framer Motion)

npm install motion

# Anime.js (v4.0.0)

npm install animejs

React Setup

1. GSAP Configuration (app-wide)

// lib/gsap.ts

'use client' // Next.js App Router

import gsap from 'gsap'

import { ScrollTrigger } from 'gsap/ScrollTrigger'

import { useGSAP } from '@gsap/react'

// Register plugins once

gsap.registerPlugin(ScrollTrigger, useGSAP)

export { gsap, ScrollTrigger, useGSAP }

2. Lenis + GSAP ScrollTrigger Integration (Critical)

// components/SmoothScroll.tsx

'use client'

import { ReactLenis, useLenis } from 'lenis/react'

import { useEffect } from 'react'

import { gsap, ScrollTrigger } from '@/lib/gsap'

export function SmoothScroll({ children }: { children: React.ReactNode }) {

  const lenis = useLenis()

  useEffect(() => {

    if (!lenis) return

    lenis.on('scroll', ScrollTrigger.update)

    gsap.ticker.add((time) => lenis.raf(time * 1000))

    gsap.ticker.lagSmoothing(0)

    return () => { gsap.ticker.remove(lenis?.raf) }

  }, [lenis])

  return (

    <ReactLenis root options={{ lerp: 0.1, duration: 1.2, smoothWheel: true }}>

      {children}

    </ReactLenis>

  )

}

// Wrap in layout: <SmoothScroll>{children}</SmoothScroll>

Core Patterns (React)

Detailed implementations in references:

  • Advanced Text Effects: See references/text-effects.md (glitch, kinetic typography, morphing, explosion, circular text, scramble)

Quick Patterns (React)

1. Magnetic Cursor (GSAP + useGSAP)

'use client'

import { useRef, useEffect } from 'react'

import { gsap, useGSAP } from '@/lib/gsap'

export function MagneticCursor() {

  const cursorRef = useRef<HTMLDivElement>(null)

  const pos = useRef({ x: 0, y: 0, cx: 0, cy: 0 })

  useEffect(() => {

    const h = (e: MouseEvent) => { pos.current.x = e.clientX; pos.current.y = e.clientY }

    window.addEventListener('mousemove', h)

    return () => window.removeEventListener('mousemove', h)

  }, [])

  useGSAP(() => {

    gsap.ticker.add(() => {

      const p = pos.current

      p.cx += (p.x - p.cx) * 0.15; p.cy += (p.y - p.cy) * 0.15

      gsap.set(cursorRef.current, { x: p.cx, y: p.cy })

    })

  })

  return <div ref={cursorRef} className="fixed w-10 h-10 border border-white rounded-full pointer-events-none mix-blend-difference z-[9999] -translate-x-1/2 -translate-y-1/2" />

}

2. Magnetic Button (Motion)

'use client'

import { useRef, useState } from 'react'

import { motion } from 'motion/react'

export function MagneticButton({ children }: { children: React.ReactNode }) {

  const ref = useRef<HTMLButtonElement>(null)

  const [pos, setPos] = useState({ x: 0, y: 0 })

  const onMove = (e: React.MouseEvent) => {

    const { left, top, width, height } = ref.current!.getBoundingClientRect()

    setPos({ x: (e.clientX - left - width / 2) * 0.3, y: (e.clientY - top - height / 2) * 0.3 })

  }

  return (

    <motion.button ref={ref} onMouseMove={onMove} onMouseLeave={() => setPos({ x: 0, y: 0 })}

      animate={pos} transition={{ type: 'spring', stiffness: 150, damping: 15 }}

      className="px-8 py-4 bg-white text-black rounded-full">{children}</motion.button>

  )

}

3. Parallax Hero (GSAP + useGSAP)

'use client'

import { useRef } from 'react'

import { gsap, ScrollTrigger, useGSAP } from '@/lib/gsap'

export function ParallaxHero() {

  const containerRef = useRef<HTMLDivElement>(null)

  useGSAP(() => {

    gsap.to('.parallax-bg', {

      yPercent: 50,

      ease: 'none',

      scrollTrigger: {

        trigger: containerRef.current,

        start: 'top top',

        end: 'bottom top',

        scrub: true,

      },

    })

    gsap.to('.hero-title', {

      yPercent: 100,

      opacity: 0,

      scrollTrigger: {

        trigger: containerRef.current,

        start: 'top top',

        end: '50% top',

        scrub: true,

      },

    })

  }, { scope: containerRef })

  return (

    <div ref={containerRef} className="relative h-screen overflow-hidden">

      <div className="parallax-bg absolute inset-0 bg-cover bg-center" />

      <h1 className="hero-title absolute inset-0 flex items-center justify-center text-6xl">

        Hero Title

      </h1>

    </div>

  )

}

4. Text Character Reveal (Motion)

'use client'

import { motion } from 'motion/react'

const container = {

  hidden: { opacity: 0 },

  visible: {

    opacity: 1,

    transition: { staggerChildren: 0.02 },

  },

}

const child = {

  hidden: { opacity: 0, y: 50, rotateX: -90 },

  visible: {

    opacity: 1,

    y: 0,

    rotateX: 0,

    transition: { type: 'spring', damping: 12 },

  },

}

export function TextReveal({ text }: { text: string }) {

  return (

    <motion.span

      variants={container}

      initial="hidden"

      whileInView="visible"

      viewport={{ once: true }}

      className="inline-block"

    >

      {text.split('').map((char, i) => (

        <motion.span key={i} variants={child} className="inline-block">

          {char === ' ' ? '\u00A0' : char}

        </motion.span>

      ))}

    </motion.span>

  )

}

5. Image Reveal (GSAP)

'use client'

import { useRef } from 'react'

import { gsap, useGSAP } from '@/lib/gsap'

export function ImageReveal({ src, alt }: { src: string; alt: string }) {

  const containerRef = useRef<HTMLDivElement>(null)

  useGSAP(() => {

    gsap.from(containerRef.current, {

      clipPath: 'inset(100% 0% 0% 0%)',

      duration: 1.2,

      ease: 'power4.inOut',

      scrollTrigger: {

        trigger: containerRef.current,

        start: 'top 80%',

      },

    })

    gsap.from('.reveal-img', {

      scale: 1.3,

      duration: 1.5,

      ease: 'power2.out',

      scrollTrigger: {

        trigger: containerRef.current,

        start: 'top 80%',

      },

    })

  }, { scope: containerRef })

  return (

    <div ref={containerRef} className="overflow-hidden">

      <img src={src} alt={alt} className="reveal-img w-full h-full object-cover" />

    </div>

  )

}

6. Glitch Text Effect (CSS + GSAP)

'use client'

import { useRef, useEffect } from 'react'

import { gsap } from '@/lib/gsap'

export function GlitchText({ text }: { text: string }) {

  const ref = useRef<HTMLDivElement>(null)

  useEffect(() => {

    const layers = ref.current!.querySelectorAll('.g-layer')

    const tl = gsap.timeline({ repeat: -1, repeatDelay: 3 })

    tl.to(layers[0], { x: -5, duration: 0.05, ease: 'none' }, 0)

      .to(layers[0], { x: 5, duration: 0.05 }, 0.05)

      .to(layers[0], { x: 0, duration: 0.05 }, 0.1)

      .to(layers[1], { x: 5, duration: 0.05 }, 0.02)

      .to(layers[1], { x: -5, duration: 0.05 }, 0.07)

      .to(layers[1], { x: 0, duration: 0.05 }, 0.12)

    return () => { tl.kill() }

  }, [])

  return (

    <div ref={ref} className="relative font-mono text-5xl font-black">

      <span className="relative z-10">{text}</span>

      <span className="g-layer absolute inset-0 text-cyan-400 mix-blend-multiply" aria-hidden>{text}</span>

      <span className="g-layer absolute inset-0 text-red-400 mix-blend-multiply" aria-hidden>{text}</span>

    </div>

  )

}

7. Fractal Tree (Canvas 2D)

'use client'

import { useRef, useEffect } from 'react'

export function FractalTree({ depth = 10, angle = 25 }: { depth?: number; angle?: number }) {

  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {

    const canvas = canvasRef.current!

    const ctx = canvas.getContext('2d')!

    canvas.width = canvas.offsetWidth * 2; canvas.height = canvas.offsetHeight * 2; ctx.scale(2, 2)

    let progress = 0, raf = 0

    function branch(x: number, y: number, len: number, a: number, d: number) {

      if (d > depth || len < 2) return

      const dp = Math.max(0, Math.min(1, progress * depth - d))

      if (dp <= 0) return

      const ex = x + Math.cos(a * Math.PI / 180) * len * dp

      const ey = y - Math.sin(a * Math.PI / 180) * len * dp

      ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(ex, ey)

      ctx.strokeStyle = `hsl(${120 + d * 15}, 60%, ${30 + d * 5}%)`

      ctx.lineWidth = Math.max(1, (depth - d) * 1.5); ctx.stroke()

      branch(ex, ey, len * 0.72, a + angle, d + 1)

      branch(ex, ey, len * 0.72, a - angle, d + 1)

    }

    const animate = () => {

      progress = Math.min(1, progress + 0.008)

      ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight)

      branch(canvas.offsetWidth / 2, canvas.offsetHeight, canvas.offsetHeight * 0.28, 90, 0)

      if (progress < 1) raf = requestAnimationFrame(animate)

    }

    animate()

    return () => cancelAnimationFrame(raf)

  }, [depth, angle])

  return <canvas ref={canvasRef} className="w-full h-full bg-gray-950" />

}

See references/algorithmic-art.md for L-systems, flow fields, attractors, noise, sacred geometry.

8. Geometric Dissection (SVG + GSAP)

'use client'

import { useRef, useState } from 'react'

import { gsap } from '@/lib/gsap'

const P = [

  { id: 'A', tri: 'M 0,173 L 50,87 L 100,173 Z', sq: 'M 0,0 L 100,0 L 100,87 L 0,87 Z', c: '#f43f5e' },

  { id: 'B', tri: 'M 50,87 L 100,0 L 150,87 Z', sq: 'M 100,0 L 200,0 L 200,87 L 100,87 Z', c: '#8b5cf6' },

  { id: 'C', tri: 'M 100,173 L 150,87 L 200,173 Z', sq: 'M 0,87 L 100,87 L 100,173 L 0,173 Z', c: '#06b6d4' },

  { id: 'D', tri: 'M 50,87 L 100,173 L 150,87 L 100,0 Z', sq: 'M 100,87 L 200,87 L 200,173 L 100,173 Z', c: '#f59e0b' },

]

export function GeometricDissection() {

  const svg = useRef<SVGSVGElement>(null)

  const [isSq, setSq] = useState(false)

  const morph = () => {

    const t = !isSq

    P.forEach((p, i) => {

      const el = svg.current!.querySelector(`#d-${p.id}`)

      if (el) gsap.to(el, { attr: { d: t ? p.sq : p.tri }, duration: 1.5, ease: 'power2.inOut', delay: i * 0.15 })

    }); setSq(t)

  }

  return (

    <div className="flex flex-col items-center gap-4">

      <svg ref={svg} viewBox="-10 -10 220 200" className="w-64 h-64">

        {P.map(p => <path key={p.id} id={`d-${p.id}`} d={p.tri} fill={p.c} stroke="#000" strokeWidth="1.5" />)}

      </svg>

      <button onClick={morph} className="px-6 py-2 bg-white text-black font-mono text-sm">{isSq ? '△' : '□'}</button>

    </div>

  )

}

See references/geometric-puzzles.md for tangram, tessellations, Penrose tiles, polyominoes.

9. Brutalist Grid (Motion)

'use client'

import { motion } from 'motion/react'

export function BrutalistGrid({ items }: { items: string[] }) {

  return (

    <div className="grid grid-cols-3 border-2 border-black">

      {items.map((item, i) => (

        <motion.div key={i}

          className="border-2 border-black p-6 font-mono font-black uppercase text-2xl"

          style={{ mixBlendMode: i % 2 === 0 ? 'normal' : 'difference' }}

          initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ once: true }}

          transition={{ duration: 0, delay: i * 0.1 }}

          whileHover={{ backgroundColor: '#000', color: '#BAFF39', transition: { duration: 0 } }}

        >{item}</motion.div>

      ))}

    </div>

  )

}

Design Philosophy (Quick Reference)

Style

Motion Feel

Easing

Typography

Key Trait

Brutalist

Hard, instant, jarring

none / steps()

Mono, 15-30vw

Raw honesty

Minimalist

Smooth, subtle, slow

power2.out

Sans-serif light

Purposeful restraint

Abstract

Noise-driven, parametric

Organic/sine

Varies

Mathematical beauty

Neo-Brutalist

Bold but controlled

power1.out

Mono + color

Brutalism + restraint

See references/design-philosophy.md for full guide with color palettes and mixing strategies.

Easing Reference

Feel

GSAP

Motion

Smooth

power2.out

[0.16, 1, 0.3, 1]

Snappy

power4.out

[0.87, 0, 0.13, 1]

Bouncy

back.out(1.7)

{ type: 'spring', stiffness: 300, damping: 20 }

Dramatic

power4.inOut

[0.76, 0, 0.24, 1]

Timing

  • Micro-interactions: 150-300ms
  • UI transitions: 300-500ms
  • Page transitions: 500-800ms
  • Stagger: 0.02-0.1s per item

Accessibility

// Motion: useReducedMotion() → conditionally disable/reduce animations

import { useReducedMotion } from 'motion/react'

const reduced = useReducedMotion() // true if prefers-reduced-motion: reduce
@media (prefers-reduced-motion: reduce) {

  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }

}

Performance Rules

  • Only animate transform and opacity
  • Use will-change sparingly
  • Always cleanup: useGSAP handles it automatically
  • Scope GSAP selectors to container refs
  • Use contextSafe() for event handlers with GSAP
  • Memoize Motion variants objects

Common Pitfalls

  • Not integrating Lenis with ScrollTrigger
  • Missing scope in useGSAP
  • Not using contextSafe() for click handlers
  • React 18 Strict Mode calling effects twice
  • Forgetting 'use client' in Next.js App Router
  • Not calling ScrollTrigger.refresh() after dynamic content

Testing Checklist

  • 60fps on scroll (Chrome DevTools Performance)
  • Keyboard navigation works
  • Respects prefers-reduced-motion
  • No layout shifts (CLS)
  • Mobile touch works
  • ScrollTrigger markers removed in prod
  • No memory leaks on unmount

Inspiration

Active Theory, Studio Freight, Locomotive, Resn, Aristide Benoist, Immersive Garden

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card