What this guide covers

In Part 1, I covered why mathematical animation is replacing stock photography and introduced the three-tier framework for choosing the right complexity level. This article is the practical companion — every formula family worth knowing, the tools that emerged in 2024–2025, and production-ready patterns you can drop into real projects.

This guide is designed to stand on its own. You don't need to read Part 1 first, but if you want the business context and strategic framework, it's there.


Three tiers in practice: implementation deep dive

Each complexity tier comes with its own trade-offs in performance, interactivity, and engineering effort. Here's what each one actually looks like in code.

Simple tier: Pure CSS trigonometric animations

Live — CSS Trigonometric Orbital Animation (zero JavaScript rendering)

Modern CSS natively supports trigonometric functions: sin(), cos(), tan(), asin(), acos(), atan(), and atan2(). These reached baseline support across all major browsers — Chrome 111+, Firefox 108+, Safari 15.4+, Edge 111+ — making them production-safe for any project today.

Combined with the @property rule for animatable custom properties and calc() for complex expressions, you can create genuine trigonometric orbit animations, wave movements, and parametric positioning entirely without JavaScript.

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.floating-element {
  animation: cosmic-drift 8s linear infinite;
  transform:
    translateX(calc(sin(var(--angle)) * 80px))
    translateY(calc(cos(var(--angle) * 1.3) * 50px))
    rotate(calc(sin(var(--angle) * 0.7) * 8deg));
  opacity: calc(0.6 + sin(var(--angle) * 0.5) * 0.3);
}

@keyframes cosmic-drift {
  to { --angle: 360deg; }
}

The key insight: multiplying --angle by different non-integer factors (1.3, 0.7, 0.5) within the same element creates a Lissajous-like compound motion. The element traces an irregular, never-quite-repeating path that reads as natural movement rather than mechanical rotation.

Performance reality: 60fps on all devices including 3-year-old budget phones. Zero main thread impact. Negligible battery drain. Memory under 10MB. You can animate 50+ elements simultaneously because these run entirely on the browser's compositor thread.

Medium tier: p5.js and Canvas API

Live — Perlin Noise Flow Field (move cursor to interact)

When you need interactivity — response to mouse position, scroll depth, or a system of hundreds to thousands of particles — p5.js is the most accessible entry point. Its version 2.0, released in April 2025, was the biggest update since the library's creation in 2013.

The headline feature is p5.strands — a system that lets developers write GPU shaders in JavaScript instead of GLSL. This collapses what was a steep learning curve into something any web developer can approach.

let particles = [];
const PARTICLE_COUNT = 3000;
const NOISE_SCALE = 0.005;

function setup() {
  createCanvas(windowWidth, windowHeight);
  colorMode(HSB, 360, 100, 100, 100);

  for (let i = 0; i < PARTICLE_COUNT; i++) {
    particles.push({
      x: random(width), y: random(height),
      prevX: 0, prevY: 0,
      speed: random(1, 3),
      hue: random(180, 260)
    });
  }
  background(15, 20, 10);
}

function draw() {
  fill(15, 20, 10, 5);
  noStroke();
  rect(0, 0, width, height);

  let t = frameCount * 0.002;

  for (let p of particles) {
    p.prevX = p.x;
    p.prevY = p.y;

    // The core: noise → angle → movement
    let angle = noise(p.x * NOISE_SCALE, p.y * NOISE_SCALE, t)
                * TWO_PI * 2;
    p.x += cos(angle) * p.speed;
    p.y += sin(angle) * p.speed;

    // Wrap edges
    if (p.x < 0) p.x = width;
    if (p.x > width) p.x = 0;
    if (p.y < 0) p.y = height;
    if (p.y > height) p.y = 0;

    let d = dist(p.x, p.y, mouseX, mouseY);
    let alpha = map(d, 0, 300, 60, 15);
    stroke(p.hue, 60, 85, alpha);
    strokeWeight(1);
    line(p.prevX, p.prevY, p.x, p.y);
  }
}

The formula at the core is simple: noise(x, y, t) → angle → cos/sin → movement. From this one operation, 3,000 particles create an organic, flowing visual that responds to time and mouse position. Changing NOISE_SCALE from 0.005 to 0.002 produces long sweeping curves; 0.02 produces turbulent chaos.

