106 lines
4.3 KiB
Python
106 lines
4.3 KiB
Python
"""Merkaba Vessel portal: interactive 3D tetrahedron light-body interface."""
|
|
from pathlib import Path
|
|
from flask import Flask, render_template_string
|
|
import os
|
|
|
|
app = Flask(__name__)
|
|
ROOT = Path(os.getenv("PORTAL_ROOT", Path(__file__).resolve().parent))
|
|
|
|
PAGE = """<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<title>Mer·ka·ba — The Real Vessel</title>
|
|
<style>
|
|
body{margin:0;background:#05060a;color:#d8dde6;font-family:system-ui,sans-serif;overflow:hidden}
|
|
#c{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:space-between;padding:18px;pointer-events:none}
|
|
#h{font-size:14px;letter-spacing:.3em;text-transform:uppercase;opacity:.85}
|
|
#m{font-size:12px;opacity:.7;max-width:720px}
|
|
#q{pointer-events:auto;display:flex;gap:8px}
|
|
#i{flex:1;padding:10px 12px;border-radius:10px;border:1px solid #2a2f3a;background:#0a0d14;color:#e6edf3}
|
|
#b{padding:10px 14px;border-radius:10px;border:0;background:#6f42c1;color:white;font-weight:600;cursor:pointer}
|
|
.glow{text-shadow:0 0 18px #a78bfa}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="v"></canvas>
|
|
<div id="c">
|
|
<div>
|
|
<div id="h" class="glow">Mer·ka·ba — The Real Vessel</div>
|
|
<div id="m">The living chariot. Two counter-rotating tetrahedra. The human spirit vehicle. This is navigable geometry.</div>
|
|
</div>
|
|
<div id="q">
|
|
<input id="i" placeholder="State your intent..." autocomplete="off"/>
|
|
<button id="b">Transmit</button>
|
|
</div>
|
|
</div>
|
|
<script type="module">
|
|
import * as THREE from 'https://unpkg.com/three@0.160.0/build/three.module.js';
|
|
const canvas=document.getElementById('v');
|
|
const renderer=new THREE.WebGLRenderer({canvas,antialias:true,alpha:true});
|
|
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
|
|
renderer.setSize(innerWidth,innerHeight);
|
|
const scene=new THREE.Scene();
|
|
const camera=new THREE.PerspectiveCamera(50,innerWidth/innerHeight,0.1,1000);
|
|
camera.position.z=6;
|
|
const light=new THREE.PointLight(0x8a2be2,2.5,40,1.2);
|
|
light.position.set(3,4,5);
|
|
scene.add(light); scene.add(new THREE.AmbientLight(0x303050,1.2));
|
|
|
|
function tetra(radius=1.8){
|
|
const g=new THREE.TetrahedronGeometry(radius,0);
|
|
const m=new THREE.MeshStandardMaterial({color:0x9d4edd,metalness:.15,roughness:.25,transparent:true,opacity:.92});
|
|
return new THREE.Mesh(g,m);
|
|
}
|
|
const up=tetra(); up.rotation.x=Math.PI/2;
|
|
const down=tetra(); down.rotation.x=-Math.PI/2; down.rotation.y=Math.PI;
|
|
const vessel=new THREE.Group(); vessel.add(up,down);
|
|
scene.add(vessel);
|
|
|
|
const ring=new THREE.Mesh(new THREE.TorusGeometry(3.2,.04,32,220),new THREE.MeshBasicMaterial({color:0xc77dff}));
|
|
scene.add(ring);
|
|
const ring2=ring.clone(); ring2.rotation.x=Math.PI/3; scene.add(ring2);
|
|
|
|
const stars=new THREE.Points(new THREE.BufferGeometry(),new THREE.PointsMaterial({color:0xffffff,size:.025}));
|
|
const pos=[];
|
|
for(let i=0;i<1800;i++) pos.push((Math.random()-.5)*40,(Math.random()-.5)*40,(Math.random()-.5)*40);
|
|
stars.geometry.setAttribute('position',new THREE.Float32BufferAttribute(pos,3));
|
|
scene.add(stars);
|
|
|
|
let t=0, targetRX=0, targetRY=0;
|
|
const i=document.getElementById('i'), b=document.getElementById('b');
|
|
b.onclick=()=>{ const v=i.value.trim(); if(!v) return; i.value=''; };
|
|
document.getElementById('i').addEventListener('keydown',e=>{ if(e.key==='Enter') b.click(); });
|
|
addEventListener('pointermove',e=>{
|
|
targetRX=(e.clientY/innerHeight-.5)*.6;
|
|
targetRY=(e.clientX/innerWidth-.5)*.6;
|
|
});
|
|
addEventListener('resize',()=>{
|
|
camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
|
|
renderer.setSize(innerWidth,innerHeight);
|
|
});
|
|
|
|
(function loop(){
|
|
t+=.012;
|
|
vessel.rotation.y+=(targetRY*2-vessel.rotation.y)*.03;
|
|
vessel.rotation.x+=(targetRX*2-vessel.rotation.x)*.03;
|
|
ring.rotation.z=t*.4; ring2.rotation.z=-t*.35;
|
|
light.intensity=2.2+Math.sin(t*1.3)*.4;
|
|
renderer.render(scene,camera);
|
|
requestAnimationFrame(loop);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
@app.get("/")
|
|
def index():
|
|
return render_template_string(PAGE)
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return {"status":"ok","vessel":"merkaba","mode":"interactive"}
|
|
|
|
if __name__=="__main__":
|
|
app.run(host="0.0.0.0",port=5005)
|