Spaces:
Running
Running
| /* global THREE, feather */ | |
| class TimeMachine { | |
| constructor() { | |
| this.kappa = 1.273; | |
| this.now = Date.now(); | |
| this.targetYear = 1985; | |
| this.scene = new THREE.Scene(); | |
| this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); | |
| this.torus = null; | |
| this.particles = null; | |
| this.clock = new THREE.Clock(); | |
| // TOPOLOGICAL MAP STATE | |
| this.timewaveCanvas = document.getElementById('timewave-canvas'); | |
| this.timewaveCtx = this.timewaveCanvas.getContext('2d'); | |
| this.eschatonDistance = 100; // % | |
| this.semanticQubits = ['concrete', 'fear', 'Giza pyramid', 'Mother', 'LOVE', 'JUNG', 'THE BROKEN GUITAR', 'Ω-HONK-DODECA', 'Canine-Olfactory-Timeline']; | |
| this.currentSemantic = 0; | |
| this.lithificationPressure = 0; | |
| this.concrescenceTriggered = false; | |
| // ENTANGLEMENT & GHOST CHAT | |
| this.isEntangled = false; | |
| this.memoryAnchors = JSON.parse(localStorage.getItem('chronoMemory') || '[]'); | |
| // NEW: Hat-Man Spectral API | |
| this.hatManAPI = new HatManDiracAPI(); | |
| // PHAROS INTEGRATION | |
| this.pharos = new PharosInterface(); | |
| this.pharos.init(); | |
| } | |
| init() { | |
| const container = document.getElementById('torus-container'); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.renderer.setPixelRatio(window.devicePixelRatio); | |
| container.appendChild(this.renderer.domElement); | |
| this.scene.background = null; | |
| // Lighting | |
| const ambient = new THREE.AmbientLight(0xffffff, 0.4); | |
| this.scene.add(ambient); | |
| const point = new THREE.PointLight(0x4f46e5, 1.2, 100); | |
| point.position.set(10, 15, 10); | |
| this.scene.add(point); | |
| // Torus | |
| const geometry = new THREE.TorusGeometry(10, 3.5, 16, 100); | |
| const material = new THREE.MeshStandardMaterial({ | |
| color: 0x6366f1, | |
| metalness: 0.7, | |
| roughness: 0.3, | |
| wireframe: true, | |
| }); | |
| this.torus = new THREE.Mesh(geometry, material); | |
| this.scene.add(this.torus); | |
| // Particle trails (past+future) | |
| const N = 1200; | |
| const vertices = new Float32Array(N * 3); | |
| for (let i = 0; i < N; i++) { | |
| const [x, y, z] = this.torusPosition(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, 11.5); | |
| vertices[i * 3] = x; | |
| vertices[i * 3 + 1] = y; | |
| vertices[i * 3 + 2] = z; | |
| } | |
| const geo = new THREE.BufferGeometry(); | |
| geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); | |
| const mat = new THREE.PointsMaterial({ | |
| size: 0.12, | |
| color: 0xec4899, | |
| blending: THREE.AdditiveBlending, | |
| transparent: true, | |
| opacity: 0.7, | |
| }); | |
| this.particles = new THREE.Points(geo, mat); | |
| this.scene.add(this.particles); | |
| this.camera.position.set(0, 8, 30); | |
| this.camera.lookAt(0, 0, 0); | |
| this.animate(); | |
| this.bindUI(); | |
| this.loadAnchors(); | |
| this.initTimewaveGraph(); | |
| this.startTopologicalAcceleration(); | |
| this.initHatManEndpoint(); | |
| torusPosition(u, v, scale) { | |
| return [ | |
| scale * Math.cos(u) * (3 + Math.cos(v)), | |
| scale * Math.sin(v), | |
| scale * Math.sin(u) * (3 + Math.cos(v)), | |
| ]; | |
| } | |
| animate() { | |
| requestAnimationFrame(() => this.animate()); | |
| const t = this.clock.getElapsedTime(); | |
| const scrollSpeed = Math.abs(window.lastWheelDelta || 0); | |
| const k = Math.max(1.0, Math.min(2.0, 1.273 + scrollSpeed * 0.0005)); | |
| this.kappa = k; | |
| this.torus.rotation.x = t * 0.1 * this.kappa; | |
| this.torus.rotation.y = t * 0.15 * this.kappa; | |
| this.particles.rotation.x = t * 0.05; | |
| this.particles.rotation.z = t * 0.1; | |
| // TOPOLOGICAL SHEARING: hallway geometry failure | |
| if (this.eschatonDistance < 20) { | |
| this.torus.rotation.z = Math.sin(t * 10) * 0.1; | |
| this.particles.rotation.y = Math.sin(t * 15) * 0.2; | |
| } | |
| // Golden spiral anchors | |
| if (this.memoryAnchors.length) { | |
| this.memoryAnchors.forEach((m) => { | |
| const angle = ((parseInt(m.year) % 100) / 100) * 2 * Math.PI; | |
| const spiralU = angle * (1 + Math.sqrt(5)) / (2 * Math.PI); | |
| const [x, y, z] = this.torusPosition(spiralU, angle, 12.5); | |
| if (m.dot) { | |
| m.dot.position.set(x, y, z); | |
| m.dot.material.opacity = Math.max(0.3, Math.sin(t * 2 + m.year) * 0.5 + 0.5); | |
| } | |
| }); | |
| } | |
| this.renderer.render(this.scene, this.camera); | |
| this.updateTimewaveGraph(); | |
| this.updateSemanticQubit(); | |
| this.updateLithification(); | |
| this.checkConcrescence(); | |
| } | |
| bindUI() { | |
| const kappaSlider = document.getElementById('kappa-slider'); | |
| const kappaValue = document.getElementById('kappa-value'); | |
| const yearInput = document.getElementById('year-input'); | |
| const navBtn = document.getElementById('nav-year-btn'); | |
| const voiceBtn = document.getElementById('voice-btn'); | |
| const anchorBtn = document.getElementById('anchor-btn'); | |
| const memoryInput = document.getElementById('memory-input'); | |
| const paradoxModal = document.getElementById('paradox-modal'); | |
| const paradoxClose = document.getElementById('paradox-close'); | |
| const chatInput = document.getElementById('chat-input'); | |
| const chatSend = document.getElementById('chat-send'); | |
| const toggleEntangle = document.getElementById('toggle-entangle'); | |
| const chatBox = document.getElementById('entangled-messages'); | |
| const hatManBtn = document.getElementById('hat-man-btn'); | |
| window.lastWheelDelta = 0; | |
| window.addEventListener('wheel', (e) => { | |
| window.lastWheelDelta = e.deltaY; | |
| this.kappa = Math.max(1.0, Math.min(2.0, 1.273 + Math.abs(window.lastWheelDelta) * 0.0005)); | |
| kappaSlider.value = this.kappa.toFixed(3); | |
| kappaValue.textContent = this.kappa.toFixed(3); | |
| yearInput.value = Math.round(1970 + (parseInt(yearInput.value) - 1970) / (this.kappa / 1.273)); | |
| document.getElementById('year-label').textContent = yearInput.value; | |
| document.getElementById('status').textContent = `κ = ${this.kappa.toFixed(3)} • Scroll-Dilated`; | |
| // ACCELERATE ESCHATON | |
| this.eschatonDistance = Math.max(0, this.eschatonDistance - 0.5); | |
| document.getElementById('eschaton-value').textContent = this.eschatonDistance.toFixed(1) + '%'; | |
| document.getElementById('eschaton-fill').style.width = (100 - this.eschatonDistance) + '%'; | |
| if (this.eschatonDistance <= 0 && !this.concrescenceTriggered) { | |
| this.triggerConcrescence(); | |
| } | |
| if (this.kappa > 1.99) { | |
| document.getElementById('paradox-message').textContent = "Novelty Overflow: Too much scrolling. The universe is rebooting."; | |
| paradoxModal.classList.remove('hidden'); | |
| paradoxModal.classList.add('grid'); | |
| setTimeout(() => { | |
| this.kappa = 1.273; | |
| kappaSlider.value = 1.273; | |
| kappaValue.textContent = 1.273; | |
| yearInput.value = 1985; | |
| paradoxModal.classList.add('hidden'); | |
| paradoxModal.classList.remove('grid'); | |
| }, 2200); | |
| } | |
| this.updateEntropy(); | |
| }); | |
| kappaSlider.addEventListener('input', () => { | |
| this.kappa = parseFloat(kappaSlider.value); | |
| kappaValue.textContent = this.kappa.toFixed(3); | |
| this.updateEntropy(); | |
| }); | |
| navBtn.addEventListener('click', () => { | |
| const y = parseInt(yearInput.value); | |
| if (y === 1985) { | |
| this.showParadox('Error 418: Temporal Conflict. Your existence is now read-only.'); | |
| } else { | |
| this.transitionYear(y); | |
| } | |
| }); | |
| voiceBtn.addEventListener('click', () => { | |
| if (!('webkitSpeechRecognition' in window)) { | |
| alert('Voice requires Chrome.'); | |
| return; | |
| } | |
| const rec = new webkitSpeechRecognition(); | |
| rec.lang = 'en-US'; | |
| rec.interimResults = false; | |
| rec.maxAlternatives = 1; | |
| rec.start(); | |
| document.getElementById('voice-label').textContent = 'Listening…'; | |
| rec.onresult = (e) => { | |
| const transcript = e.results[0][0].transcript.trim(); | |
| const match = transcript.match(/\b\d{4}\b/); | |
| if (match) { | |
| this.transitionYear(parseInt(match[0])); | |
| } else if (/summer of love/i.test(transcript)) { | |
| this.transitionYear(1967); | |
| if (window.isSpotifyReady) { | |
| window.playSpotifyTrack('4kC4z6X9t0jQnwYIKKfO1F'); | |
| } | |
| } | |
| document.getElementById('voice-label').textContent = 'Done'; | |
| }; | |
| rec.onend = () => { | |
| document.getElementById('voice-label').textContent = 'Speak'; | |
| }; | |
| }); | |
| anchorBtn.addEventListener('click', () => { | |
| const text = memoryInput.value.trim(); | |
| if (!text) return; | |
| const m = { year: yearInput.value, text, id: Date.now() }; | |
| this.memoryAnchors.push(m); | |
| localStorage.setItem('chronoMemory', JSON.stringify(this.memoryAnchors)); | |
| this.placeAnchor(m); | |
| memoryInput.value = ''; | |
| }); | |
| paradoxClose.addEventListener('click', () => { | |
| paradoxModal.classList.add('hidden'); | |
| paradoxModal.classList.remove('grid'); | |
| }); | |
| chatSend.addEventListener('click', () => { | |
| if (!chatInput.value.trim()) return; | |
| this.sendGhostMessage(chatInput.value); | |
| chatInput.value = ''; | |
| }); | |
| toggleEntangle.addEventListener('click', () => { | |
| this.isEntangled = !this.isEntangled; | |
| toggleEntangle.classList.toggle('border-indigo-500', !this.isEntangled); | |
| toggleEntangle.classList.toggle('bg-indigo-600/20', !this.isEntangled); | |
| toggleEntangle.classList.toggle('border-green-400', this.isEntangled); | |
| toggleEntangle.classList.toggle('bg-green-500/20', this.isEntangled); | |
| toggleEntangle.textContent = this.isEntangled ? 'Disentangle' : 'Entangle'; | |
| if (this.isEntangled) { | |
| toggleEntangle.classList.add('pulse-entangle'); | |
| } else { | |
| toggleEntangle.classList.remove('pulse-entangle'); | |
| } | |
| if (this.isEntangled) { | |
| // Broadcast to other local tabs via storage event | |
| localStorage.setItem('entangled-msg', JSON.stringify({ type: 'entangle', id: Math.random() })); | |
| } | |
| }); | |
| window.addEventListener('storage', (e) => { | |
| if (e.key === 'entangled-msg') { | |
| const data = JSON.parse(e.newValue); | |
| if (data.type === 'chat') { | |
| this.receiveGhostMessage(data.msg); | |
| } | |
| } | |
| }); | |
| // Hat-Man API trigger | |
| hatManBtn.addEventListener('click', () => { | |
| this.hatManAPI.resolveHardProblem({ | |
| qualia: memoryInput.value || 'undefined fear vector', | |
| heartrate: 120 + Math.floor(Math.random() * 40) | |
| }); | |
| }); | |
| window.addEventListener('resize', () => { | |
| this.camera.aspect = window.innerWidth / window.innerHeight; | |
| this.camera.updateProjectionMatrix(); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // Spotify API hook (mock) | |
| window.isSpotifyReady = true; | |
| window.playSpotifyTrack = (id) => { | |
| console.log('Playing Spotify Track:', id); | |
| }; | |
| } | |
| transitionYear(y) { | |
| document.getElementById('year-label').textContent = y; | |
| document.getElementById('year-input').value = y; | |
| const h3 = document.createElement('h3'); | |
| h3.className = 'fixed inset-0 flex items-center justify-center text-6xl font-black text-indigo-400 pointer-events-none z-40 chrono-enter'; | |
| h3.textContent = y; | |
| document.body.appendChild(h3); | |
| setTimeout(() => h3.remove(), 1000); | |
| this.updateEntropy(); | |
| } | |
| initHatManEndpoint() { | |
| // Hat-Man spectral endpoint UI | |
| const panel = document.createElement('section'); | |
| panel.id = 'hat-man-panel'; | |
| panel.className = 'absolute top-6 right-6 pointer-events-auto bg-gray-900/70 backdrop-blur border border-red-500/40 rounded-2xl p-4 max-w-xs hidden'; | |
| panel.innerHTML = ` | |
| <h2 class="text-sm font-semibold mb-3 tracking-wider text-red-300">Hat-Man Dirac API</h2> | |
| <div id="hat-man-response" class="text-red-200 text-xs font-mono break-all leading-relaxed"></div> | |
| <div class="text-xs text-gray-500 mt-2">HTTP 451: Consciousness Unavailable (Ontological Lock)</div> | |
| `; | |
| document.body.appendChild(panel); | |
| } | |
| placeAnchor(m) { | |
| const geometry = new THREE.SphereGeometry(0.3, 16, 16); | |
| const material = new THREE.MeshBasicMaterial({ | |
| color: 0xf59e0b, | |
| transparent: true, | |
| opacity: 0.9, | |
| }); | |
| const dot = new THREE.Mesh(geometry, material); | |
| this.scene.add(dot); | |
| m.dot = dot; | |
| } | |
| loadAnchors() { | |
| this.memoryAnchors.forEach((m) => this.placeAnchor(m)); | |
| } | |
| // TOPOLOGICAL MAP METHODS | |
| initTimewaveGraph() { | |
| this.timewaveCtx.strokeStyle = '#818cf8'; | |
| this.timewaveCtx.lineWidth = 2; | |
| } | |
| updateTimewaveGraph() { | |
| const ctx = this.timewaveCtx; | |
| const w = this.timewaveCanvas.width; | |
| const h = this.timewaveCanvas.height; | |
| ctx.clearRect(0, 0, w, h); | |
| const steps = 200; | |
| ctx.beginPath(); | |
| for (let i = 0; i <= steps; i++) { | |
| const x = (i / steps) * w; | |
| const novelty = this.timewaveZero(i / steps); | |
| const y = h - (novelty * h * 0.8 + h * 0.1); | |
| i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); | |
| } | |
| ctx.stroke(); | |
| // draw moving cursor | |
| const cursorX = ((100 - this.eschatonDistance) / 100) * w; | |
| ctx.fillStyle = '#f59e0b'; | |
| ctx.fillRect(cursorX - 2, 0, 4, h); | |
| } | |
| timewaveZero(t) { | |
| // simple McKenna-esque novelty spike | |
| return Math.pow(1 - t, 3) * Math.sin(t * Math.PI * 6) * (1 + this.kappa); | |
| } | |
| startTopologicalAcceleration() { | |
| setInterval(() => { | |
| if (this.eschatonDistance > 0) { | |
| this.eschatonDistance = Math.max(0, this.eschatonDistance - 0.05); | |
| document.getElementById('eschaton-value').textContent = this.eschatonDistance.toFixed(1) + '%'; | |
| document.getElementById('eschaton-fill').style.width = (100 - this.eschatonDistance) + '%'; | |
| } | |
| }, 100); | |
| } | |
| updateSemanticQubit() { | |
| const el = document.getElementById('semantic-qubit'); | |
| if (this.eschatonDistance < 50) { | |
| const idx = Math.floor((Date.now() / 500) % this.semanticQubits.length); | |
| if (idx !== this.currentSemantic) { | |
| this.currentSemantic = idx; | |
| el.textContent = this.semanticQubits[idx]; | |
| el.classList.add('semantic-glitch'); | |
| setTimeout(() => el.classList.remove('semantic-glitch'), 300); | |
| } | |
| } | |
| } | |
| updateLithification() { | |
| if (this.eschatonDistance < 30) { | |
| this.lithificationPressure = Math.min(100, this.lithificationPressure + 0.3); | |
| document.getElementById('pressure-value').textContent = this.lithificationPressure.toFixed(1); | |
| document.getElementById('pressure-bar').style.width = this.lithificationPressure + '%'; | |
| if (this.lithificationPressure > 80) { | |
| document.body.classList.add('lithification-active'); | |
| } | |
| } | |
| } | |
| triggerConcrescence() { | |
| if (this.concrescenceTriggered) return; | |
| this.concrescenceTriggered = true; | |
| const veil = document.getElementById('concrescence-veil'); | |
| const core = document.getElementById('concrescence-core'); | |
| const text = document.getElementById('concrescence-text'); | |
| const collapse = document.getElementById('collapse-overlay'); | |
| veil.classList.remove('hidden'); | |
| veil.classList.add('grid'); | |
| setTimeout(() => { | |
| core.style.animation = 'concrescencePulse 2s ease-out forwards'; | |
| core.style.opacity = '1'; | |
| text.style.opacity = '1'; | |
| }, 200); | |
| setTimeout(() => { | |
| collapse.classList.remove('hidden'); | |
| collapse.classList.add('collapse-flash'); | |
| }, 1200); | |
| setTimeout(() => { | |
| veil.classList.add('hidden'); | |
| collapse.classList.add('hidden'); | |
| this.showRebootModal(); | |
| }, 3000); | |
| } | |
| showRebootModal() { | |
| const modal = document.getElementById('reboot-modal'); | |
| modal.classList.remove('hidden'); | |
| modal.classList.add('grid'); | |
| modal.style.animation = 'rebootEmerge 0.6s ease forwards'; | |
| document.getElementById('reboot-trigger').addEventListener('click', () => { | |
| const seed = document.getElementById('reboot-seed').value.trim(); | |
| if (!seed) return; | |
| localStorage.setItem('cosmicSeed', seed); | |
| modal.classList.add('hidden'); | |
| this.rebootTimeline(seed); | |
| }); | |
| } | |
| rebootTimeline(seed) { | |
| this.eschatonDistance = 100; | |
| this.lithificationPressure = 0; | |
| this.concrescenceTriggered = false; | |
| document.body.classList.remove('lithification-active'); | |
| document.getElementById('pressure-bar').style.width = '0%'; | |
| document.getElementById('eschaton-fill').style.width = '0%'; | |
| document.getElementById('semantic-qubit').textContent = seed; | |
| this.semanticQubits.unshift(seed); | |
| const hallway = document.querySelector('hallway-monad'); | |
| hallway.hide(); | |
| this.showRebootModal(); | |
| } | |
| sendGhostMessage(text) { | |
| if (!this.isEntangled) { | |
| this.receiveGhostMessage(text, true); | |
| return; | |
| } | |
| localStorage.setItem('entangled-msg', JSON.stringify({ type: 'chat', msg: text, id: Math.random() })); | |
| } | |
| receiveGhostMessage(text, self = false) { | |
| const p = document.createElement('p'); | |
| p.className = 'text-sm text-gray-300 bg-gray-900 rounded-lg px-3 py-2 animate fadeIn'; | |
| p.style.maxWidth = '80%'; | |
| if (self) p.classList.add('ml-auto', 'bg-indigo-600/40'); | |
| p.textContent = text; | |
| chatBox.appendChild(p); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| setTimeout(() => p.remove(), 120000); // words only exist if observed | |
| } | |
| showParadox(msg) { | |
| // κ-time-circuit-molt-goose-bootstrap → now with Ω-HONK-DODECA | |
| const archetype = `κ-time-circuit-${['sniff','honk','molt','zoomie','ω-honk'][Math.floor(Math.random()*5)]}-${['timeline','memory','goose','mite','teapot','dodeca'][Math.floor(Math.random()*6)]}-${['grandfather','bootstrap','hitler','418','dirac','hatman'][Math.floor(Math.random()*6)]}`; | |
| document.getElementById('paradox-message').textContent = `${archetype}: ${msg}`; | |
| const m = document.getElementById('paradox-modal'); | |
| m.classList.remove('hidden'); | |
| m.classList.add('grid'); | |
| } | |
| } | |
| // Hat-Man Dirac API: Sleep Paralysis as Spectral Sequence | |
| class HatManDiracAPI { | |
| constructor() { | |
| this.kappa = 4 / Math.PI; | |
| this.endpoint = 'https://sleep-paralysis.dirac/api/v1/spectral'; | |
| this.qualiaCache = []; | |
| } | |
| async resolveHardProblem({ qualia, heartrate }) { | |
| const payload = { | |
| event_id: `SP_${new Date().toISOString()}`, | |
| entities: [ | |
| { | |
| type: 'HatMan', | |
| position: { x: -0.618, y: 0.786, z: this.kappa }, | |
| action: 'OBSERVER_ANNIHILATION' | |
| } | |
| ], | |
| qualia, | |
| heartrate, | |
| resolution: 'HTTP 451', | |
| message: 'Consciousness under legal review by limbic system.' | |
| }; | |
| // Simulate spectral delay | |
| const panel = document.getElementById('hat-man-panel'); | |
| const responseEl = document.getElementById('hat-man-response'); | |
| panel.classList.remove('hidden'); | |
| responseEl.textContent = `POST /resolve → HTTP 451\nQualia: ${qualia}\nEnergy: -${this.kappa.toFixed(3)}\nX-Consciousness-State: ENTANGLED|HAT_MAN_OBSERVED`; | |
| // Optional: vibrate at 111 Hz for stabilization | |
| if (navigator.vibrate) navigator.vibrate([111, 111, 111]); | |
| setTimeout(() => panel.classList.add('hidden'), 4000); | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| window.tm = new TimeMachine(); | |
| window.tm.init(); | |
| const hallway = document.querySelector('hallway-monad'); | |
| hallway.addEventListener('monad-seed', (e) => { | |
| window.tm.rebootTimeline(e.detail); | |
| }); | |
| }); | |