JSFiddle - React, Tailwind, and code Playground

by soulwire

HTML

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

CSS

html, body {
  background: #222;
  margin: 0;
}

JavaScript

const c = b.getContext('2d');

const camera = {
	x: 0,
  y: 0,
  z: 1,
  s: 0.4
}

let vines = [{
	x: 0,
  y: 0,
  a: 0,
  ai: 0,
  w: 4,
  l: 200,
  p: []
}];

let frame = 0;

const tick = () => {
	requestAnimationFrame(tick);
  b.width = innerWidth;
  b.height = innerHeight;
  camera.x += (vines[0].x - camera.x) * camera.s;
  camera.y += (vines[0].y - camera.y) * camera.s;
  camera.z = 0.5 + (Math.sin(frame * 0.01) / 2 + 1) * 1.5;
  c.translate(innerWidth / 2, innerHeight / 2);
  c.scale(camera.z, camera.z);
  c.translate(-camera.x, -camera.y);
  vines = vines.filter((v, i) => {
  	if (i === 0) return true;
    return --v.l > 0;
  });
  vines.forEach(v => {
	  // update
  	const dx = Math.cos(v.a) * v.w / 2;
    const dy = Math.sin(v.a) * v.w / 2;
    v.p.push({ x: v.x, y: v.y });
    v.a += v.ai / v.w / 2;
    v.x += dx;
    v.y += dy;
    //if (Math.random() < 0.1) {
    if (frame % 30 === 0) {
      v.ai = Math.random() - .5;
    }
    if (v.p.length > 200) {
    	v.p.shift();
    }
    if (
    	vines.length < 100 &&
      v.l > 50 &&
      Math.random() < 0.02
     ) {
    	vines.push({
      	x: v.x,
        y: v.y,
        a: v.a,
        ai: v.ai,
        w: v.w / 2,
        p: [],
        l: v.l
      })
    }
    // render
    c.beginPath();
    c.strokeStyle = '#fff';
    c.moveTo(v.x, v.y);
    let l = v.p.length - 1;
    for (let p, i = l; p = v.p[i]; i--) {
    	c.lineTo(p.x, p.y);
    }
    c.stroke();
  });
  frame++;
}

tick();