Performance profile: 30–60fps on desktop, 15–30fps on mobile. Memory: 10–100MB depending on particle count. Critical optimizations include object pooling, implementing prefers-reduced-motion, and throttling to 30fps on mobile.

Complex tier: WebGL/GLSL shaders

Live — Mandelbrot Fractal Zoom (Seahorse Valley, WebGL)

For hero sections that need to command attention, fractal explorers, or 3D mathematical surfaces — GPU shaders are non-negotiable. A fragment shader transforms a mathematical formula directly into pixels, with the GPU executing the computation for millions of pixels in parallel.

precision highp float;
uniform float u_time;
uniform vec2 u_resolution;

void main() {
    vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution)
              / min(u_resolution.x, u_resolution.y);

    // Animated zoom into Seahorse Valley
    float zoom = pow(2.0, -u_time * 0.3);
    vec2 center = vec2(-0.745, 0.186);
    vec2 c = center + uv * zoom;

    vec2 z = vec2(0.0);
    int maxIter = 256;
    int iter;
    for (iter = 0; iter < maxIter; iter++) {
        z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
        if (dot(z, z) > 4.0) break;
    }

    float t = float(iter) / float(maxIter);
    if (iter < maxIter) {
        float sl = t - log2(log2(dot(z,z))) + 4.0;
        t = sl / float(maxIter);
    }

    vec3 color = 0.5 + 0.5 * cos(
        3.0 + t * 6.28 + vec3(0.0, 0.6, 1.0)
    );
    if (iter == maxIter) color = vec3(0.0);

    gl_FragColor = vec4(color, 1.0);
}

This shader runs at 60fps on any device with a discrete GPU. The mathematical formula is just z = z² + c — iterated until escape or maximum iterations — but the GPU evaluates it independently for every pixel on screen, in parallel. At 1920x1080, that's over 2 million simultaneous computations per frame.


The formula catalog

Mathematics offers an infinite library of visual forms. Here are the seven formula families I use most frequently, organized by visual character and design application. Each one can be implemented at any of the three tiers above.

Trigonometric waves — the foundation

Formula: y = A · sin(ωx + φ + vt)

A single sine wave creates undulating motion — ocean surfaces, audio visualizer aesthetics, breathing indicators. But the real power is composition. Combining two frequencies close together produces beat frequency interference — a pulsing amplitude modulation that feels organic, as if the animation has a heartbeat.

Use for: section dividers, loading states, audio visualization, ambient background movement.

Live — Trigonometric Wave Superposition

Lissajous curves — mathematical elegance

Formula: x = A·sin(at + δ), y = B·sin(bt)

The visual character depends entirely on the ratio a/b. Rational ratios produce closed curves: 1:2 (figure-8), 3:2 (trefoil knot), 5:4 (star pattern). Irrational ratios like √2 mean the curve never closes — it progressively fills space, creating an evolving, never-repeating drawing.

Use for: logo animations, cursor trails, decorative motion glyphs, loading spinners.

Live — Lissajous Curve with Evolving Frequency Ratio

Perlin noise flow fields — organic streaming

Formula: angle(x,y) = noise(x·scale, y·scale, t) · 2π

Invisible force fields made visible. Thousands of particles follow noise-derived vectors, creating patterns that evoke wind, water, or migration. Fractal Brownian Motion is simply layered noise at increasing frequencies and decreasing amplitudes — the mathematical core behind the Stripe gradient.

Use for: hero backgrounds, ambient textures, generative art, data visualization metaphors.

Mandelbrot and Julia fractals — infinite detail

Formula: z(n+1) = z(n)² + c

Infinite detail at every zoom level. Animated zoom into Seahorse Valley produces a journey that can run for hours without repeating. Julia sets — the same formula with fixed c — create mesmerizing morphing effects when c is animated along a path.

Use for: hero sections, exploration interfaces, infinite zoom experiences, generative identity systems.

Fibonacci phyllotaxis — nature's pattern

Formula: Point n at angle n · 137.508°, radius c · √n

The sunflower seed spiral. The golden angle (137.508°) ensures optimal packing, producing patterns instantly recognizable as natural even when rendered abstractly. Change the angle by 0.1° and the pattern collapses into visible spokes.

