← Back
Canvas

Matrix Rain

Digital katakana rain rendered on canvas — bright head character with fading trail.

canvas · requestAnimationFrame · falling katakana

MatrixRain.tsx78 lines
"use client";

import { useEffect, useRef } from "react";

export default function MatrixRain() {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const resize = () => {
      canvas.width = canvas.offsetWidth;
      canvas.height = canvas.offsetHeight;
    };
    resize();

    const FONT_SIZE = 14;
    const CHARS =
      "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*";

    let cols = Math.floor(canvas.width / FONT_SIZE);
    let drops = new Array(cols).fill(1);

    let rafId: number;

    const draw = () => {
      ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      ctx.font = `${FONT_SIZE}px monospace`;

      for (let i = 0; i < drops.length; i++) {
        const char = CHARS[Math.floor(Math.random() * CHARS.length)];

        // bright head character
        ctx.fillStyle = drops[i] < 3 ? "#aaffaa" : "#00ff41";
        ctx.fillText(char, i * FONT_SIZE, drops[i] * FONT_SIZE);

        if (drops[i] * FONT_SIZE > canvas.height && Math.random() > 0.975) {
          drops[i] = 0;
        }
        drops[i]++;
      }

      rafId = requestAnimationFrame(draw);
    };

    draw();

    const onResize = () => {
      resize();
      cols = Math.floor(canvas.width / FONT_SIZE);
      drops = new Array(cols).fill(1);
    };
    window.addEventListener("resize", onResize);

    return () => {
      cancelAnimationFrame(rafId);
      window.removeEventListener("resize", onResize);
    };
  }, []);

  return (
    <div className="relative w-full">
      <canvas
        ref={canvasRef}
        className="w-full h-[420px] rounded-xl bg-black block"
      />
      <p className="absolute bottom-4 left-0 right-0 text-center text-[10px] font-mono text-[#00ff41]/30 pointer-events-none">
        canvas · requestAnimationFrame · falling katakana
      </p>
    </div>
  );
}

More in Canvas