Files
the-nexus/modules/celebrations.js
Alexander Whitestone cbfacdfe19
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Smoke Test / smoke-test (push) Successful in 1s
refactor: split app.js (5416 lines) into 21 modules — hard cap 1000 lines/file
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.
2026-03-24 15:12:22 -04:00

217 lines
7.1 KiB
JavaScript

// === SOVEREIGNTY EASTER EGG + SHOCKWAVE + FIREWORKS + MERGE FLASH ===
import * as THREE from 'three';
import { scene, starMaterial, constellationLines } from './scene-setup.js';
import { S } from './state.js';
import { clock } from './warp.js';
// === SOVEREIGNTY EASTER EGG ===
const SOVEREIGNTY_WORD = 'sovereignty';
const sovereigntyMsg = document.getElementById('sovereignty-msg');
export function triggerSovereigntyEasterEgg() {
const originalLineColor = constellationLines.material.color.getHex();
constellationLines.material.color.setHex(0xffd700);
constellationLines.material.opacity = 0.9;
const originalStarColor = starMaterial.color.getHex();
const originalStarOpacity = starMaterial.opacity;
starMaterial.color.setHex(0xffd700);
starMaterial.opacity = 1.0;
if (sovereigntyMsg) {
sovereigntyMsg.classList.remove('visible');
void sovereigntyMsg.offsetWidth;
sovereigntyMsg.classList.add('visible');
}
const startTime = performance.now();
const DURATION = 2500;
function fadeBack() {
const t = Math.min((performance.now() - startTime) / DURATION, 1);
const eased = t * t;
const goldR = 1.0, goldG = 0.843, goldB = 0;
const origColor = new THREE.Color(originalStarColor);
starMaterial.color.setRGB(
goldR + (origColor.r - goldR) * eased,
goldG + (origColor.g - goldG) * eased,
goldB + (origColor.b - goldB) * eased
);
starMaterial.opacity = 1.0 + (originalStarOpacity - 1.0) * eased;
const origLineColor = new THREE.Color(originalLineColor);
constellationLines.material.color.setRGB(
1.0 + (origLineColor.r - 1.0) * eased,
0.843 + (origLineColor.g - 0.843) * eased,
0 + origLineColor.b * eased
);
if (t < 1) {
requestAnimationFrame(fadeBack);
} else {
starMaterial.color.setHex(originalStarColor);
starMaterial.opacity = originalStarOpacity;
constellationLines.material.color.setHex(originalLineColor);
if (sovereigntyMsg) sovereigntyMsg.classList.remove('visible');
}
}
requestAnimationFrame(fadeBack);
}
// === SHOCKWAVE RIPPLE ===
const SHOCKWAVE_RING_COUNT = 3;
const SHOCKWAVE_MAX_RADIUS = 14;
export const SHOCKWAVE_DURATION = 2.5;
export const shockwaveRings = [];
export function triggerShockwave() {
const now = clock.getElapsedTime();
for (let i = 0; i < SHOCKWAVE_RING_COUNT; i++) {
const mat = new THREE.MeshBasicMaterial({
color: 0x00ffff, transparent: true, opacity: 0,
side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending,
});
const geo = new THREE.RingGeometry(0.9, 1.0, 64);
const mesh = new THREE.Mesh(geo, mat);
mesh.rotation.x = -Math.PI / 2;
mesh.position.y = 0.02;
scene.add(mesh);
shockwaveRings.push({ mesh, mat, startTime: now, delay: i * 0.35 });
}
}
// === FIREWORK CELEBRATION ===
const FIREWORK_COLORS = [0xff4466, 0xffaa00, 0x00ffaa, 0x4488ff, 0xff44ff, 0xffff44, 0x00ffff];
export const FIREWORK_BURST_PARTICLES = 80;
export const FIREWORK_BURST_DURATION = 2.2;
export const FIREWORK_GRAVITY = -5.0;
export const fireworkBursts = [];
function spawnFireworkBurst(origin, color) {
const now = clock.getElapsedTime();
const count = FIREWORK_BURST_PARTICLES;
const positions = new Float32Array(count * 3);
const origins = new Float32Array(count * 3);
const velocities = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
const speed = 2.5 + Math.random() * 3.5;
velocities[i * 3] = Math.sin(phi) * Math.cos(theta) * speed;
velocities[i * 3 + 1] = Math.sin(phi) * Math.sin(theta) * speed;
velocities[i * 3 + 2] = Math.cos(phi) * speed;
origins[i * 3] = origin.x;
origins[i * 3 + 1] = origin.y;
origins[i * 3 + 2] = origin.z;
positions[i * 3] = origin.x;
positions[i * 3 + 1] = origin.y;
positions[i * 3 + 2] = origin.z;
}
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const mat = new THREE.PointsMaterial({
color, size: 0.35, sizeAttenuation: true,
transparent: true, opacity: 1.0,
blending: THREE.AdditiveBlending, depthWrite: false,
});
const points = new THREE.Points(geo, mat);
scene.add(points);
fireworkBursts.push({ points, geo, mat, origins, velocities, startTime: now });
}
export function triggerFireworks() {
const burstCount = 6;
for (let i = 0; i < burstCount; i++) {
const delay = i * 0.35;
setTimeout(() => {
const x = (Math.random() - 0.5) * 12;
const y = 8 + Math.random() * 6;
const z = (Math.random() - 0.5) * 12;
const color = FIREWORK_COLORS[Math.floor(Math.random() * FIREWORK_COLORS.length)];
spawnFireworkBurst(new THREE.Vector3(x, y, z), color);
}, delay * 1000);
}
}
export function triggerMergeFlash() {
triggerShockwave();
const originalLineColor = constellationLines.material.color.getHex();
constellationLines.material.color.setHex(0x00ffff);
constellationLines.material.opacity = 1.0;
const originalStarColor = starMaterial.color.getHex();
const originalStarOpacity = starMaterial.opacity;
starMaterial.color.setHex(0x00ffff);
starMaterial.opacity = 1.0;
const startTime = performance.now();
const DURATION = 2000;
function fadeBack() {
const t = Math.min((performance.now() - startTime) / DURATION, 1);
const eased = t * t;
const mergeR = 0.0, mergeG = 1.0, mergeB = 1.0;
const origStarColor = new THREE.Color(originalStarColor);
starMaterial.color.setRGB(
mergeR + (origStarColor.r - mergeR) * eased,
mergeG + (origStarColor.g - mergeG) * eased,
mergeB + (origStarColor.b - mergeB) * eased
);
starMaterial.opacity = 1.0 + (originalStarOpacity - 1.0) * eased;
const origLineColor = new THREE.Color(originalLineColor);
constellationLines.material.color.setRGB(
mergeR + (origLineColor.r - mergeR) * eased,
mergeG + (origLineColor.g - mergeG) * eased,
mergeB + (origLineColor.b - mergeB) * eased
);
constellationLines.material.opacity = 1.0 + (0.18 - 1.0) * eased;
if (t < 1) {
requestAnimationFrame(fadeBack);
} else {
starMaterial.color.setHex(originalStarColor);
starMaterial.opacity = originalStarOpacity;
constellationLines.material.color.setHex(originalLineColor);
constellationLines.material.opacity = 0.18;
}
}
requestAnimationFrame(fadeBack);
}
export function initSovereigntyEasterEgg() {
document.addEventListener('keydown', (e) => {
if (e.metaKey || e.ctrlKey || e.altKey) return;
if (e.key.length !== 1) {
S.sovereigntyBuffer = '';
return;
}
S.sovereigntyBuffer += e.key.toLowerCase();
if (S.sovereigntyBuffer.length > SOVEREIGNTY_WORD.length) {
S.sovereigntyBuffer = S.sovereigntyBuffer.slice(-SOVEREIGNTY_WORD.length);
}
if (S.sovereigntyBuffer === SOVEREIGNTY_WORD) {
S.sovereigntyBuffer = '';
triggerSovereigntyEasterEgg();
}
if (S.sovereigntyBufferTimer) clearTimeout(S.sovereigntyBufferTimer);
S.sovereigntyBufferTimer = setTimeout(() => { S.sovereigntyBuffer = ''; }, 3000);
});
}