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.
183 lines
5.4 KiB
JavaScript
183 lines
5.4 KiB
JavaScript
// === TIMMY SIGIL ===
|
|
import * as THREE from 'three';
|
|
import { scene } from './scene-setup.js';
|
|
|
|
const SIGIL_CANVAS_SIZE = 512;
|
|
const SIGIL_RADIUS = 3.8;
|
|
|
|
function drawSigilCanvas() {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = SIGIL_CANVAS_SIZE;
|
|
canvas.height = SIGIL_CANVAS_SIZE;
|
|
const ctx = canvas.getContext('2d');
|
|
const cx = SIGIL_CANVAS_SIZE / 2;
|
|
const cy = SIGIL_CANVAS_SIZE / 2;
|
|
const r = cx * 0.88;
|
|
|
|
ctx.clearRect(0, 0, SIGIL_CANVAS_SIZE, SIGIL_CANVAS_SIZE);
|
|
|
|
const bgGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);
|
|
bgGrad.addColorStop(0, 'rgba(0, 200, 255, 0.10)');
|
|
bgGrad.addColorStop(0.5, 'rgba(0, 100, 200, 0.04)');
|
|
bgGrad.addColorStop(1, 'rgba(0, 0, 0, 0)');
|
|
ctx.fillStyle = bgGrad;
|
|
ctx.fillRect(0, 0, SIGIL_CANVAS_SIZE, SIGIL_CANVAS_SIZE);
|
|
|
|
function glowCircle(x, y, radius, color, alpha, lineW) {
|
|
ctx.save();
|
|
ctx.globalAlpha = alpha;
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = lineW;
|
|
ctx.shadowColor = color;
|
|
ctx.shadowBlur = 12;
|
|
ctx.beginPath();
|
|
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
}
|
|
|
|
function hexagram(ox, oy, hr, color, alpha) {
|
|
ctx.save();
|
|
ctx.globalAlpha = alpha;
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = 1.4;
|
|
ctx.shadowColor = color;
|
|
ctx.shadowBlur = 10;
|
|
ctx.beginPath();
|
|
for (let i = 0; i < 3; i++) {
|
|
const a = (i / 3) * Math.PI * 2 - Math.PI / 2;
|
|
const px = ox + Math.cos(a) * hr;
|
|
const py = oy + Math.sin(a) * hr;
|
|
i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
|
}
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
ctx.beginPath();
|
|
for (let i = 0; i < 3; i++) {
|
|
const a = (i / 3) * Math.PI * 2 + Math.PI / 2;
|
|
const px = ox + Math.cos(a) * hr;
|
|
const py = oy + Math.sin(a) * hr;
|
|
i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
|
}
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
}
|
|
|
|
const petalR = r * 0.32;
|
|
|
|
glowCircle(cx, cy, petalR, '#00ccff', 0.65, 1.0);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
const a = (i / 6) * Math.PI * 2;
|
|
glowCircle(cx + Math.cos(a) * petalR, cy + Math.sin(a) * petalR, petalR, '#00aadd', 0.50, 0.8);
|
|
}
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
const a = (i / 6) * Math.PI * 2 + Math.PI / 6;
|
|
glowCircle(cx + Math.cos(a) * petalR * 1.73, cy + Math.sin(a) * petalR * 1.73, petalR, '#0077aa', 0.25, 0.6);
|
|
}
|
|
|
|
hexagram(cx, cy, r * 0.62, '#ffd700', 0.75);
|
|
hexagram(cx, cy, r * 0.41, '#ffaa00', 0.50);
|
|
|
|
glowCircle(cx, cy, r * 0.92, '#0055aa', 0.40, 0.8);
|
|
glowCircle(cx, cy, r * 0.72, '#0099cc', 0.38, 0.8);
|
|
glowCircle(cx, cy, r * 0.52, '#00ccff', 0.42, 0.9);
|
|
glowCircle(cx, cy, r * 0.18, '#ffd700', 0.65, 1.2);
|
|
|
|
ctx.save();
|
|
ctx.globalAlpha = 0.28;
|
|
ctx.strokeStyle = '#00aaff';
|
|
ctx.lineWidth = 0.6;
|
|
ctx.shadowColor = '#00aaff';
|
|
ctx.shadowBlur = 5;
|
|
for (let i = 0; i < 12; i++) {
|
|
const a = (i / 12) * Math.PI * 2;
|
|
ctx.beginPath();
|
|
ctx.moveTo(cx + Math.cos(a) * r * 0.18, cy + Math.sin(a) * r * 0.18);
|
|
ctx.lineTo(cx + Math.cos(a) * r * 0.91, cy + Math.sin(a) * r * 0.91);
|
|
ctx.stroke();
|
|
}
|
|
ctx.restore();
|
|
|
|
ctx.save();
|
|
ctx.fillStyle = '#00ffcc';
|
|
ctx.shadowColor = '#00ffcc';
|
|
ctx.shadowBlur = 9;
|
|
for (let i = 0; i < 12; i++) {
|
|
const a = (i / 12) * Math.PI * 2;
|
|
ctx.globalAlpha = i % 2 === 0 ? 0.80 : 0.50;
|
|
ctx.beginPath();
|
|
ctx.arc(cx + Math.cos(a) * r * 0.91, cy + Math.sin(a) * r * 0.91, i % 2 === 0 ? 4 : 2.5, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
ctx.restore();
|
|
|
|
ctx.save();
|
|
ctx.globalAlpha = 1.0;
|
|
ctx.fillStyle = '#ffffff';
|
|
ctx.shadowColor = '#88ddff';
|
|
ctx.shadowBlur = 18;
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, 5, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
ctx.restore();
|
|
|
|
return canvas;
|
|
}
|
|
|
|
const sigilTexture = new THREE.CanvasTexture(drawSigilCanvas());
|
|
|
|
export const sigilMat = new THREE.MeshBasicMaterial({
|
|
map: sigilTexture,
|
|
transparent: true,
|
|
opacity: 0.80,
|
|
depthWrite: false,
|
|
blending: THREE.AdditiveBlending,
|
|
side: THREE.DoubleSide,
|
|
});
|
|
|
|
export const sigilMesh = new THREE.Mesh(
|
|
new THREE.CircleGeometry(SIGIL_RADIUS, 128),
|
|
sigilMat
|
|
);
|
|
sigilMesh.rotation.x = -Math.PI / 2;
|
|
sigilMesh.position.y = 0.010;
|
|
sigilMesh.userData.zoomLabel = 'Timmy Sigil';
|
|
scene.add(sigilMesh);
|
|
|
|
export const sigilRing1Mat = new THREE.MeshBasicMaterial({
|
|
color: 0x00ccff, transparent: true, opacity: 0.45, depthWrite: false, blending: THREE.AdditiveBlending,
|
|
});
|
|
export const sigilRing1 = new THREE.Mesh(
|
|
new THREE.TorusGeometry(SIGIL_RADIUS * 0.965, 0.025, 6, 96), sigilRing1Mat
|
|
);
|
|
sigilRing1.rotation.x = Math.PI / 2;
|
|
sigilRing1.position.y = 0.012;
|
|
scene.add(sigilRing1);
|
|
|
|
export const sigilRing2Mat = new THREE.MeshBasicMaterial({
|
|
color: 0xffd700, transparent: true, opacity: 0.40, depthWrite: false, blending: THREE.AdditiveBlending,
|
|
});
|
|
export const sigilRing2 = new THREE.Mesh(
|
|
new THREE.TorusGeometry(SIGIL_RADIUS * 0.62, 0.020, 6, 72), sigilRing2Mat
|
|
);
|
|
sigilRing2.rotation.x = Math.PI / 2;
|
|
sigilRing2.position.y = 0.013;
|
|
scene.add(sigilRing2);
|
|
|
|
export const sigilRing3Mat = new THREE.MeshBasicMaterial({
|
|
color: 0x00ffcc, transparent: true, opacity: 0.35, depthWrite: false, blending: THREE.AdditiveBlending,
|
|
});
|
|
export const sigilRing3 = new THREE.Mesh(
|
|
new THREE.TorusGeometry(SIGIL_RADIUS * 0.78, 0.018, 6, 80), sigilRing3Mat
|
|
);
|
|
sigilRing3.rotation.x = Math.PI / 2;
|
|
sigilRing3.position.y = 0.011;
|
|
scene.add(sigilRing3);
|
|
|
|
export const sigilLight = new THREE.PointLight(0x0088ff, 0.4, 8);
|
|
sigilLight.position.set(0, 0.5, 0);
|
|
scene.add(sigilLight);
|