All articles
22 min read

Sacred Geometry: How to Create Mesmerizing Geometric Patterns With Code

sacred geometrygeometric artcreative codingJavaScriptgenerative artmath artcanvas

Sacred geometry is the study of geometric forms and proportions that appear throughout nature, architecture, and art. From the hexagonal cells of a honeycomb to the spiral arms of a galaxy, from the rose windows of Gothic cathedrals to the mandalas of Hindu temples — the same patterns emerge again and again. These shapes have fascinated mathematicians, artists, architects, and philosophers for thousands of years.

What makes sacred geometry so compelling for creative coders is that every pattern reduces to simple mathematical rules. The Flower of Life is just overlapping circles. Metatron’s Cube is just lines connecting circle intersections. The Sri Yantra is just nine interlocking triangles. Once you see the construction, you can recreate it in code — and then animate it, transform it, and make it interactive in ways that would be impossible with compass and straightedge alone.

In this guide we build eight interactive sacred geometry simulations, starting from the foundational Flower of Life and progressing to generative compositions that combine multiple sacred forms. Every example is self-contained, runs on a plain HTML Canvas with no external libraries, and stays under 50KB.

The Flower of Life: the mother pattern

The Flower of Life is perhaps the most recognizable sacred geometry symbol. It consists of evenly-spaced, overlapping circles arranged in a hexagonal pattern, forming a flower-like design. The pattern has been found carved into the Temple of Osiris in Egypt (dating to at least 535 BCE), in Chinese temples, in Indian temples, in synagogues in Galilee, and in Leonardo da Vinci’s notebooks.

The construction is elegant: start with one circle, then place six circles of the same radius with their centers on the circumference of the first circle, spaced 60 degrees apart. Each new circle’s center sits on the intersection of two existing circles. Continue outward for as many rings as you like.

The key insight is that the center positions follow a hexagonal grid. For ring r, there are 6 × r circles (except ring 0, which is just the center). Each center can be calculated using polar coordinates, then offset for each position within the ring.

Example 1: Flower of Life

This example draws the classic Flower of Life pattern with animated construction. Click to toggle between the construction animation and the complete pattern. The circles are drawn with a subtle gradient that reveals the overlapping vesica piscis shapes.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const cx = canvas.width / 2, cy = canvas.height / 2;
const R = 60; // circle radius
const rings = 3; // number of rings
let animProgress = 0;
let showAnimation = true;

// Generate all circle centers in hex grid order
function getCircleCenters() {
  const centers = [{x: cx, y: cy}]; // ring 0
  for (let r = 1; r <= rings; r++) {
    for (let i = 0; i < 6; i++) {
      const angle0 = (Math.PI / 3) * i - Math.PI / 6;
      const angle1 = (Math.PI / 3) * ((i + 1) % 6) - Math.PI / 6;
      for (let j = 0; j < r; j++) {
        const t = j / r;
        const x = cx + R * (r * Math.cos(angle0) * (1 - t) + r * Math.cos(angle1) * t);
        const y = cy + R * (r * Math.sin(angle0) * (1 - t) + r * Math.sin(angle1) * t);
        centers.push({x, y});
      }
    }
  }
  return centers;
}

const centers = getCircleCenters();

function draw() {
  ctx.fillStyle = '#0a0a1a';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  const count = showAnimation
    ? Math.min(Math.floor(animProgress), centers.length)
    : centers.length;
  for (let i = 0; i < count; i++) {
    const {x, y} = centers[i];
    const alpha = showAnimation
      ? Math.min(1, (animProgress - i) * 2)
      : 0.7;
    ctx.beginPath();
    ctx.arc(x, y, R, 0, Math.PI * 2);
    ctx.strokeStyle = `hsla(${(i * 15) % 360}, 70%, 65%, ${alpha})`;
    ctx.lineWidth = 1.5;
    ctx.stroke();
  }
  // Draw outer bounding circle
  ctx.beginPath();
  ctx.arc(cx, cy, R * (rings + 1), 0, Math.PI * 2);
  ctx.strokeStyle = 'rgba(255,255,255,0.15)';
  ctx.lineWidth = 1;
  ctx.stroke();
  if (showAnimation && animProgress < centers.length + 1) {
    animProgress += 0.03;
  }
  requestAnimationFrame(draw);
}

canvas.onclick = () => {
  showAnimation = !showAnimation;
  animProgress = 0;
};
draw();

Metatron’s Cube: connecting all things

Metatron’s Cube is derived from the Flower of Life by connecting the centers of all 13 circles in the Fruit of Life pattern (the innermost circle plus 12 surrounding circles) with straight lines. The result contains within it every Platonic solid — the tetrahedron, cube, octahedron, dodecahedron, and icosahedron — projected into two dimensions.

The construction is straightforward: place 13 circles (1 center + 6 inner ring + 6 outer ring at double distance), then draw a line between every pair of centers. That gives you 78 lines (13 choose 2), which form the complete Metatron’s Cube.

