Some days ago I recreated an animated gif done by Charlie Deck that I happened to see on Twitter. By the way he has an amazing tumblr with lots of pattern and geometrical animations, check it out!

I call the result: "Triangle with a twist":

In this post I like to share the math behind it and how it works. All math is based on the fact that the triangle is equilateral, meaning all three sides have the same length.

I decided to put the two lower vertices in the lower corners of the canvas and the top vertex in the middle of the top edge of the canvas. My first question was:

Given a width what should the height be?

That can be solved with the Pythagorean theorem: Let the side length be b and the height of the triangle be h.

  h^2 + (0.5b)^2 = b^2


  h = sqrt(0.75)b


Ok good to have that sorted out. My second question was:

What radius should the circle have?

From each vertex draw a line that meets the opposite side in a straight angle. The three lines will meet at the center of the triangle. The distance from this point to where a line meets a side is the radius of the circle. Call that distance r. And let the length of the rest of the line be s.

  s + r = h

We already calculated the height of the triangle to be

  h = sqrt(0.75)b.

That gives us

  s + r = sqrt(0.75)

s = sqrt(0.75)b - r

And then use the Pythagorean theorem again on one of the small triangles.

  (0.5b)^2 + r^2 = s^2

Substitute s

  (0.5b)^2 + r^2 = (sqrt(0.75)b - r)^2

r = 0.25b/sqrt(0.75)

Based on the side length b we now know the radius of the circle r.


And after doing all the calculations I found everything I wanted to know to be already calculated at:

Oh well... 🤷 ¯\(ツ)


To achieve the white trails left behind, looking like motion blur, I use an old trick of clearing each animated frame with black with low opacity (alpha). Because it has low opacity it does not clear the screen completely and leaves a trail behind.

  ctx.fillStyle = "rgba(0, 0, 0, 0.04)";

Here's a little experiment for you: fork the Pen and change the last parameter from 0.04 into 1. See how boring the animation becomes!

At first I had problems with moiré pattern forming but then I got the excellent suggestion from Anders Tornblad on Twitter to increase the line width. It solved the problem. Your second experiment: set the lineWidth to 1 instead of 3 and see what happens.

Where should the lines end? - The Circle

So now we know where the three lines start (at the vertices of the triangle). Where should the lines end? -Well, some where along the edge of that circle, constantly moving. We can use trigonometric functions and think about the unit circle. By constantly increasing the angle we can use sine and cosine to calculate the x and y coordinates of a point moving along the circle. The values we get out of that calculation will be between -1 and 1 so we need to multiply with the radius. I've introduced the variable angle. It gets it's value increased slightly each time a frame is rendered. To get the x and y coordinates for a point traveling a long a circle path with radius r we can use:

  var x = Math.cos(angle) * r;
var y = Math.sin(angle) * r;

999 5 24