[claude] Animated Timmy sigil on the floor — sacred geometry that glows (#260) #337

Merged
Timmy merged 1 commits from claude/issue-260 into main 2026-03-24 05:10:07 +00:00

231
app.js
View File

@@ -618,6 +618,226 @@ async function updateHeatmap() {
updateHeatmap();
setInterval(updateHeatmap, HEATMAP_REFRESH_MS);
// === TIMMY SIGIL ===
// Animated sacred-geometry floor sigil — Metatron's Cube / Flower of Life.
// Three layers: painted canvas base + three counter-rotating glow rings.
const SIGIL_CANVAS_SIZE = 512;
const SIGIL_RADIUS = 3.8; // world-space radius (fits inside the glass platform)
/**
* Draws the Timmy sigil onto a canvas and returns it.
* Pattern: Flower of Life circles, dual hexagram triangles, concentric rings, 12-spoke radials.
* @returns {HTMLCanvasElement}
*/
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; // outer drawing radius
ctx.clearRect(0, 0, SIGIL_CANVAS_SIZE, SIGIL_CANVAS_SIZE);
// Faint radial background glow
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);
/** Draws a single circle outline with glow. */
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();
}
/** Draws two interlocking triangles (Star of David). */
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;
// Upward-pointing triangle
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();
// Downward-pointing triangle
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; // radius of each Flower-of-Life petal
// Flower of Life — center circle
glowCircle(cx, cy, petalR, '#00ccff', 0.65, 1.0);
// First ring of 6 petals
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);
}
// Second ring of 6 petals (rotated 30°)
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);
}
// Dual hexagram — outer (gold) and inner (amber)
hexagram(cx, cy, r * 0.62, '#ffd700', 0.75);
hexagram(cx, cy, r * 0.41, '#ffaa00', 0.50);
// Concentric rings
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);
// 12-spoke radial lines
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();
// Dot marks at spoke tips
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();
// Center point of light
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());
const sigilMat = new THREE.MeshBasicMaterial({
map: sigilTexture,
transparent: true,
opacity: 0.80,
depthWrite: false,
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide,
});
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);
// Outer glow ring — rotates clockwise
const sigilRing1Mat = new THREE.MeshBasicMaterial({
color: 0x00ccff,
transparent: true,
opacity: 0.45,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
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);
// Middle ring — rotates counter-clockwise, gold
const sigilRing2Mat = new THREE.MeshBasicMaterial({
color: 0xffd700,
transparent: true,
opacity: 0.40,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
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);
// Inner ring — rotates clockwise, teal
const sigilRing3Mat = new THREE.MeshBasicMaterial({
color: 0x00ffcc,
transparent: true,
opacity: 0.35,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
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);
// Subtle floor glow light below the sigil
const sigilLight = new THREE.PointLight(0x0088ff, 0.4, 8);
sigilLight.position.set(0, 0.5, 0);
scene.add(sigilLight);
// === MOUSE-DRIVEN ROTATION ===
let mouseX = 0;
let mouseY = 0;
@@ -1078,6 +1298,17 @@ function animate() {
// Heatmap floor: subtle breathing glow
heatmapMat.opacity = 0.75 + Math.sin(elapsed * 0.6) * 0.2;
// Animate Timmy sigil — base pulses, rings counter-rotate
sigilMesh.rotation.z = elapsed * 0.04;
sigilRing1.rotation.z = elapsed * 0.06;
sigilRing2.rotation.z = -elapsed * 0.10;
sigilRing3.rotation.z = elapsed * 0.08;
sigilMat.opacity = 0.65 + Math.sin(elapsed * 1.3) * 0.18;
sigilRing1Mat.opacity = 0.38 + Math.sin(elapsed * 0.9) * 0.14;
sigilRing2Mat.opacity = 0.32 + Math.sin(elapsed * 1.6 + 1.2) * 0.12;
sigilRing3Mat.opacity = 0.28 + Math.sin(elapsed * 0.7 + 2.4) * 0.10;
sigilLight.intensity = 0.30 + Math.sin(elapsed * 1.1) * 0.15;
// Animate procedural clouds
cloudMaterial.uniforms.uTime.value = elapsed;