Example 2: Metatron’s Cube

This example draws Metatron’s Cube with animated line construction. The 13 circles appear first, then lines connect them one by one. The embedded Platonic solids are highlighted in different colors.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const cx = 300, cy = 300, R = 80;
let phase = 0; // 0=circles, 1=lines, 2=solids

// 13 centers: 1 center + 6 inner + 6 outer
const centers = [{x: cx, y: cy}];
for (let i = 0; i < 6; i++) {
  const a = (Math.PI / 3) * i - Math.PI / 2;
  centers.push({x: cx + R * Math.cos(a), y: cy + R * Math.sin(a)});
}
for (let i = 0; i < 6; i++) {
  const a = (Math.PI / 3) * i - Math.PI / 2;
  centers.push({x: cx + 2 * R * Math.cos(a), y: cy + 2 * R * Math.sin(a)});
}

// All 78 lines
const lines = [];
for (let i = 0; i < 13; i++)
  for (let j = i + 1; j < 13; j++)
    lines.push([i, j]);

let lineProgress = 0;
let circleProgress = 0;

// Platonic solid subsets (indices into centers)
const solids = {
  octahedron: [[1,2],[2,3],[3,4],[4,5],[5,6],[6,1],[1,4],[2,5],[3,6]],
  starTetra: [[1,3],[3,5],[5,1],[2,4],[4,6],[6,2]],
};

function draw() {
  ctx.fillStyle = '#0a0a1a';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Draw lines
  const lCount = Math.min(Math.floor(lineProgress), lines.length);
  for (let i = 0; i < lCount; i++) {
    const [a, b] = lines[i];
    const alpha = Math.min(0.2, (lineProgress - i) * 0.5);
    ctx.beginPath();
    ctx.moveTo(centers[a].x, centers[a].y);
    ctx.lineTo(centers[b].x, centers[b].y);
    ctx.strokeStyle = `rgba(150,180,255,${alpha})`;
    ctx.lineWidth = 0.8;
    ctx.stroke();
  }

  // Highlight octahedron
  if (lineProgress > lines.length) {
    const t = Math.min(1, (lineProgress - lines.length) * 0.02);
    solids.octahedron.forEach(([a, b]) => {
      ctx.beginPath();
      ctx.moveTo(centers[a].x, centers[a].y);
      ctx.lineTo(centers[b].x, centers[b].y);
      ctx.strokeStyle = `rgba(255,100,150,${t * 0.8})`;
      ctx.lineWidth = 2;
      ctx.stroke();
    });
    solids.starTetra.forEach(([a, b]) => {
      ctx.beginPath();
      ctx.moveTo(centers[a].x, centers[a].y);
      ctx.lineTo(centers[b].x, centers[b].y);
      ctx.strokeStyle = `rgba(100,255,200,${t * 0.6})`;
      ctx.lineWidth = 2;
      ctx.stroke();
    });
  }

  // Draw circles on top
  const cCount = Math.min(Math.floor(circleProgress), 13);
  for (let i = 0; i < cCount; i++) {
    ctx.beginPath();
    ctx.arc(centers[i].x, centers[i].y, R * 0.12, 0, Math.PI * 2);
    ctx.fillStyle = `hsl(${i * 27}, 70%, 70%)`;
    ctx.fill();
    ctx.beginPath();
    ctx.arc(centers[i].x, centers[i].y, R, 0, Math.PI * 2);
    ctx.strokeStyle = `hsla(${i * 27}, 50%, 60%, 0.3)`;
    ctx.lineWidth = 1;
    ctx.stroke();
  }

  circleProgress += 0.06;
  if (circleProgress > 14) lineProgress += 0.15;
  requestAnimationFrame(draw);
}
draw();

The Seed of Life: genesis in motion

The Seed of Life is the simpler precursor to the Flower of Life — just seven circles. One center circle and six surrounding circles, each passing through the center of the original. It represents the seven days of creation in several traditions and appears in the earliest stages of cell division (mitosis), where a single cell divides into 2, 4, 8 cells in a pattern that mirrors this geometry.

What makes the Seed of Life particularly beautiful when animated is its rotational symmetry. By slowly rotating each circle’s phase, or by pulsing the radius, you can create hypnotic animations that reveal the hidden vesica piscis shapes formed by each pair of overlapping circles.

Example 3: Animated Seed of Life

This example animates the Seed of Life with breathing radius, rotating phases, and color cycling. The vesica piscis intersections are filled with translucent color to reveal the sacred geometry within.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const cx = 300, cy = 300;
let t = 0;

