Files
the-nexus/modules/effects/matrix-rain.js
Claude (Opus 4.6) 35dd6c5f17
Some checks failed
Deploy Nexus / deploy (push) Failing after 4s
[claude] Phase 4: Effects modules — matrix rain, lightning, beam, runes, gravity, shockwave (#423) (#444)
Co-authored-by: Claude (Opus 4.6) <claude@hermes.local>
Co-committed-by: Claude (Opus 4.6) <claude@hermes.local>
2026-03-24 18:19:26 +00:00

107 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* matrix-rain.js — Commit-density-driven 2D canvas matrix rain
*
* Category: DATA-TETHERED AESTHETIC
* Data source: state.zoneIntensity (commit activity) + state.commitHashes
*
* Renders a Katakana/hex character rain behind the Three.js canvas.
* Density and speed are tethered to commit zone activity.
* Real commit hashes are occasionally injected as characters.
*/
const MATRIX_CHARS = 'アイウエオカキクケコサシスセソタチツテトナニヌネハヒフヘホマミムメモヤユヨラリルレロワヲン0123456789ABCDEF';
const MATRIX_FONT_SIZE = 14;
let _state = null;
let _canvas = null;
let _ctx = null;
let _drops = [];
/**
* Computes mean activity [0..1] across all agent zones via state.
* @returns {number}
*/
function _totalActivity() {
if (!_state) return 0;
if (typeof _state.totalActivity === 'function') return _state.totalActivity();
const zi = _state.zoneIntensity;
if (!zi) return 0;
const vals = Object.values(zi);
return vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);
}
function _draw() {
if (!_canvas || !_ctx) return;
const activity = _totalActivity();
const commitHashes = _state?.commitHashes ?? [];
// Fade previous frame — creates the trailing glow
_ctx.fillStyle = 'rgba(0, 0, 8, 0.05)';
_ctx.fillRect(0, 0, _canvas.width, _canvas.height);
_ctx.font = `${MATRIX_FONT_SIZE}px monospace`;
const density = 0.1 + activity * 0.9;
const activeColCount = Math.max(1, Math.floor(_drops.length * density));
for (let i = 0; i < _drops.length; i++) {
if (i >= activeColCount) {
if (_drops[i] * MATRIX_FONT_SIZE > _canvas.height) continue;
}
let char;
if (commitHashes.length > 0 && Math.random() < 0.02) {
const hash = commitHashes[Math.floor(Math.random() * commitHashes.length)];
char = hash[Math.floor(Math.random() * hash.length)];
} else {
char = MATRIX_CHARS[Math.floor(Math.random() * MATRIX_CHARS.length)];
}
_ctx.fillStyle = '#aaffaa';
_ctx.fillText(char, i * MATRIX_FONT_SIZE, _drops[i] * MATRIX_FONT_SIZE);
const resetThreshold = 0.975 - activity * 0.015;
if (_drops[i] * MATRIX_FONT_SIZE > _canvas.height && Math.random() > resetThreshold) {
_drops[i] = 0;
}
_drops[i]++;
}
}
function _resetDrops() {
const colCount = Math.floor(window.innerWidth / MATRIX_FONT_SIZE);
_drops = new Array(colCount).fill(1);
}
/**
* @param {THREE.Scene} _scene (unused — 2D canvas effect)
* @param {object} state Shared state bus
* @param {object} _theme (unused — color is hardcoded green for matrix aesthetic)
*/
export function init(_scene, state, _theme) {
_state = state;
_canvas = document.createElement('canvas');
_canvas.id = 'matrix-rain';
_canvas.width = window.innerWidth;
_canvas.height = window.innerHeight;
document.body.appendChild(_canvas);
_ctx = _canvas.getContext('2d');
_resetDrops();
window.addEventListener('resize', () => {
_canvas.width = window.innerWidth;
_canvas.height = window.innerHeight;
_resetDrops();
});
// Run at ~20 fps independent of the Three.js RAF loop
setInterval(_draw, 50);
}
/**
* update() is a no-op — rain runs on its own setInterval.
*/
export function update(_elapsed, _delta) {}