Use for: data displays, particle effects, organic scatter, nature-inspired layouts.

Live — Fibonacci Phyllotaxis (golden angle 137.508°, move cursor to interact)

Strange attractors — deterministic chaos

Clifford: x(n+1) = sin(ay) + c·cos(ax), y(n+1) = sin(bx) + d·cos(by)

Four parameters control infinite variety. Pre-computing 80,000 points and progressively revealing them creates an "emergence" effect — watching structure materialize from apparent randomness. Each parameter set produces a completely different visual signature.

Use for: generative brand identity, unique visual fingerprints, artistic hero sections, loading experiences.

Live — Clifford Strange Attractor (80,000 points, progressive reveal)

Superformula — one formula, infinite shapes

Formula: r(θ) = (|cos(mθ/4)/a|^n₂ + |sin(mθ/4)/b|^n₃)^(-1/n₁)

Adjust parameters to get circles, stars, organic blobs, crosses, flowers, or biological cell shapes. Animating parameters creates continuous morphing — one element transforming endlessly. This single formula can replace an entire library of SVG icons with a parametric shape engine.

Use for: morphing logos, parametric icons, organic shape transitions, generative patterns.

Interactive superformula explorer — coming soon


The Stripe gradient: FBM in production

I covered the full four-layer Stripe gradient breakdown in Part 1. The core technique is Fractal Brownian Motion — six octaves of noise stacked with a rotation matrix between octaves to break grid alignment. Here's the FBM function for reference:

float fbm(vec2 p) {
    float v = 0.0, a = 0.5;
    mat2 rot = mat2(cos(0.5), sin(0.5),
                    -sin(0.5), cos(0.5));
    for (int i = 0; i < 6; i++) {
        v += a * noise(p);
        p = rot * p * 2.0;
        a *= 0.5;
    }
    return v;
}
Live — Fractal Brownian Motion Gradient (Stripe-style, WebGL)

The 2024–2025 tools most developers haven't discovered

TypeGPU — TypeScript shaders with full type safety

Created by Software Mansion. Write WebGPU shaders entirely in TypeScript with end-to-end type safety from CPU to GPU. Automatic byte alignment, shader compilation from TypeScript, Three.js integration from v0.9.0, React Native support via react-native-wgpu.

Use when: you need type-safe WebGPU in a TypeScript codebase. Skip when: quick prototyping — the setup overhead doesn't pay off for throwaway experiments.

ralph-gpu — WebGPU in one function call

From Vercel Labs. Write a WGSL fragment shader, call .draw(). Auto-injects uniforms for resolution, time, delta time, frame count, and aspect ratio. First-class ping-pong buffer support for fluid simulations.

Use when: rapid shader prototyping or one-off visual effects. Skip when: you need complex scene graphs or 3D — it's 2D fragment shaders only.

POINTS — WebGPU-native generative art

Built entirely on WebGPU with built-in shader modules for noise, signed distance functions, color manipulation, and mathematical effects. 2,398+ commits, actively maintained. The first WebGPU-native creative coding framework.

Use when: building generative art or creative coding projects targeting WebGPU. Skip when: you need broad browser support today — WebGPU isn't universal yet.

Mafs — React components for math visualization

~4,000+ GitHub stars. Opinionated React components for interactive 2D math: coordinate planes, function plots, vectors, parametric curves, drag interactions.

Use when: you need a math visualization in React that looks great immediately. Skip when: you need custom rendering or 3D — it's specifically 2D educational/documentation math.

The WebGPU milestone

WebGPU reached a critical milestone: all major browsers now ship it by default, covering roughly 70% of desktop users. Chrome and Edge since 2023, Firefox 141 on Windows (July 2025), Firefox 145 on macOS (Apple Silicon only), Safari 26 in macOS Tahoe — enabled by default, not experimental. Linux and Android remain in progress, so mobile coverage is still lower.

The performance gains are transformative. A particle system running 10,000 particles at 30ms per frame via WebGL handles 100,000 particles in under 2ms with WebGPU compute shaders — roughly 150x improvement.


Three patterns worth stealing

Every technique above can be distilled into a minimal, production-ready pattern. Three combinations that work as drop-in design elements — each constrained to a single color, generous negative space, and under 40 lines of canvas code.

