Animation lab

Ten animation libraries, actually measured.

Everything below is wired into a real production build of this site — not a sandbox. Each library was installed, integrated, measured in the emitted bundle, and then judged on whether it earned its bytes. Sizes come from the live vite build output: raw, gzip and brotli, per library.

Verdicts & bundle cost

“Adopted” means shipped on the main page. “Lab only” means integrated and demonstrated here but deliberately left out of the production critical path.

Library Verdict Raw Gzip Brotli What it does here
Measuring the build…

Live demos

Scroll-driven demos start when they enter the viewport. Interactive ones are labelled with what to try.

GSAP + ScrollTrigger

Timeline sequencing & scroll scrubbing

Adopted · core

One timeline drives 12 staggered bars. The main page uses the same API for the hero intro, every heading reveal and the scrubbed experience timeline.

Lenis

Inertial smooth scrolling

Adopted · global
scroll velocity0.00
progress0%

This whole page is scrolling through Lenis right now — the native scrollbar is bypassed.

Driven by the GSAP ticker, so both libraries share a single rAF loop and ScrollTrigger stays in sync with the eased position.

SplitType

Text splitting for masked reveals

Adopted · headlines

Ship the interface, not just the model.

—

The wrapper in src/lib/split.js sets aria-label on the container and aria-hidden on every fragment, so screen readers still hear one clean sentence.

Motion motion.dev

Spring physics for UI

Adopted · UI layer
spring
tween

Click a track — the spring overshoots and settles, the tween just stops.

Motion owns every short, retargetable interaction: the sliding nav pill, the mobile menu, cursor spotlight, card tilt and magnetic buttons.

anime.js v4

SVG drawing, motion paths, text scramble

Adopted · diagram

svg · stagger · motionpath · scramble

svg.createDrawable animates the curve, createMotionPath sends the dot along it, stagger pops the row, and scrambleText decodes the caption. This card is the only remaining use of anime.js — the diagram it drove was cut from the portfolio.

three.js

WebGL shader + 3D geometry

Adopted · heaviest
fps—
draw calls—
renderer—

The largest dependency by a wide margin. It powers the hero only — a full-bleed GLSL aurora plus a 3D point constellation, suspended when off-screen or when the tab is hidden. A site that cannot justify that weight should keep the 2D canvas fallback that ships beside it.

tsParticles slim

Declarative interactive particles

Adopted · contact only

Move the pointer through it, or click to push particles out.

Right tool for the contact card, wrong tool for the hero — a declarative config replaces ~150 lines of bespoke canvas work, but it cannot match a purpose-built shader. The engine is composed by hand from @tsparticles/basic plus three interactions rather than using the convenient slim preset, which halves the payload by dropping emoji/star/polygon shapes and nine interactions this site never uses.

Lottie lottie-web

Vector animation from JSON

Adopted · hero badge
agent-core.json — authored for this site (2.0s loop)
echarts chart.json — Apache-2.0, third-party

The site's own asset is generated programmatically, so it carries no licensing questions. The second player proves the runtime handles third-party artwork too.

Rive @rive-app/canvas

State-machine driven vector animation

Lab only · asset gate
rating.riv — MIT, from rive-app/rive-react
Loading runtime…

Verdict: technically excellent, blocked on the asset pipeline. The runtime is MIT, has no attribution requirement, and its player is far smaller than Lottie's. But .riv is a compiled binary that can only be produced by the Rive editor, and exporting one needs a paid plan (Cadet, ~$9/seat/mo). You can play any .riv for free — which is exactly what this demo does with the MIT-licensed sample. It is absent from the main page because a star-rating widget says nothing about AI engineering.

AutoAnimate

Automatic FLIP layout transitions

Adopted · skills filter

    Zero configuration beyond attaching it to the parent — it diffs child positions and interpolates the reflow. On the main page it animates the capability grid whenever a filter chip changes what is rendered.

    How the stack was split

    GSAP owns when

    Anything with a timeline or a scroll position: the pinned hero flight and the scrubbed exit of the hero copy. One ScrollTrigger, plus the refresh() bookkeeping that keeps its measurements honest after fonts and resizes land.

    Lenis owns feel

    Inertial scrolling, driven off the GSAP ticker so the two share a single rAF loop. The flight camera reads better against interpolated scroll than against raw wheel deltas.

    three.js owns the frame

    The dark chapter: node graph, shader floor, dust field and the bloom → tone map → grade chain. Scroll drives a scroll-scrubbed position along a curve; dragging adds a look offset in a separate accumulator, so the two inputs cannot fight.