Files
the-nexus/modules/platform.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

448 lines
16 KiB
JavaScript

// === GLASS PLATFORM + PERLIN NOISE + FLOATING ISLAND + CLOUDS ===
import * as THREE from 'three';
import { NEXUS } from './constants.js';
import { scene } from './scene-setup.js';
// === GLASS PLATFORM ===
const glassPlatformGroup = new THREE.Group();
const platformFrameMat = new THREE.MeshStandardMaterial({
color: 0x0a1828,
metalness: 0.9,
roughness: 0.1,
emissive: new THREE.Color(NEXUS.colors.accent).multiplyScalar(0.06),
});
const platformRimGeo = new THREE.RingGeometry(4.7, 5.3, 64);
const platformRim = new THREE.Mesh(platformRimGeo, platformFrameMat);
platformRim.rotation.x = -Math.PI / 2;
platformRim.castShadow = true;
platformRim.receiveShadow = true;
glassPlatformGroup.add(platformRim);
const borderTorusGeo = new THREE.TorusGeometry(5.0, 0.1, 6, 64);
const borderTorus = new THREE.Mesh(borderTorusGeo, platformFrameMat);
borderTorus.rotation.x = Math.PI / 2;
borderTorus.castShadow = true;
borderTorus.receiveShadow = true;
glassPlatformGroup.add(borderTorus);
const glassTileMat = new THREE.MeshPhysicalMaterial({
color: new THREE.Color(NEXUS.colors.accent),
transparent: true,
opacity: 0.09,
roughness: 0.0,
metalness: 0.0,
transmission: 0.92,
thickness: 0.06,
side: THREE.DoubleSide,
depthWrite: false,
});
const glassEdgeBaseMat = new THREE.LineBasicMaterial({
color: NEXUS.colors.accent,
transparent: true,
opacity: 0.55,
});
export const GLASS_TILE_SIZE = 0.85;
const GLASS_TILE_GAP = 0.14;
const GLASS_TILE_STEP = GLASS_TILE_SIZE + GLASS_TILE_GAP;
export const GLASS_RADIUS = 4.55;
const tileGeo = new THREE.PlaneGeometry(GLASS_TILE_SIZE, GLASS_TILE_SIZE);
const tileEdgeGeo = new THREE.EdgesGeometry(tileGeo);
/** @type {Array<{mat: THREE.LineBasicMaterial, distFromCenter: number}>} */
export const glassEdgeMaterials = [];
const _tileDummy = new THREE.Object3D();
/** @type {Array<{x: number, z: number, distFromCenter: number}>} */
const _tileSlots = [];
for (let row = -5; row <= 5; row++) {
for (let col = -5; col <= 5; col++) {
const x = col * GLASS_TILE_STEP;
const z = row * GLASS_TILE_STEP;
const distFromCenter = Math.sqrt(x * x + z * z);
if (distFromCenter > GLASS_RADIUS) continue;
_tileSlots.push({ x, z, distFromCenter });
}
}
const glassTileIM = new THREE.InstancedMesh(tileGeo, glassTileMat, _tileSlots.length);
glassTileIM.instanceMatrix.setUsage(THREE.StaticDrawUsage);
_tileDummy.rotation.x = -Math.PI / 2;
for (let i = 0; i < _tileSlots.length; i++) {
const { x, z } = _tileSlots[i];
_tileDummy.position.set(x, 0, z);
_tileDummy.updateMatrix();
glassTileIM.setMatrixAt(i, _tileDummy.matrix);
}
glassTileIM.instanceMatrix.needsUpdate = true;
glassPlatformGroup.add(glassTileIM);
for (const { x, z, distFromCenter } of _tileSlots) {
const mat = glassEdgeBaseMat.clone();
const edges = new THREE.LineSegments(tileEdgeGeo, mat);
edges.rotation.x = -Math.PI / 2;
edges.position.set(x, 0.002, z);
glassPlatformGroup.add(edges);
glassEdgeMaterials.push({ mat, distFromCenter });
}
export const voidLight = new THREE.PointLight(NEXUS.colors.accent, 0.5, 14);
voidLight.position.set(0, -3.5, 0);
glassPlatformGroup.add(voidLight);
scene.add(glassPlatformGroup);
glassPlatformGroup.traverse(obj => {
if (obj.isMesh) obj.userData.zoomLabel = 'Glass Platform';
});
// === PERLIN NOISE ===
function createPerlinNoise() {
const p = new Uint8Array(256);
for (let i = 0; i < 256; i++) p[i] = i;
let seed = 42;
function seededRand() {
seed = (seed * 1664525 + 1013904223) & 0xffffffff;
return (seed >>> 0) / 0xffffffff;
}
for (let i = 255; i > 0; i--) {
const j = Math.floor(seededRand() * (i + 1));
const tmp = p[i]; p[i] = p[j]; p[j] = tmp;
}
const perm = new Uint8Array(512);
for (let i = 0; i < 512; i++) perm[i] = p[i & 255];
function fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
function lerp(a, b, t) { return a + t * (b - a); }
function grad(hash, x, y, z) {
const h = hash & 15;
const u = h < 8 ? x : y;
const v = h < 4 ? y : (h === 12 || h === 14) ? x : z;
return ((h & 1) ? -u : u) + ((h & 2) ? -v : v);
}
return function noise(x, y, z) {
z = z || 0;
const X = Math.floor(x) & 255, Y = Math.floor(y) & 255, Z = Math.floor(z) & 255;
x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
const u = fade(x), v = fade(y), w = fade(z);
const A = perm[X] + Y, AA = perm[A] + Z, AB = perm[A + 1] + Z;
const B = perm[X + 1] + Y, BA = perm[B] + Z, BB = perm[B + 1] + Z;
return lerp(
lerp(lerp(grad(perm[AA], x, y, z ), grad(perm[BA], x-1, y, z ), u),
lerp(grad(perm[AB], x, y-1, z ), grad(perm[BB], x-1, y-1, z ), u), v),
lerp(lerp(grad(perm[AA + 1], x, y, z-1), grad(perm[BA + 1], x-1, y, z-1), u),
lerp(grad(perm[AB + 1], x, y-1, z-1), grad(perm[BB + 1], x-1, y-1, z-1), u), v),
w
);
};
}
const perlin = createPerlinNoise();
// === FLOATING ISLAND TERRAIN ===
(function buildFloatingIsland() {
const ISLAND_RADIUS = 9.5;
const SEGMENTS = 96;
const SIZE = ISLAND_RADIUS * 2;
function islandFBm(nx, nz) {
const wx = perlin(nx * 0.5 + 3.7, nz * 0.5 + 1.2) * 0.55;
const wz = perlin(nx * 0.5 + 8.3, nz * 0.5 + 5.9) * 0.55;
const px = nx + wx, pz = nz + wz;
let h = 0;
h += perlin(px, pz ) * 1.000;
h += perlin(px * 2, pz * 2 ) * 0.500;
h += perlin(px * 4, pz * 4 ) * 0.250;
h += perlin(px * 8, pz * 8 ) * 0.125;
h += perlin(px * 16, pz * 16 ) * 0.063;
h /= 1.938;
const ridge = 1.0 - Math.abs(perlin(px * 3.1 + 5.0, pz * 3.1 + 7.0));
return h * 0.78 + ridge * 0.22;
}
const geo = new THREE.PlaneGeometry(SIZE, SIZE, SEGMENTS, SEGMENTS);
geo.rotateX(-Math.PI / 2);
const pos = geo.attributes.position;
const vCount = pos.count;
const rawHeights = new Float32Array(vCount);
for (let i = 0; i < vCount; i++) {
const x = pos.getX(i);
const z = pos.getZ(i);
const dist = Math.sqrt(x * x + z * z) / ISLAND_RADIUS;
const rimNoise = perlin(x * 0.38 + 10, z * 0.38 + 10) * 0.10;
const edgeFactor = Math.max(0, 1 - Math.pow(Math.max(0, dist - rimNoise), 2.4));
const h = islandFBm(x * 0.15, z * 0.15);
const height = ((h + 1) * 0.5) * edgeFactor * 3.2;
pos.setY(i, height);
rawHeights[i] = height;
}
geo.computeVertexNormals();
const colBuf = new Float32Array(vCount * 3);
for (let i = 0; i < vCount; i++) {
const h = rawHeights[i];
let r, g, b;
if (h < 0.25) {
r = 0.11; g = 0.09; b = 0.07;
} else if (h < 0.75) {
const t = (h - 0.25) / 0.50;
r = 0.11 + t * 0.13; g = 0.09 + t * 0.09; b = 0.07 + t * 0.06;
} else if (h < 1.4) {
const t = (h - 0.75) / 0.65;
r = 0.24 + t * 0.12; g = 0.18 + t * 0.10; b = 0.13 + t * 0.10;
} else if (h < 2.2) {
const t = (h - 1.4) / 0.80;
r = 0.36 + t * 0.14; g = 0.28 + t * 0.11; b = 0.23 + t * 0.13;
} else {
const t = Math.min(1, (h - 2.2) / 0.9);
r = 0.50 + t * 0.05;
g = 0.39 + t * 0.10;
b = 0.36 + t * 0.28;
}
colBuf[i * 3] = r;
colBuf[i * 3 + 1] = g;
colBuf[i * 3 + 2] = b;
}
geo.setAttribute('color', new THREE.BufferAttribute(colBuf, 3));
const topMat = new THREE.MeshStandardMaterial({
vertexColors: true,
roughness: 0.86,
metalness: 0.05,
});
const topMesh = new THREE.Mesh(geo, topMat);
topMesh.castShadow = true;
topMesh.receiveShadow = true;
const crystalMat = new THREE.MeshStandardMaterial({
color: new THREE.Color(NEXUS.colors.accent).multiplyScalar(0.55),
emissive: new THREE.Color(NEXUS.colors.accent),
emissiveIntensity: 0.5,
roughness: 0.08,
metalness: 0.25,
transparent: true,
opacity: 0.80,
});
const CRYSTAL_MIN_H = 2.05;
/** @type {Array<{sx:number,sz:number,posY:number,rotX:number,rotZ:number,scaleXZ:number,scaleY:number}>} */
const _spireData = [];
for (let row = -5; row <= 5; row++) {
for (let col = -5; col <= 5; col++) {
const bx = col * 1.75, bz = row * 1.75;
if (Math.sqrt(bx * bx + bz * bz) > ISLAND_RADIUS * 0.72) continue;
const edF = Math.max(0, 1 - Math.pow(Math.sqrt(bx * bx + bz * bz) / ISLAND_RADIUS, 2.4));
const candidateH = ((islandFBm(bx * 0.15, bz * 0.15) + 1) * 0.5) * edF * 3.2;
if (candidateH < CRYSTAL_MIN_H) continue;
const jx = bx + perlin(bx * 0.7 + 20, bz * 0.7 + 20) * 0.55;
const jz = bz + perlin(bx * 0.7 + 30, bz * 0.7 + 30) * 0.55;
if (Math.sqrt(jx * jx + jz * jz) > ISLAND_RADIUS * 0.68) continue;
const clusterSize = 2 + Math.floor(Math.abs(perlin(bx * 0.5 + 40, bz * 0.5 + 40)) * 3);
for (let c = 0; c < clusterSize; c++) {
const angle = (c / clusterSize) * Math.PI * 2 + perlin(bx + c, bz + c) * 1.4;
const spread = 0.08 + Math.abs(perlin(bx + c * 5, bz + c * 5)) * 0.22;
const sx = jx + Math.cos(angle) * spread;
const sz = jz + Math.sin(angle) * spread;
const spireScale = 0.14 + (candidateH - CRYSTAL_MIN_H) * 0.11;
const spireH = spireScale * (0.8 + Math.abs(perlin(sx, sz)) * 0.45);
const spireR = spireH * 0.17;
_spireData.push({
sx, sz,
posY: candidateH + spireH * 0.5,
rotX: perlin(sx * 3 + 1, sz * 3 + 1) * 0.18,
rotZ: perlin(sx * 2, sz * 2) * 0.28,
scaleXZ: spireR,
scaleY: spireH * 2.8,
});
}
}
}
const _spireDummy = new THREE.Object3D();
const spireBaseGeo = new THREE.ConeGeometry(1, 1, 5);
const crystalGroup = new THREE.Group();
const spireIM = new THREE.InstancedMesh(spireBaseGeo, crystalMat, _spireData.length);
spireIM.castShadow = true;
spireIM.instanceMatrix.setUsage(THREE.StaticDrawUsage);
for (let i = 0; i < _spireData.length; i++) {
const { sx, sz, posY, rotX, rotZ, scaleXZ, scaleY } = _spireData[i];
_spireDummy.position.set(sx, posY, sz);
_spireDummy.rotation.set(rotX, 0, rotZ);
_spireDummy.scale.set(scaleXZ, scaleY, scaleXZ);
_spireDummy.updateMatrix();
spireIM.setMatrixAt(i, _spireDummy.matrix);
}
spireIM.instanceMatrix.needsUpdate = true;
crystalGroup.add(spireIM);
const BOTTOM_SEGS_R = 52;
const BOTTOM_SEGS_V = 10;
const BOTTOM_HEIGHT = 2.6;
const bottomGeo = new THREE.CylinderGeometry(
ISLAND_RADIUS * 0.80, ISLAND_RADIUS * 0.28,
BOTTOM_HEIGHT, BOTTOM_SEGS_R, BOTTOM_SEGS_V, true
);
const bPos = bottomGeo.attributes.position;
for (let i = 0; i < bPos.count; i++) {
const bx = bPos.getX(i);
const bz = bPos.getZ(i);
const by = bPos.getY(i);
const angle = Math.atan2(bz, bx);
const r = Math.sqrt(bx * bx + bz * bz);
const radDisp = perlin(Math.cos(angle) * 1.6 + 50, Math.sin(angle) * 1.6 + 50) * 0.65;
const vNorm = (by + BOTTOM_HEIGHT * 0.5) / BOTTOM_HEIGHT;
const stalDisp = (1 - vNorm) * Math.abs(perlin(bx * 0.35 + 70, by * 0.7 + bz * 0.35)) * 0.9;
const newR = r + radDisp;
bPos.setX(i, (bx / r) * newR);
bPos.setZ(i, (bz / r) * newR);
bPos.setY(i, by - stalDisp);
}
bottomGeo.computeVertexNormals();
const bottomMat = new THREE.MeshStandardMaterial({ color: 0x0c0a08, roughness: 0.93, metalness: 0.02 });
const bottomMesh = new THREE.Mesh(bottomGeo, bottomMat);
bottomMesh.position.y = -BOTTOM_HEIGHT * 0.5;
bottomMesh.castShadow = true;
const capGeo = new THREE.CircleGeometry(ISLAND_RADIUS * 0.28, 48);
capGeo.rotateX(Math.PI / 2);
const capMesh = new THREE.Mesh(capGeo, bottomMat);
capMesh.position.y = -(BOTTOM_HEIGHT + 0.1);
const islandGroup = new THREE.Group();
islandGroup.add(topMesh);
islandGroup.add(crystalGroup);
islandGroup.add(bottomMesh);
islandGroup.add(capMesh);
islandGroup.position.y = -2.8;
scene.add(islandGroup);
})();
// === PROCEDURAL CLOUD LAYER ===
const CLOUD_LAYER_Y = -6.0;
const CLOUD_DIMENSIONS = 120;
const CLOUD_THICKNESS = 15;
const CLOUD_OPACITY = 0.6;
const cloudGeometry = new THREE.BoxGeometry(CLOUD_DIMENSIONS, CLOUD_THICKNESS, CLOUD_DIMENSIONS, 8, 4, 8);
const CloudShader = {
uniforms: {
'uTime': { value: 0.0 },
'uCloudColor': { value: new THREE.Color(0x88bbff) },
'uNoiseScale': { value: new THREE.Vector3(0.015, 0.015, 0.015) },
'uDensity': { value: 0.8 },
},
vertexShader: `
varying vec3 vWorldPosition;
void main() {
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float uTime;
uniform vec3 uCloudColor;
uniform vec3 uNoiseScale;
uniform float uDensity;
varying vec3 vWorldPosition;
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod289(i);
vec4 p = permute(permute(permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 0.142857142857;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ * ns.x + ns.yyyy;
vec4 y = y_ * ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0) * 2.0 + 1.0;
vec4 s1 = floor(b1) * 2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
vec3 p0 = vec3(a0.xy, h.x);
vec3 p1 = vec3(a0.zw, h.y);
vec3 p2 = vec3(a1.xy, h.z);
vec3 p3 = vec3(a1.zw, h.w);
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
}
void main() {
vec3 noiseCoord = vWorldPosition * uNoiseScale + vec3(uTime * 0.003, 0.0, uTime * 0.002);
float noiseVal = snoise(noiseCoord) * 0.500;
noiseVal += snoise(noiseCoord * 2.0) * 0.250;
noiseVal += snoise(noiseCoord * 4.0) * 0.125;
noiseVal /= 0.875;
float density = smoothstep(0.25, 0.85, noiseVal * 0.5 + 0.5);
density *= uDensity;
float layerBottom = ${(CLOUD_LAYER_Y - CLOUD_THICKNESS * 0.5).toFixed(1)};
float yNorm = (vWorldPosition.y - layerBottom) / ${CLOUD_THICKNESS.toFixed(1)};
float fadeFactor = smoothstep(0.0, 0.15, yNorm) * smoothstep(1.0, 0.85, yNorm);
gl_FragColor = vec4(uCloudColor, density * fadeFactor * ${CLOUD_OPACITY.toFixed(1)});
if (gl_FragColor.a < 0.04) discard;
}
`,
};
export const cloudMaterial = new THREE.ShaderMaterial({
uniforms: CloudShader.uniforms,
vertexShader: CloudShader.vertexShader,
fragmentShader: CloudShader.fragmentShader,
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide,
});
const clouds = new THREE.Mesh(cloudGeometry, cloudMaterial);
clouds.position.y = CLOUD_LAYER_Y;
scene.add(clouds);