Supplementary reference for the
js-animationskill. Every library with CDN URLs, sizes, licenses, API quick-reference, and technique tables.
Status: 100% FREE (including all plugins) since v3.13 (March 2025). Acquired by Webflow. License: Standard GreenSock License (free for all use including commercial) Core Size: ~28kb min+gzip
<!-- Core (required) -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/gsap.min.js"></script>
<!-- Scroll Plugins -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/ScrollSmoother.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/Observer.min.js"></script>
<!-- Text Plugins -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/SplitText.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/TextPlugin.min.js"></script>
<!-- SVG Plugins -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/DrawSVGPlugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/MorphSVGPlugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/MotionPathPlugin.min.js"></script>
<!-- Layout & Interaction Plugins -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/Flip.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/Draggable.min.js"></script>
<!-- Easing & Physics -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/CustomEase.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14/dist/EasePack.min.js"></script>| Plugin | Size (gzip) | Purpose |
|---|---|---|
| gsap core | ~28kb | Timeline, tweens, easing |
| ScrollTrigger | ~10kb | Scroll-based triggering, pinning, scrubbing |
| ScrollSmoother | ~6kb | Smooth scroll + parallax (requires ScrollTrigger) |
| SplitText | ~5kb | Split text into chars/words/lines |
| Flip | ~8kb | FLIP layout animations |
| DrawSVGPlugin | ~3kb | Animate SVG stroke drawing |
| MorphSVGPlugin | ~8kb | SVG shape morphing |
| MotionPathPlugin | ~6kb | Animate along SVG paths |
| TextPlugin | ~2kb | Character-by-character text replacement |
| Draggable | ~7kb | Drag and throw with momentum |
| Observer | ~4kb | Unified scroll/touch/pointer events |
| CustomEase | ~3kb | Design custom easing curves |
| EasePack | ~2kb | ExpoScaleEase, RoughEase, SlowMo |
// Always register plugins first
gsap.registerPlugin(ScrollTrigger, SplitText, Flip, DrawSVGPlugin);
// --- Basic Tween ---
gsap.to('.el', { x: 100, opacity: 1, duration: 0.6, ease: 'power2.out' });
gsap.from('.el', { y: 50, opacity: 0, duration: 0.6 });
gsap.fromTo('.el', { opacity: 0 }, { opacity: 1, duration: 0.6 });
gsap.set('.el', { x: 0, opacity: 1 }); // instant set, no animation
// --- Timeline ---
const tl = gsap.timeline({ defaults: { duration: 0.6, ease: 'power2.out' } });
tl.from('.a', { y: 30, opacity: 0 })
.from('.b', { y: 30, opacity: 0 }, '-=0.3') // overlap 0.3s
.from('.c', { y: 30, opacity: 0 }, '+=0.1'); // gap 0.1s
// --- Stagger ---
gsap.from('.cards', { y: 40, opacity: 0, stagger: 0.1, duration: 0.5 });
gsap.from('.cards', { y: 40, opacity: 0, stagger: { each: 0.1, from: 'center' } });
// --- ScrollTrigger ---
gsap.from('.section', {
opacity: 0, y: 50,
scrollTrigger: {
trigger: '.section',
start: 'top 80%', // trigger-top hits 80% of viewport
end: 'top 30%', // trigger-top hits 30% of viewport
toggleActions: 'play none none none', // onEnter onLeave onEnterBack onLeaveBack
scrub: true, // tie to scroll position (true or number for smoothing)
pin: true, // pin the trigger element
snap: 0.5, // snap to halfway point
markers: true, // debug (remove for production)
}
});
// --- ScrollSmoother ---
// Requires: #smooth-wrapper > #smooth-content wrapping all page content
ScrollSmoother.create({
wrapper: '#smooth-wrapper',
content: '#smooth-content',
smooth: 1.2, // smoothing amount
effects: true, // enable data-speed parallax
});
// HTML: <div data-speed="0.8">slower</div> <div data-speed="1.3">faster</div>
// --- SplitText ---
const split = new SplitText('.heading', { type: 'chars,words,lines' });
gsap.from(split.chars, {
opacity: 0, y: 60, rotateX: -40,
stagger: 0.03, duration: 0.8, ease: 'back.out(1.7)'
});
// Revert (restore original HTML): split.revert();
// --- DrawSVGPlugin ---
gsap.from('.svg-path', { drawSVG: '0%', duration: 2, ease: 'power2.inOut' });
gsap.to('.svg-path', { drawSVG: '50% 100%', duration: 1 }); // partial draw
// --- MorphSVGPlugin ---
gsap.to('#shape1', { morphSVG: '#shape2', duration: 1.5, ease: 'power2.inOut' });
gsap.to('#shape1', { morphSVG: { shape: '#shape2', shapeIndex: 'auto' } });
// --- Flip ---
const state = Flip.getState('.items'); // record current positions
container.classList.toggle('reordered'); // change layout
Flip.from(state, { duration: 0.6, ease: 'power2.inOut', stagger: 0.05, absolute: true });
// --- MotionPathPlugin ---
gsap.to('.dot', {
motionPath: { path: '#myPath', align: '#myPath', autoRotate: true },
duration: 3, ease: 'power1.inOut'
});
// --- Draggable ---
Draggable.create('.draggable', {
type: 'x,y',
bounds: '.container',
inertia: true, // throw with momentum
onDrag: function() { console.log(this.x, this.y); }
});
// --- Observer ---
Observer.create({
target: window,
type: 'wheel,touch,pointer',
onUp: () => goToPrevSlide(),
onDown: () => goToNextSlide(),
tolerance: 50,
preventDefault: true,
});
// --- Easing ---
// power1-4.in / .out / .inOut
// expo.in / .out / .inOut
// back.out(1.7)
// elastic.out(1, 0.3)
// bounce.out
// sine.inOut
// circ.out
// steps(5)
// "none" (linear)License: MIT Size: ~10kb gzipped (full bundle)
<!-- UMD Bundle (script tag) -->
<script src="https://cdn.jsdelivr.net/npm/animejs@4.3/dist/bundles/anime.umd.min.js"></script>
<!-- ES Module -->
<script type="module">
import { animate, stagger, timeline, utils } from 'https://cdn.jsdelivr.net/npm/animejs@4.3/+esm';
</script>// --- Basic Animation ---
anime.animate('.el', {
translateX: 250,
opacity: [0, 1], // from 0 to 1
duration: 800,
ease: 'outExpo',
});
// --- Stagger ---
anime.animate('.items', {
translateY: [-40, 0],
opacity: [0, 1],
delay: anime.stagger(100), // 100ms between each
delay: anime.stagger(100, { start: 500 }), // start after 500ms
delay: anime.stagger(100, { from: 'center' }), // ripple from center
});
// --- Timeline ---
const tl = anime.timeline({ defaults: { duration: 600, ease: 'outQuad' } });
tl.add('.a', { translateY: [30, 0], opacity: [0, 1] })
.add('.b', { translateY: [30, 0], opacity: [0, 1] }, '-=300');
// --- Text Split (v4.1+) ---
const split = anime.text.split('.heading');
anime.animate(split.chars, {
opacity: [0, 1],
translateY: [20, 0],
delay: anime.stagger(30),
});
// --- Scroll-Linked (v4+) ---
anime.animate('.el', {
translateX: [0, 300],
composition: 'blend',
}, {
autoplay: anime.onScroll({
target: '.scroll-container',
axis: 'y',
enter: 'top bottom',
leave: 'bottom top',
})
});
// --- Spring Physics (v4.2+) ---
anime.animate('.el', {
translateX: 200,
ease: 'spring',
bounce: 0.4, // 0-1
duration: 800,
});
// --- SVG Path Drawing ---
anime.animate('.path', {
strokeDashoffset: [anime.setDashoffset, 0],
duration: 2000,
ease: 'inOutQuad',
});
// --- Easing ---
// in / out / inOut variants: Quad, Cubic, Quart, Quint, Sine, Expo, Circ, Back, Bounce, Elastic
// Examples: 'outExpo', 'inOutQuad', 'outElastic(1, 0.5)', 'outBack(1.7)'
// Spring: 'spring' with bounce paramLicense: Free for vanilla JS Size: Mini = 2.3kb; Full = ~18kb
<!-- Full library (global) -->
<script src="https://cdn.jsdelivr.net/npm/motion@latest/dist/motion.js"></script>
<!-- Mini (2.3kb, ES Module) -->
<script type="module">
import { animate } from 'https://cdn.jsdelivr.net/npm/motion@latest/mini/+esm';
</script>
<!-- Full (ES Module) -->
<script type="module">
import { animate, scroll, inView, spring } from 'https://cdn.jsdelivr.net/npm/motion@latest/+esm';
</script>// --- Basic Animation (WAAPI-powered) ---
animate('.el', { opacity: [0, 1], y: [30, 0] }, { duration: 0.6, easing: 'ease-out' });
// --- Spring ---
animate('.el', { x: 200 }, { type: 'spring', stiffness: 300, damping: 20 });
// --- Stagger ---
animate('.items', { opacity: [0, 1], y: [20, 0] }, { delay: stagger(0.1) });
// --- Scroll-Linked ---
scroll(
animate('.el', { opacity: [0, 1], y: [50, 0] }),
{ target: document.querySelector('.section'), offset: ['start end', 'end start'] }
);
// --- In-View Detection ---
inView('.el', (info) => {
animate(info.target, { opacity: 1, y: 0 }, { duration: 0.6 });
return () => { /* cleanup on leave */ };
});
// --- Keyframes ---
animate('.el', {
x: [0, 100, 50, 100],
opacity: [0, 1, 1, 0],
}, { duration: 2 });
// --- Controls ---
const controls = animate('.el', { x: 200 }, { duration: 2 });
controls.pause();
controls.play();
controls.reverse();
controls.cancel();
await controls.finished; // promise-basedLicense: MIT | Size: ~2.1kb gzipped
<script src="https://cdn.jsdelivr.net/npm/lenis@latest/dist/lenis.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lenis@latest/dist/lenis.css">const lenis = new Lenis({
duration: 1.2, // scroll smoothness
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
orientation: 'vertical',
smoothWheel: true,
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
// GSAP integration
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);License: MIT | Size: ~5kb gzipped
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.css">
<script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js"></script>AOS.init({ duration: 800, once: true, offset: 100 });<div data-aos="fade-up" data-aos-delay="200">Content</div>
<!-- Types: fade-up, fade-down, fade-left, fade-right, zoom-in, flip-left, slide-up -->Support: Chrome 115+, Safari 26+, Firefox (behind flag) Size: 0kb (native CSS)
/* Progress bar tied to page scroll */
.progress-bar {
animation: grow-width linear;
animation-timeline: scroll();
}
@keyframes grow-width {
from { width: 0%; }
to { width: 100%; }
}
/* Element fades in as it enters viewport */
.reveal {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}License: Free personal / Paid commercial | Size: ~4kb gzipped
<script src="https://cdn.jsdelivr.net/npm/typeit@8/dist/index.umd.js"></script>new TypeIt('#terminal', {
speed: 50,
waitUntilVisible: true,
cursor: true,
cursorChar: '|',
cursorSpeed: 1000,
})
.type('npm install my-app')
.pause(500)
.break()
.type('npm start', { speed: 30 })
.pause(300)
.delete(9)
.type('run dev')
.go();License: MIT | Size: ~4kb gzipped
<script src="https://cdn.jsdelivr.net/npm/typed.js@2/lib/typed.min.js"></script>new Typed('#typed-output', {
strings: ['Developer', 'Designer', 'Creator'],
typeSpeed: 50,
backSpeed: 30,
backDelay: 2000,
loop: true,
showCursor: true,
cursorChar: '|',
});License: MIT | Size: ~3kb gzipped
<script src="https://cdn.jsdelivr.net/npm/countup.js@2/dist/countUp.umd.js"></script>const counter = new countUp.CountUp('stat-element', 1250, {
duration: 2.5,
separator: ',',
prefix: '$',
suffix: 'M',
decimal: '.',
decimals: 1,
enableScrollSpy: true,
scrollSpyOnce: true,
});
counter.start();License: MIT | Size: ~5kb gzipped
<script src="https://cdn.jsdelivr.net/npm/vivus@0.4.6/dist/vivus.min.js"></script>// Animate all SVG paths drawing in sequence
new Vivus('my-svg', {
type: 'delayed', // 'delayed' | 'sync' | 'oneByOne'
duration: 200, // in frames (~3.3s at 60fps)
start: 'inViewport',
animTimingFunction: Vivus.EASE,
});
// Manual control
const anim = new Vivus('my-svg', { type: 'scenario-sync' });
anim.play();
anim.reset();
anim.finish();License: MIT | Size: ~50kb (full), ~35kb (light)
<!-- Full (SVG + Canvas + HTML renderers) -->
<script src="https://cdn.jsdelivr.net/npm/lottie-web@5.13/build/player/lottie.min.js"></script>
<!-- Light (SVG renderer only) -->
<script src="https://cdn.jsdelivr.net/npm/lottie-web@5.13/build/player/lottie_light.min.js"></script>const anim = lottie.loadAnimation({
container: document.getElementById('lottie-container'),
renderer: 'svg',
loop: true,
autoplay: true,
path: 'animation.json', // Lottie JSON file URL
// OR: animationData: jsonObj // inline JSON object
});
// Controls
anim.play();
anim.pause();
anim.stop();
anim.setSpeed(1.5);
anim.goToAndStop(30, true); // go to frame 30
anim.setDirection(-1); // reverse
// Events
anim.addEventListener('complete', () => console.log('done'));
anim.addEventListener('loopComplete', () => console.log('loop'));License: MIT | Size: ~160kb gzipped
<!-- Classic script tag (UMD) -->
<script src="https://cdn.jsdelivr.net/npm/three@0.183/build/three.min.js"></script>
<!-- ES Modules (recommended) -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.183/build/three.module.min.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.183/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
</script>
<!-- Older UMD (for non-module contexts) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>// --- Minimal Scene Setup ---
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// --- Geometry + Material ---
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x00ffcc })
);
scene.add(mesh);
// --- Lighting ---
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
scene.add(dirLight);
// --- Particle System (BufferGeometry) ---
const COUNT = 500;
const positions = new Float32Array(COUNT * 3);
for (let i = 0; i < COUNT * 3; i++) positions[i] = (Math.random() - 0.5) * 20;
const particleGeo = new THREE.BufferGeometry();
particleGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const particles = new THREE.Points(
particleGeo,
new THREE.PointsMaterial({ color: 0xffffff, size: 0.05, transparent: true, opacity: 0.6 })
);
scene.add(particles);
// --- InstancedMesh (efficient thousands of identical objects) ---
const instanceMesh = new THREE.InstancedMesh(
new THREE.SphereGeometry(0.1, 16, 16),
new THREE.MeshStandardMaterial({ color: 0xff6600 }),
1000 // count
);
const dummy = new THREE.Object3D();
for (let i = 0; i < 1000; i++) {
dummy.position.set(Math.random()*10-5, Math.random()*10-5, Math.random()*10-5);
dummy.updateMatrix();
instanceMesh.setMatrixAt(i, dummy.matrix);
}
scene.add(instanceMesh);
// --- Animation Loop ---
function animate() {
requestAnimationFrame(animate);
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// --- Resize Handler ---
window.addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
// --- Post-Processing (Bloom) ---
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new UnrealBloomPass(
new THREE.Vector2(innerWidth, innerHeight),
1.5, // strength
0.4, // radius
0.85 // threshold
));
// In animation loop: composer.render() instead of renderer.render()
// --- GLTF Model Loading ---
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('model.glb', (gltf) => {
scene.add(gltf.scene);
// Access animations: gltf.animations
});License: MIT | Size: ~100kb gzipped
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8/dist/pixi.min.js"></script>const app = new PIXI.Application();
await app.init({ width: 800, height: 600, backgroundAlpha: 0 });
document.body.appendChild(app.canvas);
const sprite = new PIXI.Sprite(PIXI.Texture.from('image.png'));
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
app.stage.addChild(sprite);
app.ticker.add((delta) => {
sprite.rotation += 0.01 * delta.deltaTime;
});License: LGPL | Size: ~100kb gzipped
<script src="https://cdn.jsdelivr.net/npm/p5@1/lib/p5.min.js"></script>// Instance mode (doesn't pollute global scope)
new p5((p) => {
p.setup = () => {
const canvas = p.createCanvas(800, 600);
canvas.parent('canvas-container');
};
p.draw = () => {
p.background(0, 10);
p.fill(255, 100);
p.noStroke();
for (let i = 0; i < 50; i++) {
const x = p.noise(i * 0.1, p.frameCount * 0.01) * p.width;
const y = p.noise(i * 0.1 + 100, p.frameCount * 0.01) * p.height;
p.circle(x, y, 8);
}
};
});License: ISC | Size: ~6kb gzipped
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9/dist/confetti.browser.min.js"></script>// Basic burst
confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
// Fireworks
confetti({ particleCount: 50, angle: 60, spread: 55, origin: { x: 0 } });
confetti({ particleCount: 50, angle: 120, spread: 55, origin: { x: 1 } });
// Custom colors
confetti({ particleCount: 80, spread: 100, colors: ['#ff0000', '#00ff00', '#0000ff'] });
// Confetti cannon (sustained)
const end = Date.now() + 3000;
(function frame() {
confetti({ particleCount: 3, angle: 60, spread: 55, origin: { x: 0 }, colors: ['#ff6b6b'] });
confetti({ particleCount: 3, angle: 120, spread: 55, origin: { x: 1 }, colors: ['#4ecdc4'] });
if (Date.now() < end) requestAnimationFrame(frame);
})();License: MIT | Size: ~40kb gzipped (confetti bundle)
<script src="https://cdn.jsdelivr.net/npm/@tsparticles/confetti@3/tsparticles.confetti.bundle.min.js"></script>// Confetti
tsParticles.confetti({ count: 100, spread: 160, ticks: 200 });
// Snow
tsParticles.confetti({
count: 50, spread: 360, ticks: 500,
gravity: 0.5, drift: 1,
shapes: ['circle'], colors: ['#ffffff'],
origin: { x: 0.5, y: 0 },
});License: MIT | Size: ~30kb gzipped
<script src="https://cdn.jsdelivr.net/npm/matter-js@0.20/build/matter.min.js"></script>const { Engine, Render, World, Bodies, Runner } = Matter;
const engine = Engine.create();
const render = Render.create({
element: document.getElementById('physics-container'),
engine: engine,
options: { width: 800, height: 600, wireframes: false, background: 'transparent' }
});
// Ground
World.add(engine.world, Bodies.rectangle(400, 580, 800, 40, { isStatic: true }));
// Falling boxes
for (let i = 0; i < 10; i++) {
World.add(engine.world, Bodies.rectangle(
200 + Math.random() * 400, -50 - i * 60, 40, 40,
{ render: { fillStyle: '#E07A5F' } }
));
}
Render.run(render);
Runner.run(Runner.create(), engine);License: MIT | Size: ~3.8kb gzipped
<script src="https://cdn.jsdelivr.net/npm/rough-notation@0.5/lib/rough-notation.iife.js"></script>// Available types: underline, box, circle, highlight, strike-through, crossed-off, bracket
// Single annotation
const annotation = RoughNotation.annotate(document.getElementById('target'), {
type: 'highlight', // animation type
color: 'rgba(245,158,11,0.3)', // annotation color
animationDuration: 1200, // ms
strokeWidth: 2, // for underline/box/circle
padding: 5, // space around element
iterations: 2, // roughness (1 = neat, 3+ = very rough)
multiline: true, // support multi-line text
brackets: ['left', 'right'], // for type: 'bracket'
});
annotation.show();
annotation.hide();
annotation.remove();
// Annotation Group — sequential reveal
const group = RoughNotation.annotationGroup([
RoughNotation.annotate(el1, { type: 'underline', color: '#E07A5F' }),
RoughNotation.annotate(el2, { type: 'circle', color: '#3B82F6' }),
RoughNotation.annotate(el3, { type: 'highlight', color: 'rgba(34,197,94,0.3)' }),
]);
group.show(); // plays all annotations one after anotherSupport: Baseline available Oct 2025 (Chrome 111+, Safari 18+, Firefox 133+) Size: 0kb (native browser API)
// Same-document transition
document.startViewTransition(() => {
// Update the DOM here
container.innerHTML = newContent;
});
// Shared element transition
// HTML: <img style="view-transition-name: hero-image" src="...">
// The browser automatically morphs elements with matching view-transition-name
// Customizing the transition
document.startViewTransition({
update: () => { /* DOM update */ },
types: ['slide-left'], // named transition type
});/* Customize the transition animation */
::view-transition-old(root) {
animation: fade-out 0.3s ease-in;
}
::view-transition-new(root) {
animation: fade-in 0.3s ease-out;
}
/* Target specific named transitions */
::view-transition-group(hero-image) {
animation-duration: 0.5s;
}License: MIT | Size: ~7kb gzipped
<script src="https://cdn.jsdelivr.net/npm/@barba/core@2/dist/barba.umd.js"></script>barba.init({
transitions: [{
name: 'fade',
leave(data) {
return gsap.to(data.current.container, { opacity: 0, duration: 0.3 });
},
enter(data) {
return gsap.from(data.next.container, { opacity: 0, duration: 0.3 });
}
}]
});License: MIT | Size: ~1.9kb gzipped
<script src="https://cdn.jsdelivr.net/npm/@formkit/auto-animate@0.9/dist/index.js"></script>// One function call — all children get animated on add/remove/reorder
autoAnimate(document.getElementById('list'));
// With options
autoAnimate(document.getElementById('list'), {
duration: 250,
easing: 'ease-in-out',
disrespectUserMotionPreference: false,
});License: MIT | Size: ~3kb gzipped
<script src="https://cdn.jsdelivr.net/npm/vanilla-tilt@1.8/dist/vanilla-tilt.min.js"></script>VanillaTilt.init(document.querySelectorAll('.tilt-card'), {
max: 15, // max tilt degrees
speed: 400, // transition speed
glare: true, // enable glare effect
'max-glare': 0.3, // glare opacity
perspective: 1000,
scale: 1.05, // scale on hover
});| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| Character stagger reveal | GSAP SplitText | ~10 | High |
| Word-by-word scroll reveal | GSAP ScrollTrigger | ~15 | High |
| Typewriter effect | TypeIt / Custom | ~5 / ~60 | Medium |
| Text scramble/decode | GSAP ScrambleText / Custom | ~5 / ~30 | Medium |
| Number counter | CountUp.js / GSAP | ~5 | Medium |
| Gradient text animation | CSS only | ~8 | Low |
| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| Pinned section + scrub | GSAP ScrollTrigger | ~15 | Very High |
| Smooth scroll + parallax | ScrollSmoother / Lenis | ~8 | High |
| Simple fade-in on scroll | Intersection Observer | ~10 | Medium |
| Horizontal scroll section | GSAP ScrollTrigger | ~20 | High |
| Progress indicator | CSS scroll-timeline | ~8 | Low |
| Parallax headings | GSAP ScrollTrigger | ~10 | Medium |
| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| Particle mesh background | Three.js | ~80 | Very High |
| Floating 3D object | Three.js | ~40 | High |
| Post-processing bloom | Three.js EffectComposer | ~20 | High |
| Instanced particles | Three.js InstancedMesh | ~30 | High |
| GLTF model display | Three.js GLTFLoader | ~20 | High |
| Shader effects | Three.js ShaderMaterial | ~50+ | Very High |
| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| Path stroke drawing | GSAP DrawSVG | ~8 | High |
| Shape morphing | GSAP MorphSVG | ~5 | High |
| Line art sequence | Vivus.js | ~8 | Medium |
| Lottie animation | Lottie Web | ~10 | High |
| Animated icons | Lottie / CSS | ~10 | Medium |
| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| Magnetic button | GSAP / Custom | ~12 | Medium |
| 3D card tilt | vanilla-tilt.js / Custom | ~3 / ~15 | Medium |
| Ripple click effect | CSS + JS | ~15 | Low |
| Custom cursor trail | Canvas + RAF | ~40 | Medium |
| Hover color shift | CSS transitions | ~5 | Low |
| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| FLIP reorder/filter | GSAP Flip | ~8 | High |
| Auto-animate children | AutoAnimate | ~1 | Medium |
| Stagger grid reveal | GSAP / anime.js | ~5 | Medium |
| Accordion expand | CSS / GSAP | ~10 | Low |
| Technique | Library | Lines of Code | Impact |
|---|---|---|---|
| Hand-drawn highlight | Rough Notation | ~5 | High |
| Hand-drawn underline | Rough Notation | ~5 | Medium |
| Hand-drawn circle | Rough Notation | ~5 | High |
| Sequential annotation group | Rough Notation | ~10 | Very High |
| Bracket emphasis | Rough Notation | ~5 | Medium |
| Audience | Primary Stack | Add-Ons | Total Size |
|---|---|---|---|
| Corporate | GSAP + ScrollTrigger + Rough Notation | CountUp.js | ~45kb |
| Developer | GSAP + ScrollTrigger + SplitText + DrawSVG | TypeIt | ~50kb |
| Creative | GSAP + ScrollTrigger + SplitText + Three.js | Lenis, vanilla-tilt | ~210kb |
| Marketing | GSAP + ScrollTrigger + SplitText + Rough Notation | CountUp.js, confetti | ~50kb |
| Educational | GSAP + ScrollTrigger + SplitText + DrawSVG + Rough Notation | TypeIt | ~55kb |