Spaces:
Running
Running
File size: 11,875 Bytes
510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa 510b5fb 76bbcfa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /* ============================================================================
* a11oy_cathedral.js — "The Sovereign Lattice" front-door hero (vendored Three.js).
* A luminous gold lattice-core (a11oy) with three governed faculties orbiting it,
* in deep sovereign space. Cinematic: layered additive bloom, multi-depth starfield,
* faint nebula, pointer parallax, scroll-driven camera. Live /healthz + Λ.
* Honesty: locked proven = 5; Λ = Conjecture 1. No fakery; honest fallback.
* Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
* ========================================================================== */
import * as THREE from 'three';
const REDUCED = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const canvas = document.getElementById('scene');
const renderer = new THREE.WebGLRenderer({ canvas, antialias:true, alpha:false, powerPreference:'high-performance' });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.9));
renderer.setSize(innerWidth, innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.18;
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x05060f, 0.0016);
const camera = new THREE.PerspectiveCamera(46, innerWidth/innerHeight, 0.1, 6000);
// composition: core sits on the right; hero text breathes on the left
const HOME = new THREE.Vector3(46, 16, 250);
const LOOK = new THREE.Vector3(64, 6, 0);
camera.position.copy(HOME);
scene.add(new THREE.AmbientLight(0x2a2942, 0.85));
const keyLight = new THREE.PointLight(0xffe2a0, 2.7, 2200, 1.5); keyLight.position.set(70, 30, 40); scene.add(keyLight);
const rim = new THREE.DirectionalLight(0x6d8fd6, 0.55); rim.position.set(-220, 120, -120); scene.add(rim);
const fill = new THREE.DirectionalLight(0x7fd6d1, 0.18); fill.position.set(120, -80, 120); scene.add(fill);
/* ---- faint nebula: large additive gradient sprites ---- */
function radialSprite(c1, c2, sz, op){
const cv=document.createElement('canvas'); cv.width=cv.height=256; const g=cv.getContext('2d');
const grd=g.createRadialGradient(128,128,0,128,128,128);
grd.addColorStop(0,c1); grd.addColorStop(0.45,c2); grd.addColorStop(1,'rgba(0,0,0,0)');
g.fillStyle=grd; g.fillRect(0,0,256,256);
const tex=new THREE.CanvasTexture(cv);
const s=new THREE.Sprite(new THREE.SpriteMaterial({ map:tex, transparent:true, opacity:op, blending:THREE.AdditiveBlending, depthWrite:false }));
s.scale.set(sz,sz,1); return s;
}
const neb1=radialSprite('rgba(215,185,107,0.5)','rgba(120,90,30,0.18)',1500,0.5); neb1.position.set(160,40,-500); scene.add(neb1);
const neb2=radialSprite('rgba(60,80,150,0.5)','rgba(30,40,90,0.16)',1900,0.45); neb2.position.set(-380,-120,-820); scene.add(neb2);
const neb3=radialSprite('rgba(127,214,209,0.32)','rgba(20,90,90,0.1)',1000,0.4); neb3.position.set(-180,180,-360); scene.add(neb3);
/* ---- multi-depth starfield ---- */
function starLayer(n, rMin, rMax, size, col, op){
const pos=new Float32Array(n*3);
for(let i=0;i<n;i++){ const r=rMin+Math.random()*(rMax-rMin), t=Math.random()*Math.PI*2, p=Math.acos(2*Math.random()-1);
pos[i*3]=r*Math.sin(p)*Math.cos(t); pos[i*3+1]=r*Math.cos(p); pos[i*3+2]=r*Math.sin(p)*Math.sin(t); }
const g=new THREE.BufferGeometry(); g.setAttribute('position', new THREE.BufferAttribute(pos,3));
const pts=new THREE.Points(g, new THREE.PointsMaterial({ color:col, size, sizeAttenuation:true, transparent:true, opacity:op, depthWrite:false }));
scene.add(pts); return pts;
}
const stars1=starLayer(1700, 700, 1500, 1.1, 0x9aa0d0, 0.55);
const stars2=starLayer(1100, 1400, 2800, 2.2, 0xcdbb8e, 0.5);
const stars3=starLayer(500, 380, 900, 1.5, 0x7fd6d1, 0.32);
/* ---- the sovereign lattice-core (a11oy) ---- */
const core=new THREE.Group(); core.position.copy(LOOK); scene.add(core);
const coreMesh=new THREE.Mesh(new THREE.IcosahedronGeometry(34,5),
new THREE.MeshStandardMaterial({ color:0xffcf73, emissive:0xe0a536, emissiveIntensity:1.55, roughness:0.34, metalness:0.12, flatShading:true }));
core.add(coreMesh);
const latticeA=new THREE.Mesh(new THREE.IcosahedronGeometry(44,2),
new THREE.MeshBasicMaterial({ color:0xffe6a8, wireframe:true, transparent:true, opacity:0.26 }));
core.add(latticeA);
const latticeB=new THREE.Mesh(new THREE.IcosahedronGeometry(56,1),
new THREE.MeshBasicMaterial({ color:0xd7b96b, wireframe:true, transparent:true, opacity:0.12 }));
core.add(latticeB);
// layered additive bloom halos
for(const [r,o] of [[64,0.16],[92,0.09],[140,0.05]]){
const h=new THREE.Mesh(new THREE.SphereGeometry(r,32,32),
new THREE.MeshBasicMaterial({ color:0xffd98a, transparent:true, opacity:o, side:THREE.BackSide, blending:THREE.AdditiveBlending, depthWrite:false }));
core.add(h);
}
const flare=radialSprite('rgba(255,222,150,0.95)','rgba(224,165,54,0.35)',360,0.9); core.add(flare);
// inner sparks orbiting inside the core
const sparkN=80, sparkPos=new Float32Array(sparkN*3), sparkSeed=[];
for(let i=0;i<sparkN;i++){ sparkSeed.push({a:Math.random()*Math.PI*2,b:Math.random()*Math.PI,r:20+Math.random()*22,s:0.4+Math.random()}); }
const sparkGeo=new THREE.BufferGeometry(); sparkGeo.setAttribute('position',new THREE.BufferAttribute(sparkPos,3));
core.add(new THREE.Points(sparkGeo, new THREE.PointsMaterial({ color:0xfff0c8, size:1.7, transparent:true, opacity:0.85, blending:THREE.AdditiveBlending, depthWrite:false })));
/* ---- three orbiting faculties ---- */
const FAC=[
{color:0x7fd6d1, angle:0.2, rx:120, rz:120, tilt:0.12},
{color:0xd7b96b, angle:2.25, rx:138, rz:128, tilt:-0.22},
{color:0xe58e54, angle:4.2, rx:128, rz:140, tilt:0.30},
];
const facBodies=[];
FAC.forEach((f)=>{
const g=new THREE.Group();
const m=new THREE.Mesh(new THREE.IcosahedronGeometry(11,2),
new THREE.MeshStandardMaterial({ color:f.color, emissive:f.color, emissiveIntensity:0.7, roughness:0.45, metalness:0.25 }));
g.add(m);
const ring=new THREE.Mesh(new THREE.TorusGeometry(17,0.5,8,72),
new THREE.MeshBasicMaterial({ color:f.color, transparent:true, opacity:0.4 })); ring.rotation.x=Math.PI/2; g.add(ring);
g.add(radialSprite(`rgba(${(f.color>>16)&255},${(f.color>>8)&255},${f.color&255},0.8)`,'rgba(0,0,0,0)',70,0.7));
scene.add(g);
// curved tether (core -> faculty) as a tube we rebuild each frame
const tetherMat=new THREE.MeshBasicMaterial({ color:f.color, transparent:true, opacity:0.16, blending:THREE.AdditiveBlending, depthWrite:false });
let tetherMesh=null;
facBodies.push({ f, g, m, tetherMat, get tether(){return tetherMesh;}, set tether(v){tetherMesh=v;} });
});
/* ---- live mesh: /healthz + Λ ---- */
const lamDot=document.getElementById('lam-dot'), lamText=document.getElementById('lam-text');
async function getJSON(url,ms){ const c=new AbortController(); const t=setTimeout(()=>c.abort(),ms||7000);
try{ const r=await fetch(url,{signal:c.signal,headers:{accept:'application/json'}}); if(!r.ok) throw 0; return await r.json(); } finally{ clearTimeout(t); } }
let lambda=null, alive=false;
async function poll(){
try{ const h=await getJSON('/healthz',7000); alive=!!(h&&(h.status==='ok'||h.ok)); }catch(_){ alive=false; }
try{ const l=await getJSON('/api/a11oy/v1/lambda',7000); if(l&&typeof l.lambda==='number') lambda=l.lambda; }catch(_){}
paint();
}
function paint(){
if(!lamDot) return;
if(alive){ lamDot.classList.add('live'); }else{ lamDot.classList.remove('live'); }
if(lambda!==null) lamText.innerHTML = `<b>Λ ${lambda.toFixed(3)}</b> · live trust score · 13-axis`;
else lamText.textContent = alive ? 'mesh live · Λ reconnecting…' : 'live mesh offline · honest fallback';
}
paint(); poll(); setInterval(poll, 8000);
/* ---- pointer parallax + scroll ---- */
let px=0, py=0, tpx=0, tpy=0;
if(!REDUCED) addEventListener('pointermove',(e)=>{ tpx=(e.clientX/innerWidth-0.5); tpy=(e.clientY/innerHeight-0.5); }, {passive:true});
let scrollP=0;
function onScroll(){ const max=document.body.scrollHeight-innerHeight; scrollP = max>0 ? Math.min(1, scrollY/max) : 0; }
addEventListener('scroll', onScroll, {passive:true}); onScroll();
/* ---- intro dolly ---- */
let intro=REDUCED?1:0;
const _v=new THREE.Vector3();
const _look=new THREE.Vector3();
let t=0;
function animate(){
requestAnimationFrame(animate);
t+=0.0045;
if(intro<1){ intro=Math.min(1, intro+0.012); }
const e=intro<1 ? 1-Math.pow(1-intro,3) : 1; // easeOutCubic
// core motion
coreMesh.rotation.y+=0.0016; coreMesh.rotation.x+=0.0005;
latticeA.rotation.y-=0.0012; latticeA.rotation.x+=0.0007;
latticeB.rotation.y+=0.0009; latticeB.rotation.z-=0.0006;
const pulse=1+0.012*Math.sin(t*2.0); core.scale.setScalar(pulse);
flare.material.opacity=0.78+0.12*Math.sin(t*1.7);
// inner sparks
for(let i=0;i<sparkN;i++){ const s=sparkSeed[i]; const a=s.a+t*s.s; const b=s.b+t*0.3*s.s; const r=s.r;
sparkPos[i*3]=r*Math.sin(b)*Math.cos(a); sparkPos[i*3+1]=r*Math.cos(b); sparkPos[i*3+2]=r*Math.sin(b)*Math.sin(a); }
sparkGeo.attributes.position.needsUpdate=true;
// faculties orbit + curved tethers
facBodies.forEach((fb,i)=>{
const f=fb.f; const a=f.angle + t*0.22*(i%2?-1:1);
const x=LOOK.x+Math.cos(a)*f.rx;
const z=LOOK.z+Math.sin(a)*f.rz;
const y=LOOK.y+Math.sin(a*1.4+i)*26 + f.tilt*40*Math.cos(a*0.7);
fb.g.position.set(x,y,z); fb.m.rotation.y+=0.012;
const mid=new THREE.Vector3((LOOK.x+x)/2,(LOOK.y+y)/2+24,(LOOK.z+z)/2);
const curve=new THREE.QuadraticBezierCurve3(new THREE.Vector3(LOOK.x,LOOK.y,LOOK.z), mid, new THREE.Vector3(x,y,z));
if(fb.tether){ fb.tether.geometry.dispose(); core.parent.remove(fb.tether); }
fb.tether=new THREE.Mesh(new THREE.TubeGeometry(curve,20,0.45,6,false), fb.tetherMat); scene.add(fb.tether);
});
// starfield drift
stars1.rotation.y+=0.00012; stars2.rotation.y-=0.00007; stars3.rotation.y+=0.0002;
// camera: parallax + gentle idle + scroll framing.
// We aim LEFT of the core so it sits in the right third (text breathes on the
// left). As you scroll we push the core toward the right edge (no zoom-in that
// would swamp the headlines) and pull back slightly so it reads as an accent.
px+=(tpx-px)*0.04; py+=(tpy-py)*0.04;
const frameX = 60 + 84*scrollP; // aim offset: larger -> core further right
const frameY = 4 + 14*scrollP;
const camHome=HOME.clone();
camHome.y += 24*scrollP; // gentle lift
camHome.z += 30*scrollP; // pull back (core smaller) instead of zoom-in
camHome.x += 14*scrollP;
const idleX=Math.sin(t*0.5)*5, idleY=Math.cos(t*0.4)*4;
_v.set(camHome.x + px*32 + idleX, camHome.y - py*22 + idleY, camHome.z);
camera.position.lerp(_v, e<1?0.06*e+0.02:0.06);
const look=_look.set(LOOK.x - frameX + px*12, LOOK.y + frameY - py*6, LOOK.z);
camera.lookAt(look);
renderer.toneMappingExposure = 1.2 - 0.16*scrollP;
renderer.render(scene,camera);
if(!_shown){ _shown=true; requestAnimationFrame(()=>canvas.classList.add('on')); }
}
let _shown=false;
animate();
addEventListener('resize',()=>{ camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth,innerHeight); });
/* ---- nav stuck + scroll reveals + boot ---- */
const nav=document.getElementById('nav');
function navState(){ if(scrollY>24) nav.classList.add('stuck'); else nav.classList.remove('stuck'); }
addEventListener('scroll', navState, {passive:true}); navState();
const io=new IntersectionObserver((es)=>{ es.forEach(en=>{ if(en.isIntersecting){ en.target.classList.add('in'); io.unobserve(en.target); } }); }, {threshold:0.16});
document.querySelectorAll('.reveal').forEach((el,i)=>{ el.style.transitionDelay=(Math.min(i,6)*70)+'ms'; io.observe(el); });
/* boot overlay removed: editorial hero paints instantly; the 3D canvas fades in
on its first rendered frame (see #scene.on). Safety net if WebGL never starts: */
setTimeout(()=>canvas.classList.add('on'), 1800);
|