JSFiddle - React, Tailwind, and code Playground

HTML

<canvas id="c" width="220" height="250"></canvas><input type="button" value="Start" id="toggle"/>
<br/>
Z: 
<input type="value" id="minZ" class="val" value="50"/>
<input type="range" min="0" max="100" step="0.001" value="50" id="percentZ"/>
<input type="value" id="maxZ" class="val" value="300"/>
:
<input type="value" id="valZ" class="val" value=""/>
<br/>
b: 
<input type="value" id="minb" class="val" value="2"/>
<input type="range" min="0" max="100" step="0.001" value="50" id="percentb"/>
<input type="value" id="maxb" class="val" value=".5"/>
:
<input type="value" id="valb" class="val" value=""/>
<br/>
e: 
<input type="value" id="mine" class="val" value="3"/>
<input type="range" min="0" max="100" step="0.001" value="50" id="percente"/>
<input type="value" id="maxe" class="val" value="1.01"/>
:
<input type="value" id="vale" class="val" value=""/>
<br/>
Scale: <span id="scale"></span>

CSS

#c {
    border: 1px solid #000;
}
.val {
    width: 30px;
}

JavaScript

var c = document.getElementById('c');
var context = c.getContext("2d");
var centerx = context.canvas.width / 2;
var centery = context.canvas.height / 2;

// shim layer with setTimeout fallback
window.requestAnimFrame = (function() {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
    function(callback) {
        throw new Error("WTF? why no annimation?");
    };
})();


// usage: 
// instead of setInterval(render, 16) ....
running = false;
$('#toggle').click(function() {
    i = 500;
    running = !running;
    $('#toggle').val(running);
    animloop();
});
i = 500;

function animloop() {
    if (!running) return;
    requestAnimFrame(animloop);
    advance();
    if (i-- < 0) running = false;
};
// place the rAF *before* the render() to assure as close to 
// 60fps with the setTimeout fallback.

function advance() {
    Z = incrementVal("Z", 0.01);
    b = incrementVal("b", 0.01);
    e = incrementVal("e", 0.01);

    renderSpiral({
        a: 1,
        //1,
        b: b,
        e: e,
        //1.5,
        Z: Z,
        tail: 10
    });
}

function incrementVal(valName, step) {
    percentVal = parseFloat($('#percent' + valName).val());
    diff = parseFloat($('#max' + valName).val()) - parseFloat($('#min' + valName).val());
    val = parseFloat($('#min' + valName).val()) + diff * percentVal / 100;
    $('#percent' + valName).val(percentVal + step);
    $('#val' + valName).val(val);
    return val;
}

function renderSpiral(params) {
    a = params.a;
    b = params.b;
    e = params.e;
    Z = params.Z;
    tail = params.tail;

    context.clearRect(0, 0, 400, 400);

    targetX = a * Math.pow(e, b * Z * .1) * Math.cos(Z * .1);
    targetY = a * Math.pow(e, b * Z * .1) * Math.sin(Z * .1);
    theta = Math.atan(targetX / targetY);

    y = targetX * Math.sin(theta) + targetY * Math.cos(theta);
    if (y < 0) theta = theta +...