JSFiddle - React, Tailwind, and code Playground

HTML

<canvas width="800" height="500"></canvas>

CSS

*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: #121212;
  display: grid;
  place-items: center;
  min-height: 100vh;
}

canvas {
  border: 1px solid #7dff19;
  border-radius: 5px;
}

JavaScript

const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");

const worms = [];

init();

//---------------------

function init() {
  for (let i = 0; i < 100; i++) {
    const worm = [];
    const position = {
      x: random(0, canvas.width),
      y: random(-500, canvas.height)
    };

    for (let i = 0; i < 10; i++) {
      worm[i] = {
        x: position.x,
        y: position.y
      };
    }

    worms.push(worm);
  }

  anime();
}

function anime() {
  context.clearRect(0, 0, canvas.width, canvas.height);

  for (let worm of worms) {
    draw_worm(worm);
    move_worm(worm, {
      x: random(-10, 10),
      y: random(-5, 15)
    });
  }

  requestAnimationFrame(anime);
}

function draw_worm(worm) {
  context.beginPath();
  context.fillStyle = "#c6fdfd";
  context.strokeStyle = "#56fdad";
  context.arc(worm[0].x, worm[0].y, 2, 0, Math.PI * 2);
  context.fill();

  for (let seg of worm)
    context.lineTo(seg.x, seg.y);
  context.stroke();
}

function move_worm(worm, v) {
  for (let i = worm.length - 1; i > 0; i--)
    worm[i] = {
      x: worm[i - 1].x,
      y: worm[i - 1].y
    };

  worm[0] = {
    x: clamp(worm[0].x + v.x, 0, canvas.width),
    y: worm[0].y + v.y
  };

  if (worm[worm.length - 1].y > canvas.height)
    for (let seg of worm)
      seg.y -= random(600, 1000);
}

function random(min, max) {
  return min + Math.random() * (max - min);
}

function clamp(number, min, max) {
  if (number < min)
    return min;
  if (number > max)
    return max;
  return number;
}