Skip to content

Instantly share code, notes, and snippets.

@Daethyra
Created January 27, 2025 05:38
Show Gist options
  • Save Daethyra/0d83ce92fffb25aa17342e9d657723cb to your computer and use it in GitHub Desktop.
Save Daethyra/0d83ce92fffb25aa17342e9d657723cb to your computer and use it in GitHub Desktop.
A multilingual matrix rain visualizer.
<!DOCTYPE html>
<html>
<head>
<title>Matrix Rain</title>
<style>
body {
margin: 0;
overflow: hidden;
background: black;
}
canvas {
display: block;
image-rendering: crisp-edges;
text-rendering: geometricPrecision;
}
</style>
</head>
<body>
<!--
Filename: matrix-rain.html
Author: Daethyra Carino <[email protected]>
Date: 2025-01-26
Version: v0.1.0
License: MIT (c) 2025 Daethyra Carino
Description: A responsive, multilingual Matrix-style code rain animation featuring glowing green characters (Katakana, Latin, Hanzi, Hangul) with dynamic trails, variable fall speeds, and random resets. Built with HTML5 Canvas.
-->
<canvas id="matrix"></canvas>
<script>
const canvas = document.getElementById('matrix');
const ctx = canvas.getContext('2d');
// Character sets
const katakana = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレヱゲゼデベペオォコソトノホモヨョロヲゴゾドボポヴッン';
const roman = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@*#-%$';
const hanzi = Array.from({length: 200}, (_, i) => String.fromCharCode(0x4E00 + i));
const hangul = Array.from({length: 200}, (_, i) => String.fromCharCode(0xAC00 + i));
const chars = [
katakana,
roman,
hanzi.join(''),
hangul.join('')
].join('');
let charsArray = chars.split('').filter(c => c.trim() !== '');
if (charsArray.length === 0) charsArray = ['A'];
let fontSize = 16;
let columns = [];
let drops = [];
const trailLength = 4;
function initialize() {
fontSize = Math.floor(window.innerWidth / 90);
fontSize = Math.min(Math.max(fontSize, 14), 24);
ctx.font = `${fontSize}px "MS Gothic", "AppleGothic", "Monaco", "Consolas", monospace`;
columns = [];
drops = [];
let x = 0;
while (x < canvas.width) {
const randomChar = charsArray[Math.floor(Math.random() * charsArray.length)];
const charWidth = ctx.measureText(randomChar).width;
const columnWidth = charWidth * 1.25;
if (x + columnWidth > canvas.width) break;
columns.push({
width: columnWidth,
x: x
});
drops.push({
y: -Math.random() * canvas.height/2,
speed: Math.random() * 0.5 + 0.5 // Variable fall speed
});
x += columnWidth;
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initialize();
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
let lastFrame = performance.now();
const fpsInterval = 1000/60;
function draw() {
const now = performance.now();
const elapsed = now - lastFrame;
if (elapsed > fpsInterval) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < columns.length; i++) {
const column = columns[i];
const drop = drops[i];
// Random chance to reset column
if (Math.random() < 0.005) { // 0.5% chance per frame to reset
drop.y = -trailLength;
drop.speed = Math.random() * 0.5 + 0.5; // New random speed
}
// Draw characters with fading trail
for (let j = trailLength - 1; j >= 0; j--) {
const pos = drop.y - j;
if (pos < 0) continue;
const char = charsArray[Math.floor(Math.random() * charsArray.length)];
const fade = Math.floor(255 * (1 - j/trailLength));
const charWidth = ctx.measureText(char).width;
const xPos = column.x + (column.width - charWidth)/2;
if (j === 0) {
ctx.save();
ctx.shadowColor = `rgba(255, 255, 255, ${0.3 + (1 - j/trailLength)*0.5})`;
ctx.shadowBlur = 15;
ctx.fillStyle = `rgb(${fade}, 255, ${fade})`;
ctx.fillText(char, xPos, pos * fontSize);
ctx.restore();
} else {
ctx.fillStyle = `rgb(${fade}, 255, ${fade})`;
ctx.fillText(char, xPos, pos * fontSize);
}
}
// Update position
drop.y += drop.speed;
if (drop.y * fontSize > canvas.height + trailLength) {
drop.y = -trailLength;
}
}
lastFrame = now - (elapsed % fpsInterval);
}
requestAnimationFrame(draw);
}
draw();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment