JSFiddle - React, Tailwind, and code Playground

HTML

<body>

<canvas id='canvas' width='320' height='300'></canvas>

   
</body>

CSS

canvas { background:gray }
div { width:100px; height:100px; margin-top:100px; border:1px solid black }

JavaScript

function draw_points( ctx, points ) {

   // Do nothing if no points
   if ( points.length < 1 )
       return;
         
       // Draw single point
   if ( points.length <= 2 ) {
        var b = points[0];
        ctx.beginPath();
        ctx.arc( b.x, b.y, ctx.lineWidth / 2, 0, Math.PI * 2, true );
        ctx.closePath();
        ctx.fill();
        return;
   }
    
    function average(p1, p2) {
        return {x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2};
    }
    function vectorwise(func, args) {
        return {
            x: func.apply(this, args.map(function(p){return p.x})),
            y: func.apply(this, args.map(function(p){return p.y}))
        };
    }
    
    function hop(hopper, fencepost, fac) {
        return vectorwise(function(p1,p2){return p1-fac*(p2-p1)}, [fencepost, hopper]);
    }
        
    var ms=1; // milliseconds
    var sec=1000; // 1000 milliseconds
    var animationDuration = 2*sec;
    var timePerPoint = animationDuration/points.length;
                          
    function drawSplineSegment(prevPoint, startPoint, endPoint, nextPoint) {
        var controlPoint = average(hop(nextPoint, endPoint, 0.1), hop(prevPoint, startPoint, 0.1));
        ctx.beginPath();
        ctx.moveTo(startPoint.x, startPoint.y);
        ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
        ctx.stroke();
        ctx.closePath();
    }
                                      
    var extendedPoints = [].concat(
        [hop(points[1], points[0], 1)], 
        points, 
        [hop(points[points.length-2], points[points.length-1], 1)]
    );
    
    var startTime = (new Date()).getTime();
    function scheduledTime(i) {
        return startTime + timePerPoint*(i+1);
    }
    function drawSplineSegmentAndWait(i) {
        if (i+4 < extendedPoints.length) {
            drawSplineSegment.apply(this, extendedPoints.slice(i, i+4));
            var now = (new Date()).getTime();
            var timeLeft = scheduledTime(i+1) - now;
  ...