JSFiddle - React, Tailwind, and code Playground

HTML

<svg width="100%" height="100%" viewBox="0 0 800 600"></svg>
<p></p>

JavaScript

const svgNs = 'http://www.w3.org/2000/svg';
const p1inc = 2 * Math.PI / 57;
const p2inc = 2 * Math.PI / 74;
function radius(i, p1, p2) {
    return 4 + 2 * Math.sin(i * 2 * Math.PI / (27 + 3 * Math.cos(p1))) + 2 * Math.cos(i * 2 * Math.PI / (11 + 5 * Math.sin(p2)));
}

var container = document.querySelector('svg');
for (var i = 0; i < 16; ++i)
    container = container.appendChild(document.createElementNS(svgNs, 'g'));

for (var i = 0; i < 80*60; ++i) {
    var x = (i / 80) | 0, y = (i % 80) | 0;
    var circle = document.createElementNS(svgNs, 'circle');
    circle.setAttribute('cx', x * 10 + 5);
    circle.setAttribute('cy', y * 10 + 5);
    circle.setAttribute('r', radius(i, 0, 0));
    container.appendChild(circle);
}

var circles = document.getElementsByTagName('circle');
var phase1 = 0;
var phase2 = 0;
var samples = new Array();

var prevT = performance.now();
requestAnimationFrame(animate);

function animate() {
    phase1 += p1inc;
    phase2 += p2inc;
    for (var i = 0, n = circles.length; i < n; ++i)
        circles[i].setAttribute('r', radius(i, phase1, phase2));

    var t = performance.now();
    var elapsed = t - prevT;
    if (samples.push(elapsed) > 100)
        samples.shift();
    var avg = samples.reduce(function(a, b) { return a + b; }) / samples.length;
    document.querySelector('p').textContent = elapsed + " (" + samples.length + " sample avg: " + avg + ")";
    prevT = t;
    requestAnimationFrame(animate);
}