app.js: 5416 → 528 lines (entry point, animation loop, event wiring) modules/state.js: shared mutable state object modules/constants.js: color palette modules/matrix-rain.js: matrix rain canvas effect modules/scene-setup.js: scene, camera, renderer, lighting, stars modules/platform.js: glass platform, perlin noise, floating island, clouds modules/heatmap.js: commit heatmap modules/sigil.js: Timmy sigil modules/controls.js: mouse, overview, zoom, photo mode modules/effects.js: energy beam, sovereignty meter, rune ring modules/earth.js: holographic earth modules/warp.js: warp tunnel, crystals, lightning modules/dual-brain.js: dual-brain holographic panel modules/audio.js: Web Audio, spatial, portal hums modules/debug.js: debug mode, websocket, session export modules/celebrations.js: easter egg, shockwave, fireworks modules/portals.js: portal loading modules/bookshelves.js: floating bookshelves, spine textures modules/oath.js: The Oath interactive SOUL.md modules/panels.js: agent status board, LoRA panel modules/weather.js: weather system, portal health modules/extras.js: gravity zones, speech, timelapse, bitcoin Largest file: 528 lines (app.js). No file exceeds 1000. All files pass node --check. No refactoring — mechanical split only.
84 lines
2.8 KiB
JavaScript
84 lines
2.8 KiB
JavaScript
// === MATRIX RAIN === + === ASSET LOADER ===
|
||
import * as THREE from 'three';
|
||
import { S } from './state.js';
|
||
|
||
// === ASSET LOADER ===
|
||
export const loadedAssets = new Map();
|
||
|
||
// Forward ref: animate() is set by app.js after all modules load
|
||
let _animateFn = null;
|
||
export function setAnimateFn(fn) { _animateFn = fn; }
|
||
|
||
export const loadingManager = new THREE.LoadingManager(() => {
|
||
document.getElementById('loading-bar').style.width = '100%';
|
||
document.getElementById('loading').style.display = 'none';
|
||
if (_animateFn) _animateFn();
|
||
});
|
||
|
||
loadingManager.onProgress = (url, itemsLoaded, itemsTotal) => {
|
||
const progress = (itemsLoaded / itemsTotal) * 100;
|
||
document.getElementById('loading-bar').style.width = `${progress}%`;
|
||
};
|
||
|
||
// === MATRIX RAIN ===
|
||
const matrixCanvas = document.createElement('canvas');
|
||
matrixCanvas.id = 'matrix-rain';
|
||
matrixCanvas.width = window.innerWidth;
|
||
matrixCanvas.height = window.innerHeight;
|
||
document.body.appendChild(matrixCanvas);
|
||
|
||
const matrixCtx = matrixCanvas.getContext('2d');
|
||
|
||
const MATRIX_CHARS = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン0123456789ABCDEF';
|
||
const MATRIX_FONT_SIZE = 14;
|
||
const MATRIX_COL_COUNT = Math.floor(window.innerWidth / MATRIX_FONT_SIZE);
|
||
const matrixDrops = new Array(MATRIX_COL_COUNT).fill(1);
|
||
|
||
// totalActivity is provided by warp module — imported lazily via a setter
|
||
let _totalActivityFn = () => 0;
|
||
export function setTotalActivityFn(fn) { _totalActivityFn = fn; }
|
||
|
||
function drawMatrixRain() {
|
||
matrixCtx.fillStyle = 'rgba(0, 0, 8, 0.05)';
|
||
matrixCtx.fillRect(0, 0, matrixCanvas.width, matrixCanvas.height);
|
||
|
||
matrixCtx.font = `${MATRIX_FONT_SIZE}px monospace`;
|
||
|
||
const activity = _totalActivityFn();
|
||
const density = 0.1 + activity * 0.9;
|
||
const activeColCount = Math.max(1, Math.floor(matrixDrops.length * density));
|
||
|
||
for (let i = 0; i < matrixDrops.length; i++) {
|
||
if (i >= activeColCount) {
|
||
if (matrixDrops[i] * MATRIX_FONT_SIZE > matrixCanvas.height) continue;
|
||
}
|
||
|
||
let char;
|
||
if (S._matrixCommitHashes.length > 0 && Math.random() < 0.02) {
|
||
const hash = S._matrixCommitHashes[Math.floor(Math.random() * S._matrixCommitHashes.length)];
|
||
char = hash[Math.floor(Math.random() * hash.length)];
|
||
} else {
|
||
char = MATRIX_CHARS[Math.floor(Math.random() * MATRIX_CHARS.length)];
|
||
}
|
||
|
||
const x = i * MATRIX_FONT_SIZE;
|
||
const y = matrixDrops[i] * MATRIX_FONT_SIZE;
|
||
|
||
matrixCtx.fillStyle = '#aaffaa';
|
||
matrixCtx.fillText(char, x, y);
|
||
|
||
const resetThreshold = 0.975 - activity * 0.015;
|
||
if (y > matrixCanvas.height && Math.random() > resetThreshold) {
|
||
matrixDrops[i] = 0;
|
||
}
|
||
matrixDrops[i]++;
|
||
}
|
||
}
|
||
|
||
setInterval(drawMatrixRain, 50);
|
||
|
||
window.addEventListener('resize', () => {
|
||
matrixCanvas.width = window.innerWidth;
|
||
matrixCanvas.height = window.innerHeight;
|
||
});
|