view live animations at: matthen.github.io/animations.
This is a collection of mathematical animations, written as small React components.
- animations - code for each animation
- lib/Animation.tsx - react component for rendering and exporting animations
- lib/graphics.ts - library to simplify drawing to 2d canvas
This project uses pnpm as the package manager.
pnpm install# Start development server (runs on localhost:3000)
pnpm startThe development server supports hot reloading - changes to animation code will automatically refresh in the browser.
# Create optimized production build
pnpm build
# The output will be in the build/ directoryUse the interactive animation generator:
pnpm new-animationThis will prompt you to:
- Choose animation name
- Select type (shader or 2D canvas)
- Define parameters (all default to 0-1 range)
The script generates all necessary files with proper templates and TODO comments to guide implementation.
The Graphics library (src/lib/graphics.ts) provides a declarative approach to Canvas 2D drawing, inspired by Mathematica's Graphics[] system. Instead of imperatively calling canvas methods, you build a nested structure of graphics objects that automatically manage drawing context.
Graphics uses a mathematical coordinate system rather than pixel coordinates. You specify the drawing bounds with xmin, xmax, ymin, and ymax options, and the library automatically handles the canvas transformation. Coordinates work as expected mathematically:
- X-axis: increases left to right (like pixels)
- Y-axis: increases bottom to top (unlike pixels, which increase top to bottom)
For example, with bounds {xmin: -1, xmax: 1, ymin: -1, ymax: 1}, the point [0, 0] is at the center, [1, 1] is top-right, and [-1, -1] is bottom-left.
Graphics uses DrawCommand functions organized in nested arrays. Each array level creates a new canvas context scope using save() and restore(), allowing transformations and style changes to be automatically contained within their scope.
Graphics.draw(
[
// Set canvas context properties at root level (ctx.save() called automatically)
Graphics.Set({ strokeStyle: 'white', fillStyle: 'red' }),
Graphics.AbsoluteLineWidth(4),
// Red-filled disk with white outline
Graphics.Disk({ center: [0, 0], radius: 0.5, fill: true, edge: true }),
[
// New context scope (ctx.save() called) - inherits white stroke + red fill
// Override fill to blue while keeping white stroke inherited from parent
Graphics.Set({ fillStyle: 'blue' }),
// Blue-filled square with inherited white outline
Graphics.Polygon({
pts: [
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
edge: true,
fill: true,
}),
[
// Deeper context scope (nested ctx.save()) - inherits white stroke + blue fill
// Add rotation transform + change fill to white for text
Graphics.Rotate({ angle: -Math.PI / 4, center: [0, 0] }),
Graphics.Set({ font: '0.25px monospace', fillStyle: 'white' }),
Graphics.Text({ at: [0, 0], text: 'hello' }),
// When this scope ends: ctx.restore() removes rotation + white text fill
],
// Back in blue-fill scope: rotation gone, fillStyle blue, strokeStyle white
],
// Back in root scope (ctx.restore() called): fillStyle red, strokeStyle white, no transforms
[
// New sibling scope (ctx.save() called) - inherits red fill + white stroke
// Add translation transform
Graphics.Translate({ offset: [-1, -1] }),
// Red-filled square at translated position with white outline
Graphics.Polygon({
pts: [
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
edge: true,
fill: true,
}),
// When this scope ends: ctx.restore() removes translation
],
// Back in root scope: original red fill + white stroke, no transforms
],
{
xmin: -1.1,
xmax: 1.1,
ymin: -1.1,
ymax: 1.1,
},
ctx,
);Shapes:
Graphics.Disk()- Circles and arcs with fill/edge optionsGraphics.Polygon()- Filled/outlined polygons from point arraysGraphics.Line()- Polylines from point arraysGraphics.Text()- Text at specified coordinates
Transformations:
Graphics.Rotate()- Rotate around a center pointGraphics.Translate()- Translate by offsetGraphics.Scale()- Scale around a center point
Styling:
Graphics.Set()- Set canvas properties (colors, line width, etc.)Graphics.AbsoluteLineWidth()- Line width in pixels regardless of zoom