Sine + Interference

Waveform Divider

Replace static ‹hr› with breathing sine interference. Five overlapping frequencies, white only, opacity 6–18%.

FBM + Grid

Ambient Texture

Noise-driven pixel field as a living surface. One hue, near-invisible — adds depth without competing with content.

Lissajous + Trail

Motion Glyph

A 3:2 Lissajous trace as an animated accent mark. Single curve, fading trail — a living decorative element.


Performance, accessibility, and Core Web Vitals

Mathematical animations must coexist with Google's Core Web Vitals: LCP under 2.5 seconds, INP under 200ms, CLS under 0.1. Sites passing all three consistently see lower bounce rates. Here's how to get there.

Loading strategy

Heavy libraries loaded synchronously block rendering. Three.js: ~150KB gzipped. p5.js: ~300KB. Code-split animation code out of the critical path, use async/defer on script tags, and never make the animated canvas the Largest Contentful Paint element.

Viewport lifecycle management

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) animation.loop();
    else animation.noLoop();
  });
}, { threshold: 0.1 });
observer.observe(document.querySelector('canvas'));

Adaptive quality detection

function getQualityTier() {
  const cores = navigator.hardwareConcurrency || 4;
  const memory = navigator.deviceMemory || 4;
  const isMobile = window.innerWidth < 768;

  if (isMobile || cores <= 2 || memory <= 2) return 'low';
  if (cores <= 4 || memory <= 4) return 'medium';
  return 'high';
}

const qualityMultipliers = {
  low: 0.25, medium: 0.5, high: 1.0
};
const particleCount = Math.floor(
  5000 * qualityMultipliers[getQualityTier()]
);

This gives you 1,250 particles on a budget phone, 2,500 on a mid-range device, and the full 5,000 on a desktop.

Accessibility is non-negotiable

prefers-reduced-motion must be respected — not by eliminating motion, but by replacing pulsing with gentle fading and slowing transitions. WCAG 2.2.2 (AA): provide a mechanism to pause animations lasting 5+ seconds. WCAG 2.3.1: no content flashing more than 3 times per second.


What's next: AI, real-time data, and WebGPU compute

AI-assisted creative coding can already generate working p5.js sketches, GLSL shaders, and CSS animations from natural language. The barrier to a first draft is near zero. But the barrier to a production-quality result — performant, accessible, mathematically refined — remains high. AI generates code that compiles; human judgment shapes code that impresses.

Real-time data integration turns animation into ambient communication. Dashboard backgrounds driven by live metrics — pulse faster when traffic spikes, shift color based on revenue trends, particle density mapping to user activity.

WebGPU compute shaders unlock previously impossible browser simulations: millions of particles with inter-particle forces, fluid dynamics, cloth simulation, real-time procedural terrain — all at 60fps in a browser tab.


Production checklist

Before shipping any mathematical animation, verify:

Performance

  • Animation doesn't block Largest Contentful Paint
  • Scripts loaded with async or defer
  • Intersection Observer starts/stops based on viewport visibility
  • Frame rate throttled to 30fps on mobile
  • Tab visibility API pauses animation when hidden
  • Particle counts adapt to device capability

Accessibility

  • prefers-reduced-motion reduces or pauses animation
  • Pause mechanism available for animations longer than 5 seconds
  • No content flashes more than 3 times per second
  • Canvas elements have appropriate aria-hidden or aria-label
  • Animation doesn't interfere with screen reader navigation

Quality

  • Animation communicates brand values, not just visual noise
  • Seeded randomness enables reproducible, shareable states
  • Fallback exists for devices without WebGL/WebGPU support
  • Colors work in both light and dark contexts
  • Animation degrades gracefully on low-end devices

Start with one formula, ship it, then iterate

You don't need seven formula families to ship something impressive. Pick one — a CSS sine wave divider, a canvas flow field, or a GLSL fractal hero. Get it running, measure the Core Web Vitals impact, and iterate from there.

The formulas aren't going anywhere. The tools keep getting better. The only variable is when you start.

If you want the strategic framework — why this matters for business, the cost breakdown, and the ROI case — read Part 1: How Mathematical Animation Is Replacing Stock Photography.

Ready to implement mathematical animation in your next project? Let's discuss which formulas and tools fit your goals.