60 FPS
0 Drops
0 Chars/sec

Controls

How It Works

function animate(timestamp) {
  const delta = timestamp - lastTime;
  if (delta >= frameInterval) {
    // Semi-transparent bg creates trails
    ctx.fillStyle = bgColor;
    ctx.fillRect(0, 0, width, height);

    drops.forEach(drop => drop.updateAndDraw());

    lastTime = timestamp;
  }
  requestAnimationFrame(animate);
}
class Drop {
  constructor(column) {
    this.x = column * fontSize;
    this.y = Math.random() * -100;
    this.speed = Math.random() * 0.5 + 0.5;
    this.trailLength = Math.floor(Math.random() * 20) + 5;
    this.chars = this.generateChars();
  }

  updateAndDraw() {
    this.y += this.speed;
    this.drawTrail();
    if (this.y > height) this.reset();
  }
}
drawTrail() {
  for (let t = 0; t < this.trailLength; t++) {
    const alpha = 1 - t / this.trailLength;
    const y = this.y - t * fontSize;

    // Head = bright, trail = faded
    ctx.fillStyle = t === 0
      ? headColor
      : `rgba(r, g, b, ${alpha * 0.7})`;

    ctx.fillText(this.chars[t], this.x, y);
  }
}

Try the ASCII Lab

Want more? The ASCII Lab has 20+ render modes, post-effects, image upload, and full parameter controls — inspired by 21st.dev.

Open ASCII Lab →