Created
March 20, 2026 21:54
-
-
Save anthonyrussano/2c6ea9f7dc890276a5601b2f1ce358b8 to your computer and use it in GitHub Desktop.
matrix
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
| <div id="matrix-container" style="width: 100%; height: 100%; overflow: hidden; background: black; position: relative;"> | |
| <canvas id="matrix-canvas" style="width: 100%; height: 100%; display: block;"></canvas> | |
| </div> | |
| <script> | |
| (() => { | |
| const container = document.getElementById('matrix-container'); | |
| const canvas = document.getElementById('matrix-canvas'); | |
| const ctx = canvas.getContext('2d', { alpha: false }); | |
| const config = { | |
| fontSize: 16, | |
| columnSpacing: 2, | |
| fps: 18, | |
| glowBlur: 8, | |
| trailColor: '#22c55e', | |
| headColor: '#d9f99d', | |
| backgroundFade: 'rgba(0, 0, 0, 0.08)', | |
| chars: 'アイウエオカキクケコサシスセソ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%&*+-<>' | |
| }; | |
| let width = 0; | |
| let height = 0; | |
| let dpr = Math.max(1, window.devicePixelRatio || 1); | |
| let columnWidth = config.fontSize + config.columnSpacing; | |
| let columns = 0; | |
| let drops = []; | |
| let animationId = null; | |
| let lastFrameTime = 0; | |
| const frameInterval = 1000 / config.fps; | |
| let isRunning = false; | |
| const chars = config.chars.split(''); | |
| function randomChar() { | |
| return chars[(Math.random() * chars.length) | 0]; | |
| } | |
| function setupCanvas() { | |
| const rect = container.getBoundingClientRect(); | |
| width = Math.max(1, Math.floor(rect.width)); | |
| height = Math.max(1, Math.floor(rect.height)); | |
| dpr = Math.max(1, window.devicePixelRatio || 1); | |
| canvas.width = Math.floor(width * dpr); | |
| canvas.height = Math.floor(height * dpr); | |
| canvas.style.width = `${width}px`; | |
| canvas.style.height = `${height}px`; | |
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | |
| ctx.textBaseline = 'top'; | |
| ctx.font = `${config.fontSize}px monospace`; | |
| columnWidth = config.fontSize + config.columnSpacing; | |
| columns = Math.max(1, Math.floor(width / columnWidth)); | |
| drops = Array.from({ length: columns }, () => | |
| Math.floor((Math.random() * height) / config.fontSize) * -1 | |
| ); | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect(0, 0, width, height); | |
| // Draw once immediately so it does not appear blank | |
| drawFrame(); | |
| } | |
| function drawFrame() { | |
| ctx.fillStyle = config.backgroundFade; | |
| ctx.fillRect(0, 0, width, height); | |
| ctx.font = `${config.fontSize}px monospace`; | |
| for (let i = 0; i < columns; i++) { | |
| const x = i * columnWidth; | |
| const y = drops[i] * config.fontSize; | |
| ctx.shadowBlur = config.glowBlur; | |
| ctx.shadowColor = config.headColor; | |
| ctx.fillStyle = config.headColor; | |
| ctx.fillText(randomChar(), x, y); | |
| if (y - config.fontSize > 0) { | |
| ctx.shadowBlur = 0; | |
| ctx.fillStyle = config.trailColor; | |
| ctx.fillText(randomChar(), x, y - config.fontSize); | |
| } | |
| if (y > height && Math.random() > 0.975) { | |
| drops[i] = Math.floor(Math.random() * -20); | |
| } else { | |
| drops[i] += 1; | |
| } | |
| } | |
| ctx.shadowBlur = 0; | |
| } | |
| function animate(timestamp) { | |
| if (!isRunning) return; | |
| if (!lastFrameTime) lastFrameTime = timestamp; | |
| const elapsed = timestamp - lastFrameTime; | |
| if (elapsed >= frameInterval) { | |
| lastFrameTime = timestamp - (elapsed % frameInterval); | |
| drawFrame(); | |
| } | |
| animationId = requestAnimationFrame(animate); | |
| } | |
| function start() { | |
| if (animationId !== null) return; | |
| isRunning = true; | |
| lastFrameTime = 0; | |
| animationId = requestAnimationFrame(animate); | |
| } | |
| function stop() { | |
| isRunning = false; | |
| if (animationId !== null) { | |
| cancelAnimationFrame(animationId); | |
| animationId = null; | |
| } | |
| } | |
| const resizeObserver = new ResizeObserver(() => { | |
| setupCanvas(); | |
| }); | |
| resizeObserver.observe(container); | |
| document.addEventListener('visibilitychange', () => { | |
| if (document.hidden) { | |
| stop(); | |
| } else { | |
| start(); | |
| } | |
| }); | |
| setupCanvas(); | |
| start(); | |
| })(); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment