Math Art: How to Create Beautiful Mathematical Patterns With Code
Math art is exactly what it sounds like — visual art generated from mathematical formulas. A sine wave becomes a flowing ribbon. A polar equation draws a rose. A recursive function builds a spiral galaxy. The surprising thing isn't that math can produce art, it's how little math you need to create something genuinely beautiful.
This guide walks you through the most rewarding mathematical patterns for visual art, each with working JavaScript code you can paste into a browser and immediately see results. No advanced math background required — if you can read x = Math.cos(angle), you're ready.
Why math makes beautiful art
Humans are wired to find mathematical patterns beautiful. Symmetry, proportion, repetition with variation — these are the foundations of aesthetics across cultures. The Parthenon uses the golden ratio. Islamic architecture is built on tessellations. Nautilus shells follow logarithmic spirals. When you code math art, you tap into the same patterns that nature and architecture have used for millennia.
The appeal for creative coders is that a single formula can generate infinite variation. Change one parameter and a calm spiral becomes a chaotic storm. That's the creative space — not inventing the math, but exploring it.
Setting up your canvas
Every example in this article uses the same minimal HTML setup — a canvas element and vanilla JavaScript. No libraries, no frameworks, no build tools:
<canvas id="c" width="800" height="800"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
ctx.fillStyle = '#0a0a0a';
ctx.fillRect(0, 0, W, H);
// Your math art code goes here
</script>
Save this as an HTML file, open it in a browser, and you have a blank canvas ready for math.
1. Spirals: the golden spiral
The golden spiral appears everywhere in nature — sunflower seed heads, hurricane formations, galaxies. It's based on the golden angle (approximately 137.508 degrees), which distributes points with maximum spacing.
const goldenAngle = Math.PI * (3 - Math.sqrt(5));
for (let i = 0; i < 2000; i++) {
const angle = i * goldenAngle;
const radius = Math.sqrt(i) * 8;
const x = W/2 + radius * Math.cos(angle);
const y = H/2 + radius * Math.sin(angle);
const hue = (i * 0.15) % 360;
const size = 2 + (i / 2000) * 4;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = `hsl(${hue}, 70%, 60%)`;
ctx.fill();
}
This produces a phyllotaxis pattern — the same arrangement used by sunflowers. Each dot is placed at a fixed angular offset from the previous one, with the radius growing as the square root of the index. The result is organic and balanced, despite being generated from pure arithmetic.
2. Rose curves: polar flowers
Rose curves are polar equations of the form r = cos(k * theta) where k determines the number of petals. When k is an integer, you get a flower with exactly k petals (if k is odd) or 2k petals (if k is even). When k is a fraction, things get wonderfully complicated.
ctx.strokeStyle = 'rgba(255, 120, 200, 0.6)';
ctx.lineWidth = 1.5;
for (let k = 2; k <= 7; k++) {
ctx.beginPath();
for (let theta = 0; theta <= Math.PI * 2; theta += 0.01) {
const r = 150 * Math.cos(k * theta);
const x = W/2 + r * Math.cos(theta);
const y = H/2 + r * Math.sin(theta);
if (theta === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.strokeStyle = `hsla(${k * 50}, 80%, 65%, 0.5)`;
ctx.stroke();
}
Try changing k to a fraction like 5/3 or 7/4 — the curve no longer closes after one revolution and traces out elaborate overlapping patterns. The ratio determines how many loops the curve makes before connecting back to its start.
3. Lissajous curves: harmonic beauty
Lissajous curves are the visual form of two oscillations happening simultaneously — like sound waves in stereo. The parametric equations are x = sin(a*t + delta) and y = sin(b*t), where the ratio a:b determines the shape and delta is the phase offset.
ctx.lineWidth = 2;
const a = 3, b = 4, delta = Math.PI / 4;
const scale = 300;
ctx.beginPath();
for (let t = 0; t <= Math.PI * 2; t += 0.005) {
const x = W/2 + scale * Math.sin(a * t + delta);
const y = H/2 + scale * Math.sin(b * t);
if (t === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.strokeStyle = '#4ecdc4';
ctx.stroke();
For artistic variation, draw many Lissajous curves with slowly shifting parameters:
for (let i = 0; i < 50; i++) {
const a = 3, b = 4;
const delta = (i / 50) * Math.PI;
const alpha = 1 - i / 50;
ctx.beginPath();
for (let t = 0; t <= Math.PI * 2; t += 0.005) {
const x = W/2 + 300 * Math.sin(a * t + delta);
const y = H/2 + 300 * Math.sin(b * t);
if (t === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.strokeStyle = `hsla(${180 + i * 3}, 70%, 60%, ${alpha * 0.3})`;
ctx.stroke();
}
This creates a 3D-like volume from flat curves — each frame of the phase shift stacks to form what looks like a rotating shape frozen in time.
4. Tessellations: filling the plane
Tessellation is the art of filling a surface with repeating shapes that leave no gaps. The simplest tessellations use regular polygons — triangles, squares, hexagons. But the interesting math art comes from irregular and Penrose tilings, where shapes repeat without true periodicity.
// Hexagonal tessellation with color variation
const hexSize = 30;
const rows = Math.ceil(H / (hexSize * 1.5)) + 1;
const cols = Math.ceil(W / (hexSize * Math.sqrt(3))) + 1;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = col * hexSize * Math.sqrt(3) + (row % 2) * hexSize * Math.sqrt(3) / 2;
const y = row * hexSize * 1.5;
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (Math.PI / 3) * i - Math.PI / 6;
const hx = x + hexSize * Math.cos(angle);
const hy = y + hexSize * Math.sin(angle);
if (i === 0) ctx.moveTo(hx, hy);
else ctx.lineTo(hx, hy);
}
ctx.closePath();
// Color based on distance from center
const dist = Math.hypot(x - W/2, y - H/2);
const hue = (dist * 0.5 + row * 10 + col * 10) % 360;
ctx.fillStyle = `hsl(${hue}, 60%, ${30 + Math.sin(dist * 0.02) * 20}%)`;
ctx.fill();
ctx.strokeStyle = 'rgba(255,255,255,0.1)';
ctx.stroke();
}
}
Hexagonal grids appear everywhere in math art because they're the most efficient way to divide a plane into equal areas — bees figured this out long before mathematicians proved it.
5. Superformula: one equation to draw everything
The superformula, discovered by Johan Gielis in 2003, is a generalization of the circle that can produce an astonishing range of natural shapes — from starfish to flowers to butterflies — by adjusting six parameters:
function superformula(theta, a, b, m, n1, n2, n3) {
const t1 = Math.pow(Math.abs(Math.cos(m * theta / 4) / a), n2);
const t2 = Math.pow(Math.abs(Math.sin(m * theta / 4) / b), n3);
return Math.pow(t1 + t2, -1 / n1);
}
// Draw a starfish shape
ctx.beginPath();
const params = { a: 1, b: 1, m: 5, n1: 2, n2: 7, n3: 7 };
for (let theta = 0; theta <= Math.PI * 2; theta += 0.01) {
const r = 200 * superformula(theta, params.a, params.b, params.m, params.n1, params.n2, params.n3);
const x = W/2 + r * Math.cos(theta);
const y = H/2 + r * Math.sin(theta);
if (theta === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.strokeStyle = '#ff6b6b';
ctx.lineWidth = 2;
ctx.stroke();
Try these parameter sets for different shapes:
- Circle: m=0, n1=1, n2=1, n3=1
- Square (rounded): m=4, n1=100, n2=100, n3=100
- Star: m=5, n1=30, n2=100, n3=100
- Butterfly: m=3, n1=0.5, n2=0.5, n3=0.5
- Organic blob: m=7, n1=0.2, n2=1.7, n3=1.7
Layer multiple superformula shapes with different parameters and transparency for kaleidoscopic compositions.
6. Strange attractors: chaos made visible
Strange attractors are systems of equations where points orbit in patterns that never exactly repeat but never leave a bounded region. The most famous is the Lorenz attractor (the "butterfly effect" shape), but for 2D art, the Clifford attractor is more visually striking:
// Clifford attractor
const a = -1.4, b = 1.6, c = 1.0, d = 0.7;
let x = 0.1, y = 0.1;
const imgData = ctx.createImageData(W, H);
for (let i = 0; i < 5000000; i++) {
const nx = Math.sin(a * y) + c * Math.cos(a * x);
const ny = Math.sin(b * x) + d * Math.cos(b * y);
x = nx;
y = ny;
// Map to canvas coordinates
const px = Math.floor((x + 3) / 6 * W);
const py = Math.floor((y + 3) / 6 * H);
if (px >= 0 && px < W && py >= 0 && py < H) {
const idx = (py * W + px) * 4;
imgData.data[idx] = Math.min(255, imgData.data[idx] + 3); // R
imgData.data[idx+1] = Math.min(255, imgData.data[idx+1] + 1); // G
imgData.data[idx+2] = Math.min(255, imgData.data[idx+2] + 5); // B
imgData.data[idx+3] = 255;
}
}
ctx.putImageData(imgData, 0, 0);
The trick with attractor art is iteration count. At 1,000 points you see dots. At 100,000 you see structure. At 5 million, fine detail emerges like a photograph developing. Change the four parameters (a, b, c, d) even slightly and the entire shape transforms — an infinite landscape of visual possibility from four numbers.
7. Moiré patterns: interference art
Moiré patterns occur when two regular patterns overlap at slightly different scales or angles. The interference creates large-scale waves and ripples that seem to shimmer — an optical illusion emerging purely from geometry.
// Two sets of concentric circles creating moiré interference
for (let i = 0; i < 200; i++) {
const r = i * 5;
// First center
ctx.beginPath();
ctx.arc(W/2 - 50, H/2, r, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(0, 200, 255, 0.3)';
ctx.lineWidth = 1;
ctx.stroke();
// Second center, slightly offset
ctx.beginPath();
ctx.arc(W/2 + 50, H/2, r, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(255, 100, 200, 0.3)';
ctx.stroke();
}
The resulting pattern shows hyperbolic curves emerging between the two sets of circles — curves that exist nowhere in the code, only in the interaction between the two grids. This is a powerful idea in math art: complexity from the interference of simple systems.
8. The golden ratio in composition
The golden ratio (phi, approximately 1.618) appears throughout art and nature. For generative art, it's useful as a proportion tool — spacing elements by phi creates compositions that feel balanced without being rigid.
const phi = (1 + Math.sqrt(5)) / 2;
// Golden rectangles nested inside each other
let x = 50, y = 50;
let w = 700, h = 700 / phi;
for (let i = 0; i < 12; i++) {
const hue = i * 30;
ctx.fillStyle = `hsla(${hue}, 60%, 50%, 0.15)`;
ctx.fillRect(x, y, w, h);
ctx.strokeStyle = `hsl(${hue}, 60%, 50%)`;
ctx.strokeRect(x, y, w, h);
// Draw quarter-circle arc
ctx.beginPath();
const arcX = (i % 4 === 0) ? x + w : (i % 4 === 1) ? x + w : (i % 4 === 2) ? x : x;
const arcY = (i % 4 === 0) ? y + h : (i % 4 === 1) ? y : (i % 4 === 2) ? y : y + h;
const arcR = Math.min(w, h);
const startAngle = (i % 4) * Math.PI / 2 + Math.PI;
ctx.arc(arcX, arcY, arcR, startAngle, startAngle + Math.PI / 2);
ctx.strokeStyle = `hsl(${hue}, 80%, 70%)`;
ctx.lineWidth = 2;
ctx.stroke();
// Subdivide by golden ratio
if (i % 2 === 0) {
const newW = w;
const newH = h - w / phi;
x = x;
y = y + w / phi;
w = newW;
h = newH;
} else {
const newW = w - h / phi;
x = x + h / phi;
w = newW;
}
}
Each rectangle's proportions maintain the golden ratio as it subdivides, with quarter-circle arcs forming the iconic golden spiral. This single structure contains the mathematical foundation that architects, painters, and designers have used for centuries.
Making it move: animated math art
Static math art is beautiful, but animation reveals the dynamics hidden in equations. The key technique is introducing a time variable into your formulas:
function animate(time) {
ctx.fillStyle = 'rgba(10, 10, 10, 0.05)'; // Trail effect
ctx.fillRect(0, 0, W, H);
const t = time * 0.001;
for (let i = 0; i < 500; i++) {
const angle = (i / 500) * Math.PI * 2;
const r1 = 200 + 50 * Math.sin(angle * 3 + t);
const r2 = 150 + 80 * Math.cos(angle * 5 - t * 0.7);
const x = W/2 + r1 * Math.cos(angle) + 30 * Math.sin(t + i);
const y = H/2 + r2 * Math.sin(angle) + 30 * Math.cos(t * 1.3 + i);
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI * 2);
ctx.fillStyle = `hsl(${(angle * 180 / Math.PI + t * 50) % 360}, 70%, 60%)`;
ctx.fill();
}
requestAnimationFrame(animate);
}
animate(0);
The rgba fill with low alpha creates a motion trail — previous frames fade slowly, giving the animation a ghostly persistence. This technique works with any of the patterns above: add + t to angles, multiply radii by sin(t), or slowly sweep through parameter space.
Combining techniques
The most compelling math art typically layers multiple techniques. A golden-ratio spiral made of superformula shapes. A tessellation with moiré interference. Attractor points colored by their Lissajous phase. The mathematics doesn't care about boundaries — every formula can be nested inside every other.
Some ideas to try:
- Spiral + rose curve: place rose curves along a golden spiral, scaling each one by its position
- Attractor + color mapping: color attractor points based on their velocity or iteration count
- Hexagonal grid + superformula: fill each hexagon with a different superformula shape
- Animated moiré: slowly rotate one pattern relative to another
- Fractal + tessellation: place fractal trees at the vertices of a hexagonal grid
From math to art: the creative decisions
The math gives you structure. The art comes from choices that have nothing to do with equations:
- Color palette: a Clifford attractor in monochrome blue feels oceanic; in fire tones it feels volcanic. The same data, completely different mood.
- Scale and framing: zoom in on a tiny region of a pattern and it becomes abstract. Zoom out and it becomes decorative.
- Density: 1,000 points feel sparse and geometric. 10 million points feel photographic and textured.
- Transparency and layering: additive blending (where overlapping elements get brighter) creates depth that solid fills can't achieve.
- Imperfection: add tiny random offsets to break mathematical rigidity. Nature isn't perfectly symmetric — a little noise makes patterns feel alive.
These artistic choices are what separate math art from math diagrams. The equation is the instrument; you're the musician.
Where to explore further
Math art is a deep field with centuries of history and an active creative coding community:
- Wolfram MathWorld — comprehensive reference for mathematical curves and surfaces
- Shadertoy — GPU-accelerated math art using GLSL shaders
- The Book of Shaders — free online book about mathematical patterns in shader code
- Creative coding tools — from p5.js to Shadertoy and beyond
- Coding Math (YouTube) — video series on the math behind creative coding
Ready to see math art in action? Explore the Lumitree — many of the micro-worlds use the same mathematical patterns described here, from spirals to attractors to tessellations. Every branch is generated from code, and every pattern emerges from a formula. Or try drawing with code yourself — the only prerequisite is curiosity and a text editor.