Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save gkucmierz/a20230f2139ef4083b046480eb376cfb to your computer and use it in GitHub Desktop.

Select an option

Save gkucmierz/a20230f2139ef4083b046480eb376cfb to your computer and use it in GitHub Desktop.
Run this code instantly in your browser: https://instacode.app/gist/a20230f2139ef4083b046480eb376cfb
import { getContext, onResize, onPointerDown } from 'canvas';
// --- CONFIGURATION CONSTANTS ---
const MAX_POINTS = 50000; // Total number of fractal points to generate
const STAGE_1_POINTS = 15; // Number of points in slow educational phase
const STAGE_1_DELAY_MS = 400; // Delay between steps in slow phase (milliseconds)
const STAGE_2_POINTS = 100; // Number of points in normal phase (1 point/frame)
const STAGE_3_POINTS = 500; // Number of points in medium phase (10 points/frame)
const STAGE_4_SPEED = 250; // Number of points generated per frame in fast phase
// Styling and Colors
const COLOR_BACKGROUND = '#0f172a'; // Deep slate background
const COLOR_FRACTAL = 'rgba(56, 189, 248, 0.55)'; // Sky blue for accumulated points
const COLOR_VERTEX = 'rgba(244, 63, 94, 0.85)'; // Rose glow for main wierzcholki
const COLOR_GUIDE = 'rgba(245, 158, 11, 0.35)'; // Amber for step line guides
const COLOR_ACTIVE = '#fbbf24'; // Bright amber for active moving point
// -------------------------------
const ctx = getContext('2d');
let width = 400;
let height = 400;
let vertices = [];
let px = 0;
let py = 0;
let chosenVertex = null;
let lastPx = 0;
let lastPy = 0;
// Typed arrays to store generated points (highly performant)
const pointsX = new Float32Array(MAX_POINTS);
const pointsY = new Float32Array(MAX_POINTS);
let pointCount = 0;
let lastStepTime = 0;
let isFinished = false;
// Initialize triangle vertices and starting point
function initSimulation() {
const size = Math.min(width, height) * 0.82;
const h = size * Math.sin(Math.PI / 3); // Equilateral triangle height
const centerX = width / 2;
const centerY = height / 2 + 25;
vertices = [
{ name: 'A', x: centerX, y: centerY - h / 2 },
{ name: 'B', x: centerX - size / 2, y: centerY + h / 2 },
{ name: 'C', x: centerX + size / 2, y: centerY + h / 2 }
];
// Start with a random point in the center area
px = centerX + (Math.random() - 0.5) * 50;
py = centerY + (Math.random() - 0.5) * 50;
pointCount = 0;
isFinished = false;
chosenVertex = null;
}
// Reset and restart simulation on user click
onPointerDown(() => {
initSimulation();
});
// Re-initialize layout on canvas resize
onResize((w, h) => {
width = w;
height = h;
initSimulation();
});
// Single step of the chaos game
function step() {
if (pointCount >= MAX_POINTS) {
isFinished = true;
return;
}
// 1. Pick a random vertex (A, B, or C)
const randIdx = Math.floor(Math.random() * 3);
chosenVertex = vertices[randIdx];
// 2. Store the current position as the last position
lastPx = px;
lastPy = py;
// 3. Move the point halfway towards the chosen vertex
px = (px + chosenVertex.x) / 2;
py = (py + chosenVertex.y) / 2;
// 4. Save the new point coordinates
pointsX[pointCount] = px;
pointsY[pointCount] = py;
pointCount++;
}
// Animation loop
function animate(time) {
if (!isFinished) {
if (pointCount < STAGE_1_POINTS) {
// Stage 1: Slow educational step-by-step
if (time - lastStepTime > STAGE_1_DELAY_MS) {
step();
lastStepTime = time;
}
} else if (pointCount < STAGE_2_POINTS) {
// Stage 2: Steady speed (1 point per frame)
step();
} else if (pointCount < STAGE_3_POINTS) {
// Stage 3: Faster (10 points per frame)
for (let i = 0; i < 10; i++) step();
} else {
// Stage 4: High-speed generation
for (let i = 0; i < STAGE_4_SPEED; i++) step();
}
}
draw();
requestAnimationFrame(animate);
}
// Draw frame
function draw() {
// Clear background
ctx.fillStyle = COLOR_BACKGROUND;
ctx.fillRect(0, 0, width, height);
// 1. Draw all accumulated points
ctx.fillStyle = COLOR_FRACTAL;
for (let i = 0; i < pointCount; i++) {
ctx.fillRect(pointsX[i] - 0.5, pointsY[i] - 0.5, 1, 1);
}
// 2. Draw the three main vertices with glow
vertices.forEach(v => {
// Outer neon glow
const grad = ctx.createRadialGradient(v.x, v.y, 2, v.x, v.y, 14);
grad.addColorStop(0, COLOR_VERTEX);
grad.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = grad;
ctx.beginPath();
ctx.arc(v.x, v.y, 14, 0, Math.PI * 2);
ctx.fill();
// Solid core point
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(v.x, v.y, 4, 0, Math.PI * 2);
ctx.fill();
// Vertex name tag
ctx.fillStyle = '#f1f5f9';
ctx.font = 'bold 13px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillText(v.name, v.x, v.y - 16);
});
// 3. Draw educational guides (only during slow startup)
if (pointCount < STAGE_2_POINTS && chosenVertex) {
// Dotted line connecting current point to chosen vertex
ctx.strokeStyle = COLOR_GUIDE;
ctx.lineWidth = 1.5;
ctx.setLineDash([4, 4]);
ctx.beginPath();
ctx.moveTo(lastPx, lastPy);
ctx.lineTo(chosenVertex.x, chosenVertex.y);
ctx.stroke();
ctx.setLineDash([]); // Reset line dash
// Highlight current active point with a glowing halo
ctx.fillStyle = COLOR_ACTIVE;
ctx.shadowColor = COLOR_ACTIVE;
ctx.shadowBlur = 8;
ctx.beginPath();
ctx.arc(px, py, 4, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0; // Reset shadow
}
// 4. UI texts and labels
ctx.fillStyle = '#f8fafc';
ctx.font = 'bold 18px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText("The Chaos Game: Sierpinski Triangle", width / 2, 20);
ctx.fillStyle = '#94a3b8';
ctx.font = '13px monospace';
ctx.fillText(`Points generated: ${pointCount.toLocaleString()}`, width / 2, 50);
if (pointCount < STAGE_2_POINTS) {
ctx.fillStyle = '#f59e0b';
ctx.font = 'italic 12px sans-serif';
ctx.fillText("Rule: Move 1/2 of the way to a random vertex", width / 2, 74);
} else if (isFinished) {
ctx.fillStyle = '#10b981'; // Emerald 500
ctx.font = 'bold 13px sans-serif';
ctx.fillText("Simulation complete! Click canvas to restart.", width / 2, 74);
} else {
ctx.fillStyle = '#38bdf8'; // Sky 400
ctx.font = '12px sans-serif';
ctx.fillText("Generating fractal at high speed...", width / 2, 74);
}
}
// Start simulation
initSimulation();
requestAnimationFrame(animate);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment