Created
January 27, 2025 05:38
-
-
Save Daethyra/0d83ce92fffb25aa17342e9d657723cb to your computer and use it in GitHub Desktop.
A multilingual matrix rain visualizer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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