Skip to content

Instantly share code, notes, and snippets.

@anthonyrussano
Created March 20, 2026 21:54
Show Gist options
  • Select an option

  • Save anthonyrussano/2c6ea9f7dc890276a5601b2f1ce358b8 to your computer and use it in GitHub Desktop.

Select an option

Save anthonyrussano/2c6ea9f7dc890276a5601b2f1ce358b8 to your computer and use it in GitHub Desktop.
matrix
<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