function draw() {
  ctx.fillStyle = 'rgba(5,5,20,0.15)';
  ctx.fillRect(0, 0, 600, 600);
  const R = 80 + Math.sin(t * 0.5) * 10; // breathing
  const rotation = t * 0.1;

  // Draw 7 circles: center + 6 petals
  for (let i = 0; i < 7; i++) {
    const angle = i === 0 ? 0 : (Math.PI / 3) * (i - 1) + rotation;
    const dist = i === 0 ? 0 : R;
    const x = cx + dist * Math.cos(angle);
    const y = cy + dist * Math.sin(angle);
    const hue = (i * 51 + t * 20) % 360;

    // Fill vesica piscis regions
    if (i > 0) {
      ctx.beginPath();
      ctx.arc(cx, cy, R, angle - Math.PI / 3, angle + Math.PI / 3);
      ctx.arc(x, y, R, angle + Math.PI - Math.PI / 3, angle + Math.PI + Math.PI / 3);
      ctx.closePath();
      ctx.fillStyle = `hsla(${hue}, 80%, 50%, 0.05)`;
      ctx.fill();
    }

    // Circle outline
    ctx.beginPath();
    ctx.arc(x, y, R, 0, Math.PI * 2);
    ctx.strokeStyle = `hsla(${hue}, 70%, 65%, 0.6)`;
    ctx.lineWidth = 1.5;
    ctx.stroke();

    // Center dot
    ctx.beginPath();
    ctx.arc(x, y, 3, 0, Math.PI * 2);
    ctx.fillStyle = `hsl(${hue}, 80%, 75%)`;
    ctx.fill();
  }

  // Outer boundary circle
  ctx.beginPath();
  ctx.arc(cx, cy, R * 2.2, 0, Math.PI * 2);
  ctx.strokeStyle = 'rgba(255,255,255,0.08)';
  ctx.lineWidth = 1;
  ctx.stroke();

  t += 0.016;
  requestAnimationFrame(draw);
}
draw();

The Sri Yantra: nine triangles of creation

The Sri Yantra is one of the most complex and revered sacred geometry forms, originating in Hindu and Buddhist traditions. It consists of nine interlocking triangles — four pointing upward (representing Shiva/masculine) and five pointing downward (representing Shakti/feminine). These nine triangles intersect to form 43 smaller triangles, arranged in concentric layers radiating from a central point (the bindu).

Constructing a mathematically precise Sri Yantra is notoriously difficult. The intersections must be exact — all triple-point crossings must be concurrent, which creates a system of constraints that has no simple closed-form solution. Historically, most Sri Yantras were drawn by eye or with iterative compass-and-straightedge methods. We use a numerical approach: define the triangles by their vertex positions, then refine them iteratively until the intersection constraints are satisfied.

Example 4: Sri Yantra

This example draws a Sri Yantra with concentric triangle rings, surrounding lotus petals, and a pulsing bindu center point. The triangles are colored by direction (up vs down) and the construction animates from center outward.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const cx = 300, cy = 300;
let t = 0;

// Simplified Sri Yantra: concentric triangle pairs
// Heights for upward triangles (from center, as fraction of outer radius)
const upH = [0.12, 0.32, 0.54, 0.82];
// Heights for downward triangles
const downH = [0.06, 0.22, 0.44, 0.66, 0.90];
const outerR = 220;

function drawTriangle(yBase, yTip, isUp, hue, alpha) {
  const tipY = isUp ? cy - yTip * outerR : cy + yTip * outerR;
  const baseY = isUp ? cy + yBase * outerR : cy - yBase * outerR;
  // Width at base proportional to distance from center
  const halfW = Math.abs(baseY - cy) * 1.1 + 30;
  ctx.beginPath();
  ctx.moveTo(cx, tipY);
  ctx.lineTo(cx - halfW, baseY);
  ctx.lineTo(cx + halfW, baseY);
  ctx.closePath();
  ctx.strokeStyle = `hsla(${hue}, 75%, 60%, ${alpha})`;
  ctx.lineWidth = 1.5;
  ctx.stroke();
}

function drawLotus(petals, r, openness) {
  for (let i = 0; i < petals; i++) {
    const a = (Math.PI * 2 / petals) * i - Math.PI / 2;
    const x1 = cx + r * Math.cos(a - openness);
    const y1 = cy + r * Math.sin(a - openness);
    const x2 = cx + r * Math.cos(a + openness);
    const y2 = cy + r * Math.sin(a + openness);
    const tipX = cx + (r + 30) * Math.cos(a);
    const tipY = cy + (r + 30) * Math.sin(a);
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.quadraticCurveTo(tipX, tipY, x2, y2);
    ctx.strokeStyle = `hsla(40, 60%, 55%, 0.5)`;
    ctx.lineWidth = 1;
    ctx.stroke();
  }
}

function draw() {
  ctx.fillStyle = '#0a0a1a';
  ctx.fillRect(0, 0, 600, 600);

  // Outer square gate (bhupura)
  ctx.strokeStyle = 'rgba(200,170,100,0.3)';
  ctx.lineWidth = 2;
  ctx.strokeRect(cx - outerR - 40, cy - outerR - 40,
                 (outerR + 40) * 2, (outerR + 40) * 2);

  // Outer circle
  ctx.beginPath();
  ctx.arc(cx, cy, outerR + 10, 0, Math.PI * 2);
  ctx.strokeStyle = 'rgba(200,170,100,0.4)';
  ctx.stroke();

  // Lotus petals (two rings: 16 outer, 8 inner)
  const pulse = Math.sin(t * 0.5) * 0.02;
  drawLotus(16, outerR - 15, 0.12 + pulse);
  drawLotus(8, outerR - 50, 0.18 + pulse);

  // Inner circle
  ctx.beginPath();
  ctx.arc(cx, cy, outerR - 75, 0, Math.PI * 2);
  ctx.strokeStyle = 'rgba(200,170,100,0.3)';
  ctx.stroke();

  // Upward triangles (Shiva) - warm colors
  const revealT = Math.min(t * 0.3, 1);
  for (let i = 0; i < upH.length; i++) {
    const alpha = Math.max(0, Math.min(0.8, revealT * 4 - i));
    drawTriangle(upH[i], upH[i] + 0.4, true, 0 + i * 15, alpha);
  }

  // Downward triangles (Shakti) - cool colors
  for (let i = 0; i < downH.length; i++) {
    const alpha = Math.max(0, Math.min(0.8, revealT * 5 - i));
    drawTriangle(downH[i], downH[i] + 0.35, false, 200 + i * 15, alpha);
  }

  // Bindu (center point) - pulsing
  const binduR = 4 + Math.sin(t * 2) * 2;
  ctx.beginPath();
  ctx.arc(cx, cy, binduR, 0, Math.PI * 2);
  ctx.fillStyle = '#fff';
  ctx.fill();
  const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, binduR * 4);
  glow.addColorStop(0, 'rgba(255,220,150,0.4)');
  glow.addColorStop(1, 'rgba(255,220,150,0)');
  ctx.beginPath();
  ctx.arc(cx, cy, binduR * 4, 0, Math.PI * 2);
  ctx.fillStyle = glow;
  ctx.fill();

  t += 0.016;
  requestAnimationFrame(draw);
}
draw();

The golden spiral mandala: phi in circular form

Sacred geometry and the golden ratio (φ ≈ 1.618) are deeply intertwined. The golden spiral appears in nautilus shells, sunflower seed heads, hurricane formations, and spiral galaxies. A golden spiral mandala combines this spiral with radial symmetry to create a meditative, infinitely detailed pattern.

The construction uses logarithmic spirals with a growth factor of φ. By mirroring the spiral across multiple axes of symmetry, you create a mandala-like form where every arm follows the golden proportion. Adding concentric rings at golden ratio intervals (R, R×φ, R×φ², ...) reinforces the proportional harmony.

Example 5: Golden spiral mandala

This example draws a mandala made of mirrored golden spirals with 12-fold symmetry. The spiral arms rotate slowly and golden-ratio-spaced circles create a rhythmic layering.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const cx = 300, cy = 300;
const PHI = (1 + Math.sqrt(5)) / 2;
let t = 0;

function goldenSpiral(startR, maxR, rotation, hue) {
  ctx.beginPath();
  const b = Math.log(PHI) / (Math.PI / 2);
  for (let angle = 0; angle < Math.PI * 6; angle += 0.02) {
    const r = startR * Math.exp(b * angle);
    if (r > maxR) break;
    const x = cx + r * Math.cos(angle + rotation);
    const y = cy + r * Math.sin(angle + rotation);
    if (angle === 0) ctx.moveTo(x, y);
    else ctx.lineTo(x, y);
  }
  ctx.strokeStyle = `hsla(${hue}, 65%, 60%, 0.5)`;
  ctx.lineWidth = 1.2;
  ctx.stroke();
}

function draw() {
  ctx.fillStyle = 'rgba(5,5,20,0.08)';
  ctx.fillRect(0, 0, 600, 600);
  const symmetry = 12;

  // Draw mirrored golden spirals
  for (let i = 0; i < symmetry; i++) {
    const angle = (Math.PI * 2 / symmetry) * i + t * 0.1;
    const hue = (i * 30 + t * 10) % 360;
    goldenSpiral(3, 260, angle, hue);
    goldenSpiral(3, 260, -angle + Math.PI, (hue + 180) % 360);
  }

  // Golden ratio concentric circles
  let r = 15;
  for (let i = 0; i < 8; i++) {
    ctx.beginPath();
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
    ctx.strokeStyle = `rgba(255,215,100,${0.15 - i * 0.015})`;
    ctx.lineWidth = 1;
    ctx.stroke();
    r *= PHI;
    if (r > 300) break;
  }

  // Center dot
  ctx.beginPath();
  ctx.arc(cx, cy, 3, 0, Math.PI * 2);
  ctx.fillStyle = 'rgba(255,230,150,0.8)';
  ctx.fill();

  t += 0.016;
  requestAnimationFrame(draw);
}
draw();

