I saw some video on one of the socials about this so thought i would give it a go.
The Sierpinski Triangle is named after Wacław Franciszek Sierpiński (1882 – 1969) and consists of fractal equilateral triangles.

Essentially how it works is that you select a point anywhere in a triangle. Draw a line from that point to any of the corners. Then mark the middle of that line with a dot. From that dot go to any corner again, draw a line and mark the middle with a dot. Keep repeating.
To picture this look at the diagram, starting and point 1. This could be anywhere in the triangle and draw a line to any corner. 2 is the middle point where that then goes to any corner. The middle point of that is number 3.
Continue to repeat this and see the magic:

A simple html canvas will be created and some typescript for the logic.
The main page is a simple html page with a canvas on it
index.html
1. Create the HTML canvas
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TypeScript App</title>
<script defer src="index.js"></script>
</head>
<body>
<canvas id="canvas" width="1000" height="1000"></canvas>
</body>
</html>
2. Draw an equilateral triangle on to the canvas.
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
type Point = { x: number; y: number };
type Triangle = [Point, Point, Point];
const sideLength = 1000;
const height = (Math.sqrt(3) / 2) * sideLength;
const centerX = 500;
const centerY = 650;
const debug = false;
const triangle: Triangle = [
{x: centerX - sideLength / 2, y: centerY + height / 3}, //bottom left
{x: centerX + sideLength / 2, y: centerY + height / 3}, //bottom right
{x: centerX, y: centerY - (2 * height) / 3} // center top
];
// Function to draw the triangle
function drawTriangle() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(triangle[0].x, triangle[0].y);
ctx.lineTo(triangle[1].x, triangle[1].y);
ctx.lineTo(triangle[2].x, triangle[2].y);
ctx.closePath();
ctx.fillStyle = "lightblue";
ctx.fill();
ctx.stroke();
}
drawTriangle();
This will draw a simple equilateral triangle
Note the triangle array index:
0 – bottom left
1 – bottom right
2 – top center

Notice the centerX and centerY are used as offsets to position the triangle.
3. Start and mark the points
We start with setting a random point within the triangle 250,250
The choose a corner at random which is any of the triangle array elements. Calculate the length to that corner and get the middle coordinate.
Draw a dot at the middle point.
let randomPoint = {x: 250, y: 250};
function drawSierpinskiTriangle(x: number, y: number) {
randomPoint = getMidpointToRandomCorner(x, y);
drawDot(randomPoint.x, randomPoint.y);
}
function getMidpointToRandomCorner(px: number, py: number) {
const vertex = triangle[getRandomNumber(3) - 1];
return getMidpoint(px, py, vertex.x, vertex.y);
}
function getRandomNumber(x: number): number {
return Math.floor(Math.random() * x) + 1;
}
function getMidpoint(x1: number, y1: number, x2: number, y2: number) {
return {x: (x1 + x2) / 2, y: (y1 + y2) / 2};
}
function drawDot(x: number, y: number) {
ctx.beginPath();
ctx.arc(x, y, 1, 0, Math.PI * 2);
ctx.fillStyle = "black";
ctx.fill();
}
drawSierpinskiTriangle(randomPoint.x, randomPoint.y)
Use this middle point and loop around
for (let i = 0; i < 25000; i++) {
setTimeout( () => drawSierpinskiTriangle(randomPoint.x, randomPoint.y), 500);
}
add a timeout so that you can see the the pattern emerging.
Demo
See this in action and test out some iterations and see how many times it loops before the pattern emerges.