// hero3d.jsx — "The Eye of the Model": 4D hypercube projection + iris streamers // + central singularity. A symbol for godmodel — all-seeing intelligence. (function () { const PALETTES = { iridescent: { a: 0xc4b5fd, b: 0xa8e6ff, c: 0xfcd5b5, bg: 0x07070a }, obsidian: { a: 0xeeeae0, b: 0xb8b3a8, c: 0xffffff, bg: 0x050507 }, plasma: { a: 0xff4d8f, b: 0x8a2be2, c: 0x00e0ff, bg: 0x06030d }, aurora: { a: 0x6ee7b7, b: 0xa8e6ff, c: 0xc4b5fd, bg: 0x040b0a }, solar: { a: 0xfcd5b5, b: 0xff8a3d, c: 0xffe27a, bg: 0x0a0703 }, }; function createScene(canvas) { const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(34, window.innerWidth / window.innerHeight, 0.1, 100); camera.position.set(0, 0, 11); const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0x000000, 0); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 0.8; function makeEnvTexture(palette) { const w = 1024, h = 512; const cnv = document.createElement('canvas'); cnv.width = w; cnv.height = h; const ctx = cnv.getContext('2d'); const bg = '#' + palette.bg.toString(16).padStart(6, '0'); const a = '#' + palette.a.toString(16).padStart(6, '0'); const b = '#' + palette.b.toString(16).padStart(6, '0'); const c = '#' + palette.c.toString(16).padStart(6, '0'); const grad = ctx.createLinearGradient(0, 0, 0, h); grad.addColorStop(0, bg); grad.addColorStop(0.45, a); grad.addColorStop(0.55, b); grad.addColorStop(0.85, c); grad.addColorStop(1, bg); ctx.fillStyle = grad; ctx.fillRect(0, 0, w, h); [[w*0.3, h*0.4, 200, 'rgba(255,255,255,0.7)'], [w*0.7, h*0.55, 260, a]].forEach(([x,y,r,col])=>{ const g2 = ctx.createRadialGradient(x,y,0,x,y,r); g2.addColorStop(0,col); g2.addColorStop(1,'rgba(0,0,0,0)'); ctx.fillStyle=g2; ctx.fillRect(0,0,w,h); }); const tex = new THREE.CanvasTexture(cnv); tex.mapping = THREE.EquirectangularReflectionMapping; tex.colorSpace = THREE.SRGBColorSpace; return tex; } let envTex = makeEnvTexture(PALETTES.iridescent); scene.environment = envTex; scene.add(new THREE.AmbientLight(0xffffff, 0.3)); const key = new THREE.PointLight(0xc4b5fd, 30, 40); key.position.set(5,5,6); scene.add(key); const fill = new THREE.PointLight(0xa8e6ff, 20, 40); fill.position.set(-5,-3,4); scene.add(fill); const group = new THREE.Group(); scene.add(group); // ============================================================ // LAYER 1: 4D Hypercube (tesseract) — projected to 3D every frame // ============================================================ // 16 vertices of a 4D unit cube, edges connect when they differ in one coordinate. const verts4D = []; for (let i = 0; i < 16; i++) { verts4D.push([ (i & 1) ? 1 : -1, (i & 2) ? 1 : -1, (i & 4) ? 1 : -1, (i & 8) ? 1 : -1, ]); } const edges4D = []; for (let i = 0; i < 16; i++) { for (let j = i+1; j < 16; j++) { let diff = 0; for (let k = 0; k < 4; k++) if (verts4D[i][k] !== verts4D[j][k]) diff++; if (diff === 1) edges4D.push([i, j]); } } const tesseractGeo = new THREE.BufferGeometry(); const tesseractPos = new Float32Array(edges4D.length * 2 * 3); tesseractGeo.setAttribute('position', new THREE.BufferAttribute(tesseractPos, 3)); const tesseractMat = new THREE.LineBasicMaterial({ color: 0xc4b5fd, transparent: true, opacity: 0.55, blending: THREE.AdditiveBlending, depthWrite: false, }); const tesseractLines = new THREE.LineSegments(tesseractGeo, tesseractMat); group.add(tesseractLines); // Glowing vertex points const vGeo = new THREE.BufferGeometry(); const vPos = new Float32Array(16 * 3); vGeo.setAttribute('position', new THREE.BufferAttribute(vPos, 3)); const vMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.08, transparent: true, opacity: 0.9, blending: THREE.AdditiveBlending, depthWrite: false, sizeAttenuation: true, }); const tesseractPoints = new THREE.Points(vGeo, vMat); group.add(tesseractPoints); function project4D(t) { // Rotation in XW + YW planes (the "true" 4D rotation) const a = t * 0.4; const b = t * 0.3; const cosA = Math.cos(a), sinA = Math.sin(a); const cosB = Math.cos(b), sinB = Math.sin(b); const TR = 1.4; // size const proj = verts4D.map(([x,y,z,w]) => { // XW rotation let nx = x * cosA - w * sinA; let nw = x * sinA + w * cosA; // YW rotation let ny = y * cosB - nw * sinB; nw = y * sinB + nw * cosB; // Perspective project from 4D to 3D (w as depth) const dist = 3; const factor = 1 / (dist - nw); return [nx * factor * TR, ny * factor * TR, z * factor * TR]; }); // Fill edge buffer edges4D.forEach(([i, j], idx) => { const p1 = proj[i], p2 = proj[j]; tesseractPos[idx*6+0] = p1[0]; tesseractPos[idx*6+1] = p1[1]; tesseractPos[idx*6+2] = p1[2]; tesseractPos[idx*6+3] = p2[0]; tesseractPos[idx*6+4] = p2[1]; tesseractPos[idx*6+5] = p2[2]; }); tesseractGeo.attributes.position.needsUpdate = true; // Fill point buffer proj.forEach((p, i) => { vPos[i*3] = p[0]; vPos[i*3+1] = p[1]; vPos[i*3+2] = p[2]; }); vGeo.attributes.position.needsUpdate = true; } // ============================================================ // LAYER 2: The Eye — iris streamers (radial line strands) // ============================================================ const irisGroup = new THREE.Group(); group.add(irisGroup); const STRANDS = 64; const irisMat = new THREE.LineBasicMaterial({ color: 0xa8e6ff, transparent: true, opacity: 0.4, blending: THREE.AdditiveBlending, depthWrite: false, }); const strands = []; for (let i = 0; i < STRANDS; i++) { const ang = (i / STRANDS) * Math.PI * 2; const inner = 1.7 + Math.random() * 0.2; const outer = 2.5 + Math.random() * 0.6; const wob = Math.random() * 0.15; const points = []; const segs = 24; for (let s = 0; s <= segs; s++) { const t = s / segs; const r = inner + (outer - inner) * t; const a = ang + Math.sin(t * Math.PI * 2 + i) * wob; points.push(new THREE.Vector3(Math.cos(a) * r, Math.sin(a) * r, 0)); } const g = new THREE.BufferGeometry().setFromPoints(points); const line = new THREE.Line(g, irisMat.clone()); irisGroup.add(line); strands.push({ line, baseAng: ang, inner, outer, wob, points }); } // ============================================================ // LAYER 3: The Pupil — black sphere with thin glowing rim // ============================================================ const pupilGeo = new THREE.SphereGeometry(0.6, 64, 64); const pupilMat = new THREE.MeshBasicMaterial({ color: 0x000000 }); const pupil = new THREE.Mesh(pupilGeo, pupilMat); group.add(pupil); // Rim ring const rimGeo = new THREE.RingGeometry(0.6, 0.62, 96); const rimMat = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.95, side: THREE.DoubleSide, }); const rim = new THREE.Mesh(rimGeo, rimMat); group.add(rim); // Outer accent ring (subtle) const outerRingGeo = new THREE.TorusGeometry(2.7, 0.004, 8, 200); const outerRingMat = new THREE.MeshBasicMaterial({ color: 0xc4b5fd, transparent: true, opacity: 0.3, }); const outerRing = new THREE.Mesh(outerRingGeo, outerRingMat); outerRing.rotation.x = Math.PI / 2; group.add(outerRing); // ============================================================ // LAYER 4: Singularity light streams pulled toward pupil // ============================================================ const STREAM_COUNT = 600; const sGeo = new THREE.BufferGeometry(); const sPos = new Float32Array(STREAM_COUNT * 3); const sSeed = new Float32Array(STREAM_COUNT); for (let i = 0; i < STREAM_COUNT; i++) { const r = 0.6 + Math.random() * 3.5; const ang = Math.random() * Math.PI * 2; sPos[i*3] = Math.cos(ang) * r; sPos[i*3+1] = Math.sin(ang) * r; sPos[i*3+2] = (Math.random() - 0.5) * 0.4; sSeed[i] = Math.random(); } sGeo.setAttribute('position', new THREE.BufferAttribute(sPos, 3)); sGeo.setAttribute('aSeed', new THREE.BufferAttribute(sSeed, 1)); const sMat = new THREE.ShaderMaterial({ uniforms: { uTime: { value: 0 }, uColorA: { value: new THREE.Color(0xc4b5fd) }, uColorB: { value: new THREE.Color(0xa8e6ff) }, }, vertexShader: ` attribute float aSeed; uniform float uTime; varying float vSeed; varying float vR; void main() { vSeed = aSeed; vec3 p = position; // pull toward center (singularity), then respawn at outer when crosses pupil float r0 = length(p.xy); float speed = 0.35 + aSeed * 0.4; float phase = mod(uTime * speed + aSeed * 6.28, 6.28); // travel inward in 0..2 timeline, holding outer when phase > some threshold float life = mod(phase, 6.28) / 6.28; float r = mix(r0, 0.55, smoothstep(0.0, 1.0, life)); float ang = atan(p.y, p.x) + life * 1.8 * (aSeed - 0.5); vec3 np = vec3(cos(ang) * r, sin(ang) * r, p.z * (1.0 - life)); vR = r; vec4 mv = modelViewMatrix * vec4(np, 1.0); gl_Position = projectionMatrix * mv; gl_PointSize = (1.5 + aSeed * 2.0) * (300.0 / -mv.z) * (1.0 - life * 0.6); } `, fragmentShader: ` uniform vec3 uColorA; uniform vec3 uColorB; varying float vSeed; varying float vR; void main() { vec2 c = gl_PointCoord - 0.5; float d = length(c); if (d > 0.5) discard; float alpha = smoothstep(0.5, 0.0, d) * 0.7; vec3 col = mix(uColorA, uColorB, vSeed); // brighten near pupil edge float glow = smoothstep(1.2, 0.6, vR); col += glow * 0.5; gl_FragColor = vec4(col, alpha); } `, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, }); const streams = new THREE.Points(sGeo, sMat); group.add(streams); // ============================================================ // LAYER 5: Distant star field // ============================================================ const starGeo = new THREE.BufferGeometry(); const STAR_COUNT = 400; const starPos = new Float32Array(STAR_COUNT * 3); for (let i = 0; i < STAR_COUNT; i++) { const r = 6 + Math.random() * 6; const theta = Math.random() * Math.PI * 2; const phi = Math.acos(2 * Math.random() - 1); starPos[i*3] = r * Math.sin(phi) * Math.cos(theta); starPos[i*3+1] = r * Math.sin(phi) * Math.sin(theta); starPos[i*3+2] = r * Math.cos(phi); } starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3)); const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.02, transparent: true, opacity: 0.45, blending: THREE.AdditiveBlending, depthWrite: false, sizeAttenuation: true, }); const stars = new THREE.Points(starGeo, starMat); scene.add(stars); function applyPalette(palette) { key.color.setHex(palette.a); fill.color.setHex(palette.b); tesseractMat.color.setHex(palette.a); irisGroup.children.forEach((line, i) => { const c = i % 2 === 0 ? palette.a : palette.b; line.material.color.setHex(c); }); outerRingMat.color.setHex(palette.a); sMat.uniforms.uColorA.value.setHex(palette.a); sMat.uniforms.uColorB.value.setHex(palette.b); if (envTex) envTex.dispose(); envTex = makeEnvTexture(palette); scene.environment = envTex; } let state = { paletteName: 'iridescent', autoRotate: true }; applyPalette(PALETTES[state.paletteName]); // Interaction const target = { rx: 0, ry: 0, scroll: 0 }; const current = { rx: 0, ry: 0, scroll: 0 }; let mouseX = 0, mouseY = 0; let dragging = false; let dragStart = { x: 0, y: 0 }; let dragRot = { x: 0, y: 0 }; window.addEventListener('mousemove', (e) => { if (!dragging) { mouseX = (e.clientX / window.innerWidth - 0.5); mouseY = (e.clientY / window.innerHeight - 0.5); } }); canvas.addEventListener('pointerdown', (e) => { dragging = true; canvas.classList.add('dragging'); canvas.setPointerCapture(e.pointerId); dragStart.x = e.clientX; dragStart.y = e.clientY; dragRot.x = target.rx; dragRot.y = target.ry; }); canvas.addEventListener('pointermove', (e) => { if (!dragging) return; target.ry = dragRot.y + (e.clientX - dragStart.x) / 100; target.rx = dragRot.x + (e.clientY - dragStart.y) / 100; }); const endDrag = (e) => { if (!dragging) return; dragging = false; canvas.classList.remove('dragging'); try { canvas.releasePointerCapture(e.pointerId); } catch (err) {} }; canvas.addEventListener('pointerup', endDrag); canvas.addEventListener('pointercancel', endDrag); window.addEventListener('scroll', () => { target.scroll = window.scrollY; }, { passive: true }); window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); let t = 0; let rafId = null; function loop() { t += 0.005; current.rx += (target.rx - current.rx) * 0.05; current.ry += (target.ry - current.ry) * 0.05; current.scroll += (target.scroll - current.scroll) * 0.08; sMat.uniforms.uTime.value = t; // Update tesseract project4D(t); const auto = state.autoRotate ? t * 0.18 : 0; group.rotation.x = current.rx + mouseY * 0.25 + Math.sin(t * 0.5) * 0.02; group.rotation.y = current.ry + auto + mouseX * 0.25; // Iris breathing strands.forEach((s, i) => { const breath = 1 + Math.sin(t * 0.8 + i * 0.1) * 0.04; s.line.scale.setScalar(breath); s.line.material.opacity = 0.25 + Math.sin(t * 1.5 + i * 0.2) * 0.2; }); irisGroup.rotation.z = t * 0.05; // Pupil pulse const pulse = 1 + Math.sin(t * 1.5) * 0.04; pupil.scale.setScalar(pulse); rim.scale.setScalar(pulse); rimMat.opacity = 0.85 + Math.sin(t * 2) * 0.1; // Outer ring slow spin outerRing.rotation.z = t * 0.08; outerRingMat.opacity = 0.2 + Math.sin(t * 0.6) * 0.1; // Star slow drift stars.rotation.y = t * 0.02; const scrollNorm = Math.min(current.scroll / window.innerHeight, 1.5); const scale = 0.85 - scrollNorm * 0.15; group.scale.setScalar(scale); group.position.y = 0.5 - current.scroll * 0.001 + Math.sin(t * 0.6) * 0.03; camera.position.x = mouseX * 0.4; camera.position.y = -mouseY * 0.25; camera.lookAt(0, group.position.y, 0); renderer.render(scene, camera); rafId = requestAnimationFrame(loop); } loop(); return { setVariant() {}, setPalette(p) { if (!PALETTES[p]) return; state.paletteName = p; applyPalette(PALETTES[p]); }, setAutoRotate(v) { state.autoRotate = !!v; }, destroy() { if (rafId) cancelAnimationFrame(rafId); renderer.dispose(); }, PALETTES, }; } window.HeroScene = { create: createScene, PALETTES }; })();