Platonic solids: the five perfect forms

The five Platonic solids — tetrahedron (4 faces), cube (6), octahedron (8), dodecahedron (12), and icosahedron (20) — are the only convex polyhedra where every face is an identical regular polygon and every vertex has the same number of faces meeting. Plato associated each with a classical element: fire, earth, air, water, and the cosmos. Euclid devoted the final book of the Elements to proving that exactly five exist.

Projecting 3D Platonic solids onto a 2D canvas requires a simple perspective or orthographic projection. We rotate the vertices around the Y and X axes over time, then project them by dividing by a depth factor. The wireframe rendering reveals the beautiful symmetries of each solid.

Example 6: Rotating Platonic solids

This example displays all five Platonic solids rotating in a row, with wireframe rendering and depth-based opacity. Click to cycle through different rotation modes.

const canvas = document.createElement('canvas');
canvas.width = 800; canvas.height = 400;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
let t = 0, mode = 0;

// Vertex data for each solid (unit sphere)
const PHI = (1 + Math.sqrt(5)) / 2;
const solids = [
  { name: 'Tetrahedron', verts: [
    [1,1,1],[-1,-1,1],[-1,1,-1],[1,-1,-1]
  ].map(v => v.map(c => c / Math.sqrt(3))),
    edges: [[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]] },
  { name: 'Cube', verts: [
    [-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1],
    [-1,-1,1],[1,-1,1],[1,1,1],[-1,1,1]
  ].map(v => v.map(c => c / Math.sqrt(3))),
    edges: [[0,1],[1,2],[2,3],[3,0],[4,5],[5,6],[6,7],[7,4],
            [0,4],[1,5],[2,6],[3,7]] },
  { name: 'Octahedron', verts: [
    [1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]
  ], edges: [[0,2],[0,3],[0,4],[0,5],[1,2],[1,3],[1,4],[1,5],
             [2,4],[2,5],[3,4],[3,5]] },
  { name: 'Dodecahedron', verts: (() => {
    const p = PHI, q = 1/PHI;
    const v = [[1,1,1],[1,1,-1],[1,-1,1],[1,-1,-1],
      [-1,1,1],[-1,1,-1],[-1,-1,1],[-1,-1,-1],
      [0,q,p],[0,q,-p],[0,-q,p],[0,-q,-p],
      [q,p,0],[q,-p,0],[-q,p,0],[-q,-p,0],
      [p,0,q],[p,0,-q],[-p,0,q],[-p,0,-q]];
    const s = Math.sqrt(3);
    return v.map(c => c.map(x => x / s));
  })(), edges: [[0,8],[0,12],[0,16],[8,4],[8,10],[4,14],[4,18],
    [12,2],[12,14],[14,5],[2,16],[2,13],[16,17],[10,6],[10,2],
    [6,15],[6,18],[18,19],[13,3],[13,15],[3,17],[3,11],
    [17,1],[1,9],[1,12],[5,9],[5,19],[9,11],[11,7],[7,15],[7,19]] },
  { name: 'Icosahedron', verts: (() => {
    const p = PHI;
    const v = [[0,1,p],[0,1,-p],[0,-1,p],[0,-1,-p],
      [1,p,0],[1,-p,0],[-1,p,0],[-1,-p,0],
      [p,0,1],[p,0,-1],[-p,0,1],[-p,0,-1]];
    const s = Math.sqrt(1 + p * p);
    return v.map(c => c.map(x => x / s));
  })(), edges: [[0,2],[0,4],[0,6],[0,8],[0,10],[2,5],[2,7],[2,8],[2,10],
    [4,6],[4,8],[4,9],[6,10],[6,11],[8,9],[5,3],[5,8],[5,9],
    [7,3],[7,5],[7,10],[7,11],[3,9],[3,11],[9,1],[1,3],[1,4],
    [1,6],[1,11],[10,11]] }
];

function rotateY(v, a) {
  const c = Math.cos(a), s = Math.sin(a);
  return [v[0]*c + v[2]*s, v[1], -v[0]*s + v[2]*c];
}
function rotateX(v, a) {
  const c = Math.cos(a), s = Math.sin(a);
  return [v[0], v[1]*c - v[2]*s, v[1]*s + v[2]*c];
}
function project(v, ox) {
  const d = 3 + v[2];
  return [ox + v[0] * 120 / d, 200 + v[1] * 120 / d, d];
}

