JSFiddle - React, Tailwind, and code Playground

HTML

<canvas id="canvas"></canvas>

CSS

body {
  background-color: #222;
}

#canvas {
  border: 1px solid #eee;
  }
}

Babel + JSX

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');


function init() {
	canvas.width = 400;
  canvas.height = 300;
}

function deg2rad(deg) {
	return deg * 2 * Math.PI / 360;
}

/** Converts polar coordinates to cartesian. */
function poltocar(r, theta) {
	return {
  	x: r * Math.cos(theta),
    y: r * Math.sin(theta),
  };
}

/** Returns the radius of an n-sided polygon for a given theta. */
function polygon(n, theta) {
  let radPerSide = deg2rad(360 / n);
  let denom = Math.sin(theta - radPerSide * (-1 + Math.floor(theta / radPerSide)));
  let r = (Math.sqrt(3) / 2) * (1 / denom);
  return r;
}

function naiveSawtooth(theta) {
  return theta % (Math.PI * 2);
}

function step() {
  let cx = canvas.width / 2;
  let cy = canvas.height / 2;

  ctx.fillStyle = 'rgba(22, 22, 22, 0.1)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
	ctx.beginPath();

	for (let i = 0; i <= 360; i++) {
  	let theta = deg2rad(i);
    let r = polygon(6, theta) + 0.01 * naiveSawtooth(theta * 40);
    let p = poltocar(r, theta - deg2rad(Date.now() * 0.08));
    
    if (i === 0) {
        ctx.moveTo(cx + p.x * 80, cy + p.y * 80);
    } else {
        ctx.lineTo(cx + p.x * 80, cy + p.y * 80);
    }
  }

	ctx.strokeStyle = 'rgba(187, 255, 252, 0.1)';
	ctx.stroke();
	window.requestAnimationFrame(step);
}

init();
window.requestAnimationFrame(step);