function draw() {
  ctx.fillStyle = '#0a0a1a';
  ctx.fillRect(0, 0, 800, 400);

  solids.forEach((solid, si) => {
    const ox = 80 + si * 160;
    const rotated = solid.verts.map(v => {
      let r = rotateY(v, t * (0.5 + si * 0.1));
      return rotateX(r, t * 0.3 + si * 0.2);
    });
    const projected = rotated.map(v => project(v, ox));

    // Draw edges
    solid.edges.forEach(([a, b]) => {
      const pa = projected[a], pb = projected[b];
      const avgD = (pa[2] + pb[2]) / 2;
      const alpha = Math.max(0.15, Math.min(0.9, (avgD - 1.5) * 0.4));
      ctx.beginPath();
      ctx.moveTo(pa[0], pa[1]);
      ctx.lineTo(pb[0], pb[1]);
      ctx.strokeStyle = `hsla(${si * 60 + 180}, 70%, 65%, ${alpha})`;
      ctx.lineWidth = 1.5;
      ctx.stroke();
    });

    // Draw vertices
    projected.forEach(p => {
      ctx.beginPath();
      ctx.arc(p[0], p[1], 2, 0, Math.PI * 2);
      ctx.fillStyle = `hsla(${si * 60 + 180}, 80%, 75%, 0.8)`;
      ctx.fill();
    });

    // Label
    ctx.fillStyle = 'rgba(255,255,255,0.5)';
    ctx.font = '11px monospace';
    ctx.textAlign = 'center';
    ctx.fillText(solid.name, ox, 380);
  });

  t += 0.016;
  requestAnimationFrame(draw);
}
canvas.onclick = () => mode = (mode + 1) % 3;
draw();

Sacred tiling: the geometry of mosques and cathedrals

Some of the most stunning examples of sacred geometry appear in architectural tiling. Islamic geometric patterns, Gothic rose windows, and Celtic knotwork all derive from the same fundamental constructions: circles, their intersections, and the polygons that arise from connecting those intersections.

The Islamic star pattern is a particularly elegant example. Start with a regular polygon grid (usually hexagonal or square). At each polygon vertex, draw lines at specific angles toward the interior. Where these lines cross, they form star shapes. The 8-pointed star (octagram) and 6-pointed star (hexagram) are the most common, but 10, 12, and even 16-pointed stars appear in complex patterns.

Example 7: Islamic star tiling

This example generates an Islamic star tiling pattern with 8-pointed stars. The pattern animates by slowly revealing the construction lines, then filling the stars with color. Mouse position controls the line angle parameter.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
let t = 0, mouseX = 300, mouseY = 300;

canvas.onmousemove = e => {
  const r = canvas.getBoundingClientRect();
  mouseX = e.clientX - r.left;
  mouseY = e.clientY - r.top;
};

function drawStar8(x, y, r, inset, hue, alpha) {
  const points = [];
  for (let i = 0; i < 16; i++) {
    const a = (Math.PI * 2 / 16) * i - Math.PI / 2;
    const radius = i % 2 === 0 ? r : r * inset;
    points.push([
      x + radius * Math.cos(a),
      y + radius * Math.sin(a)
    ]);
  }
  // Draw star outline
  ctx.beginPath();
  points.forEach(([px, py], i) => {
    if (i === 0) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  });
  ctx.closePath();
  ctx.strokeStyle = `hsla(${hue}, 60%, 55%, ${alpha})`;
  ctx.lineWidth = 1.2;
  ctx.stroke();
  ctx.fillStyle = `hsla(${hue}, 50%, 40%, ${alpha * 0.15})`;
  ctx.fill();

  // Interlace lines
  for (let i = 0; i < 8; i++) {
    const a = (Math.PI / 4) * i;
    ctx.beginPath();
    ctx.moveTo(x + r * 0.3 * Math.cos(a), y + r * 0.3 * Math.sin(a));
    ctx.lineTo(x + r * Math.cos(a), y + r * Math.sin(a));
    ctx.strokeStyle = `hsla(${hue + 30}, 50%, 50%, ${alpha * 0.4})`;
    ctx.lineWidth = 0.8;
    ctx.stroke();
  }
}

function draw() {
  ctx.fillStyle = '#0a0a1a';
  ctx.fillRect(0, 0, 600, 600);
  const spacing = 70;
  const inset = 0.35 + (mouseX / 600) * 0.3;
  const size = 30 + (mouseY / 600) * 15;

  for (let row = -1; row < 10; row++) {
    for (let col = -1; col < 10; col++) {
      const x = col * spacing + (row % 2) * (spacing / 2);
      const y = row * spacing;
      const dist = Math.hypot(x - mouseX, y - mouseY);
      const wave = Math.sin(dist * 0.02 - t * 2) * 0.5 + 0.5;
      const hue = (dist * 0.3 + t * 20) % 360;
      const alpha = 0.3 + wave * 0.5;
      drawStar8(x, y, size, inset, hue, alpha);
    }
  }

  // Construction circles (faint)
  for (let row = 0; row < 9; row++) {
    for (let col = 0; col < 9; col++) {
      const x = col * spacing + (row % 2) * (spacing / 2);
      const y = row * spacing;
      ctx.beginPath();
      ctx.arc(x, y, spacing * 0.48, 0, Math.PI * 2);
      ctx.strokeStyle = 'rgba(255,255,255,0.03)';
      ctx.lineWidth = 0.5;
      ctx.stroke();
    }
  }

  t += 0.016;
  requestAnimationFrame(draw);
}
draw();

Generative sacred art: combining forms

The most powerful creative coding pieces combine multiple sacred geometry elements into a single composition. Imagine a Flower of Life at the center, surrounded by rotating Platonic solid wireframes, with golden spiral arms radiating outward and Islamic star tiles fading into the background. Each element reinforces the mathematical harmony of the others.

The key to combining sacred forms is shared proportion. Use the golden ratio to determine relative sizes: if the inner circle has radius R, the next ring is R × φ, the next R × φ², and so on. Use 6-fold or 12-fold symmetry to ensure rotational harmony. Let the animation rates relate by simple ratios (1:2, 2:3, 3:5 — the Fibonacci sequence appears again).

Example 8: Generative sacred geometry composition

This is the capstone example: an animated composition combining the Flower of Life core, golden spiral arms, rotating concentric geometry, and particle effects. The entire piece breathes and rotates as a unified meditative artwork.

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const cx = 300, cy = 300;
const PHI = (1 + Math.sqrt(5)) / 2;
let t = 0;

// Flower of Life core
function drawFlower(r, alpha) {
  for (let ring = 0; ring <= 2; ring++) {
    if (ring === 0) {
      ctx.beginPath();
      ctx.arc(cx, cy, r, 0, Math.PI * 2);
      ctx.strokeStyle = `rgba(200,180,255,${alpha})`;
      ctx.lineWidth = 1;
      ctx.stroke();
    } else {
      for (let i = 0; i < 6 * ring; i++) {
        const sector = Math.floor(i / ring);
        const pos = i % ring;
        const a0 = (Math.PI / 3) * sector - Math.PI / 6;
        const a1 = (Math.PI / 3) * ((sector + 1) % 6) - Math.PI / 6;
        const frac = pos / ring;
        const x = cx + r * (ring * Math.cos(a0) * (1 - frac) + ring * Math.cos(a1) * frac);
        const y = cy + r * (ring * Math.sin(a0) * (1 - frac) + ring * Math.sin(a1) * frac);
        ctx.beginPath();
        ctx.arc(x, y, r, 0, Math.PI * 2);
        ctx.strokeStyle = `hsla(${(i * 20 + t * 15) % 360}, 60%, 60%, ${alpha * 0.6})`;
        ctx.lineWidth = 0.8;
        ctx.stroke();
      }
    }
  }
}

// Golden spiral arms
function drawSpirals(arms, maxR, rotation) {
  const b = Math.log(PHI) / (Math.PI / 2);
  for (let a = 0; a < arms; a++) {
    const offset = (Math.PI * 2 / arms) * a + rotation;
    ctx.beginPath();
    for (let angle = 0; angle < Math.PI * 5; angle += 0.03) {
      const r = 5 * Math.exp(b * angle);
      if (r > maxR) break;
      const x = cx + r * Math.cos(angle + offset);
      const y = cy + r * Math.sin(angle + offset);
      if (angle === 0) ctx.moveTo(x, y);
      else ctx.lineTo(x, y);
    }
    ctx.strokeStyle = `hsla(${a * (360 / arms) + t * 8}, 55%, 55%, 0.3)`;
    ctx.lineWidth = 1;
    ctx.stroke();
  }
}

// Rotating polygon ring
function drawPolyRing(r, sides, rotation, hue) {
  ctx.beginPath();
  for (let i = 0; i <= sides; i++) {
    const a = (Math.PI * 2 / sides) * i + rotation;
    const x = cx + r * Math.cos(a);
    const y = cy + r * Math.sin(a);
    if (i === 0) ctx.moveTo(x, y);
    else ctx.lineTo(x, y);
  }
  ctx.strokeStyle = `hsla(${hue}, 50%, 55%, 0.25)`;
  ctx.lineWidth = 1;
  ctx.stroke();
}

// Particles along sacred paths
const particles = Array.from({length: 80}, (_, i) => ({
  angle: Math.random() * Math.PI * 10,
  arm: Math.floor(Math.random() * 6),
  speed: 0.3 + Math.random() * 0.5,
  hue: Math.random() * 360,
}));

function drawParticles() {
  const b = Math.log(PHI) / (Math.PI / 2);
  particles.forEach(p => {
    const offset = (Math.PI / 3) * p.arm + t * 0.1;
    const r = 5 * Math.exp(b * p.angle);
    if (r > 280) p.angle = 0;
    const x = cx + r * Math.cos(p.angle + offset);
    const y = cy + r * Math.sin(p.angle + offset);
    ctx.beginPath();
    ctx.arc(x, y, 1.5, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(${p.hue}, 80%, 70%, ${Math.max(0, 1 - r / 280)})`;
    ctx.fill();
    p.angle += p.speed * 0.01;
  });
}

function draw() {
  ctx.fillStyle = 'rgba(5,5,20,0.12)';
  ctx.fillRect(0, 0, 600, 600);

  // Background: golden ratio concentric circles
  let gr = 10;
  for (let i = 0; i < 10; i++) {
    ctx.beginPath();
    ctx.arc(cx, cy, gr, 0, Math.PI * 2);
    ctx.strokeStyle = 'rgba(255,215,100,0.06)';
    ctx.lineWidth = 0.5;
    ctx.stroke();
    gr *= PHI;
    if (gr > 300) break;
  }

  // Rotating polygon rings
  drawPolyRing(100, 3, t * 0.2, 0);
  drawPolyRing(120, 6, -t * 0.15, 60);
  drawPolyRing(155, 4, t * 0.12, 120);
  drawPolyRing(190, 12, -t * 0.08, 240);

  // Golden spiral arms
  drawSpirals(6, 270, t * 0.05);

  // Flower of Life core
  drawFlower(40 + Math.sin(t * 0.3) * 3, 0.5);

  // Particles
  drawParticles();

  // Center bindu with glow
  const br = 5 + Math.sin(t) * 1.5;
  const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, br * 6);
  glow.addColorStop(0, 'rgba(255,230,180,0.5)');
  glow.addColorStop(1, 'rgba(255,230,180,0)');
  ctx.beginPath();
  ctx.arc(cx, cy, br * 6, 0, Math.PI * 2);
  ctx.fillStyle = glow;
  ctx.fill();
  ctx.beginPath();
  ctx.arc(cx, cy, br, 0, Math.PI * 2);
  ctx.fillStyle = '#fff';
  ctx.fill();

  t += 0.016;
  requestAnimationFrame(draw);
}
draw();

The mathematics behind the mysticism

Sacred geometry bridges two worlds: the mathematical and the spiritual. While the spiritual interpretations vary across cultures, the mathematics is universal. Here are the key mathematical concepts that underpin every pattern in this guide:

  • The golden ratio (φ) — (1 + √5) / 2 ≈ 1.618. Appears in the Fibonacci sequence, phyllotaxis, Platonic solid vertex coordinates, and the relationship between pentagons and pentagrams. It is the most irrational number (hardest to approximate with fractions), which is why phyllotaxis uses it to pack seeds most efficiently.
  • Rotational symmetry — The Flower of Life has 6-fold symmetry (hexagonal). The Sri Yantra has 9-fold (and 18-fold reflection) symmetry. Regular polygons tile the plane in exactly three ways: triangles, squares, hexagons. These are the only wallpaper groups possible.
  • Circle packing — The Flower of Life is a specific circle packing where every circle passes through the centers of its neighbors. This is the densest 2D packing arrangement, proved optimal by Thue in 1892.
  • Duality — Every Platonic solid has a dual (swap faces and vertices): cube ↔ octahedron, dodecahedron ↔ icosahedron, tetrahedron ↔ itself. This duality is a deep symmetry of three-dimensional space.
  • Self-similarity — Many sacred geometry patterns are fractal: the whole contains smaller copies of itself. The Flower of Life can be extended infinitely. The golden spiral repeats at every scale. This self-similarity is what gives these patterns their sense of “infinite depth.”

Sacred geometry in creative coding: where to go next

The examples in this guide are starting points. Here are some directions to take them further:

  • 3D sacred geometry — Render the Platonic solids with faces, transparency, and lighting. Nest them inside each other (icosahedron inside dodecahedron). Use WebGL for smooth 60fps rendering.
  • Interactive meditation tools — Add breathing guides (expand/contract cycles timed to 4-7-8 breathing), ambient sound generation, and fullscreen mode. Sacred geometry is used in meditation practices — code can make it interactive.
  • Generative mandalas — Combine randomization with sacred proportions to generate unique mandalas. Each click creates a new one, but every mandala follows golden ratio proportions and rotational symmetry.
  • Physical output — Export SVG for pen plotting, laser cutting, or CNC routing. Sacred geometry patterns make stunning physical art when cut from wood, acrylic, or metal.

Sacred geometry reminds us that the most profound beauty often comes from the simplest rules. A circle, a ratio, a rotation — and suddenly you have a pattern that humans have found meaningful for five thousand years. Code lets you explore these patterns at a speed and scale that would have astonished the ancient geometers who first drew them in sand.

Explore more mathematical art patterns on Lumitree, where every branch is a unique micro-world built from code. Try our geometric art guide for more pattern techniques, the Fibonacci spiral guide for golden ratio deep dives, or the math art guide for more mathematical pattern creation.

Related articles