Text Generation
PyTorch
GGUF
English
quantum
quantum-entropy
from-scratch
char-level
cosmic-synapse-theory
custom-architecture
llama-cpp
continual-learning
reproducible-seed
open-science
null-results
Instructions to use phera-ra/QC67_cosmo with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use phera-ra/QC67_cosmo with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf phera-ra/QC67_cosmo # Run inference directly in the terminal: llama cli -hf phera-ra/QC67_cosmo
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf phera-ra/QC67_cosmo # Run inference directly in the terminal: llama cli -hf phera-ra/QC67_cosmo
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf phera-ra/QC67_cosmo # Run inference directly in the terminal: ./llama-cli -hf phera-ra/QC67_cosmo
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf phera-ra/QC67_cosmo # Run inference directly in the terminal: ./build/bin/llama-cli -hf phera-ra/QC67_cosmo
Use Docker
docker model run hf.co/phera-ra/QC67_cosmo
- LM Studio
- Jan
- vLLM
How to use phera-ra/QC67_cosmo with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "phera-ra/QC67_cosmo" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "phera-ra/QC67_cosmo", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/phera-ra/QC67_cosmo
- Ollama
How to use phera-ra/QC67_cosmo with Ollama:
ollama run hf.co/phera-ra/QC67_cosmo
- Unsloth Studio
How to use phera-ra/QC67_cosmo with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for phera-ra/QC67_cosmo to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for phera-ra/QC67_cosmo to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for phera-ra/QC67_cosmo to start chatting
- Docker Model Runner
How to use phera-ra/QC67_cosmo with Docker Model Runner:
docker model run hf.co/phera-ra/QC67_cosmo
- Lemonade
How to use phera-ra/QC67_cosmo with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull phera-ra/QC67_cosmo
Run and chat with the model
lemonade run user.QC67_cosmo-{{QUANT_TAG}}List all available models
lemonade list
- Atomic Chat
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover"> | |
| <meta name="theme-color" content="#000"> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <meta name="mobile-web-app-capable" content="yes"> | |
| <meta name="description" content="Genesis Engine - Revolutionary AI-powered quantum consciousness simulation with real-time environmental data processing"> | |
| <title>Genesis Engine: The Definitive Core - Advanced Tokenization</title> | |
| <!-- DEPENDENCIES & STYLESHEETS --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- Solana Libraries for Wallet Integration --> | |
| <script src="https://unpkg.com/@solana/web3.js@1.91.7/lib/index.iife.min.js"></script> | |
| <script src="https://unpkg.com/@solana/spl-token@0.4.8/lib/index.iife.min.js"></script> | |
| <script src="https://unpkg.com/@solana/spl-account-compression@0.2.2/lib/index.iife.min.js"></script> | |
| <script src="https://unpkg.com/@metaplex-foundation/js@0.20.1/dist/index.umd.js"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&family=Roboto+Mono:wght@300;400;500&display=swap" rel="stylesheet"> | |
| <!-- ES Module Imports for three.js --> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "three": "https://unpkg.com/three@0.158.0/build/three.module.js", | |
| "three/addons/": "https://unpkg.com/three@0.158.0/examples/jsm/" | |
| } | |
| } | |
| </script> | |
| <!-- ml5.js for Machine Learning --> | |
| <script src="https://unpkg.com/ml5@0.12.2/dist/ml5.min.js"></script> | |
| <style> | |
| /* General Styling */ | |
| body { margin: 0; background-color: #000; color: #e2e8f0; overflow: hidden; font-family: 'Inter', sans-serif; cursor: crosshair; } | |
| canvas { display: block; position: fixed; top: 0; left: 0; z-index: 1; } | |
| .font-mono { font-family: 'Roboto Mono', monospace; } | |
| /* UI Element Styling */ | |
| .ui-element { position: fixed; z-index: 10; background-color: rgba(15, 23, 42, 0.85); backdrop-filter: blur(12px); border: 1px solid rgba(51, 65, 85, 0.5); border-radius: 0.75rem; box-shadow: 0 10px 30px rgba(0,0,0,0.3); transition: opacity 0.3s ease, transform 0.3s ease; } | |
| .ui-hidden { opacity: 0; pointer-events: none; transform: scale(0.95); } | |
| /* Wallet animations */ | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| /* Mobile responsiveness */ | |
| @media (max-width: 768px) { | |
| .ui-element { | |
| width: calc(100vw - 24px) ; | |
| max-width: none ; | |
| left: 12px ; | |
| right: 12px ; | |
| } | |
| .tab-button { font-size: 12px; padding: 0.25rem 0.75rem; } | |
| } | |
| @media (max-width: 480px) { | |
| .ui-element { | |
| top: 8px ; | |
| left: 8px ; | |
| right: 8px ; | |
| width: calc(100vw - 16px) ; | |
| padding: 8px ; | |
| } | |
| .tab-button { font-size: 11px; padding: 4px 8px; } | |
| } | |
| /* Custom scrollbar styles */ | |
| .scrollbar-thin { | |
| scrollbar-width: thin; | |
| } | |
| .scrollbar-thin::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .scrollbar-thin::-webkit-scrollbar-track { | |
| background: rgba(30, 41, 59, 0.5); | |
| border-radius: 3px; | |
| } | |
| .scrollbar-thin::-webkit-scrollbar-thumb { | |
| background: rgba(71, 85, 105, 0.8); | |
| border-radius: 3px; | |
| } | |
| .scrollbar-thin::-webkit-scrollbar-thumb:hover { | |
| background: rgba(100, 116, 139, 0.9); | |
| } | |
| /* Music visualizer styles */ | |
| .visualizer-bar { | |
| background: linear-gradient(180deg, #3b82f6, #1d4ed8); | |
| border-radius: 2px; | |
| transition: height 0.1s ease, opacity 0.3s ease; | |
| box-shadow: 0 0 4px rgba(59, 130, 246, 0.5); | |
| } | |
| .visualizer-bar.active { | |
| background: linear-gradient(180deg, #10b981, #059669); | |
| box-shadow: 0 0 8px rgba(16, 185, 129, 0.8); | |
| } | |
| .frequency-spectrum { | |
| display: flex; | |
| align-items: end; | |
| gap: 2px; | |
| height: 60px; | |
| padding: 4px; | |
| background: rgba(0, 0, 0, 0.3); | |
| border-radius: 4px; | |
| } | |
| .harmony-meter { | |
| position: relative; | |
| height: 8px; | |
| background: rgba(0, 0, 0, 0.5); | |
| border-radius: 4px; | |
| overflow: hidden; | |
| } | |
| .harmony-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #ef4444, #f59e0b, #10b981); | |
| transition: width 0.5s ease; | |
| border-radius: 4px; | |
| } | |
| .consciousness-ring { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| border: 3px solid rgba(59, 130, 246, 0.3); | |
| position: relative; | |
| } | |
| .consciousness-fill { | |
| position: absolute; | |
| top: 3px; | |
| left: 3px; | |
| right: 3px; | |
| bottom: 3px; | |
| border-radius: 50%; | |
| background: conic-gradient(from 0deg, #3b82f6 0%, transparent 0%); | |
| transition: background 0.5s ease; | |
| } | |
| /* Touch controls styling */ | |
| * { -webkit-tap-highlight-color: rgba(0,0,0,0); } | |
| html, body { | |
| height: 100vh; | |
| overflow: hidden; | |
| touch-action: none; | |
| -webkit-user-select: none; | |
| user-select: none; | |
| -webkit-touch-callout: none; | |
| } | |
| canvas { | |
| display: block; | |
| width: 100vw; | |
| height: 100vh; | |
| touch-action: none; | |
| } | |
| /* Modal & Blocker Styling */ | |
| #blocker, .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 100; background: rgba(0,0,0,0.8); display: flex; justify-content: center; align-items: center; flex-direction: column; text-align: center; transition: opacity 0.5s ease; } | |
| #fps-hint { z-index: 101; background: none; backdrop-filter: none; border: none; box-shadow: none; font-size: 1.2rem; text-shadow: 0 0 10px black; color: #fff; } | |
| /* Tab Navigation Styling */ | |
| .tab-button { background-color: transparent; border: none; color: #94a3b8; padding: 0.5rem 1rem; cursor: pointer; transition: color 0.2s, border-bottom-color 0.2s; border-bottom: 2px solid transparent; font-weight: 500;} | |
| .tab-button.active { color: #e2e8f0; border-bottom-color: #4f46e5; } | |
| .tab-content { display: none; } | |
| .tab-content.active { display: block; } | |
| /* Seed Visualizer & Data Pulse Animation */ | |
| #seed-visualizer { height: 60px; display: flex; align-items: center; justify-content: space-around; background: rgba(0,0,0,0.2); border-radius: 0.5rem; overflow: hidden;} | |
| .data-stream { width: 15%; height: 100%; display: flex; align-items: center; justify-content: center; flex-direction: column; font-size: 0.6rem; text-transform: uppercase; color: #64748b; } | |
| .data-pulse { width: 10px; height: 10px; background-color: #334155; border-radius: 50%; transition: background-color 0.1s, box-shadow 0.2s; } | |
| @keyframes pulse-glow { 0% { box-shadow: 0 0 0 0 rgba(56, 189, 248, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(56, 189, 248, 0); } 100% { box-shadow: 0 0 0 0 rgba(56, 189, 248, 0); } } | |
| /* Button & Input Styling */ | |
| .btn { background-color: #4f46e5; color: white; font-weight: bold; padding: 0.5rem 1rem; border-radius: 0.5rem; border: none; transition: background-color 0.2s; cursor: pointer; text-align: center; } | |
| .btn:hover { background-color: #6366f1; } | |
| .btn:disabled { background-color: #4b5563; cursor: not-allowed; } | |
| input[type="file"] { display: none; } | |
| .slider-container, .toggle-container { display: flex; flex-direction: column; gap: 0.25rem; } | |
| .slider-label, .toggle-label { display: flex; justify-content: space-between; font-size: 0.75rem; align-items:center;} | |
| input[type=range] { -webkit-appearance: none; appearance: none; background: transparent; width: 100%;} | |
| input[type=range]:focus { outline: none; } | |
| input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; cursor: pointer; background: #334155; border-radius: 2px; } | |
| input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; border: 2px solid #4f46e5; height: 16px; width: 16px; border-radius: 50%; background: #0f172a; cursor: pointer; margin-top: -6px;} | |
| /* Toggle Switch */ | |
| .switch { position: relative; display: inline-block; width: 34px; height: 20px; } | |
| .switch input { opacity: 0; width: 0; height: 0; } | |
| .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #334155; transition: .4s; border-radius: 20px;} | |
| .slider:before { position: absolute; content: ""; height: 12px; width: 12px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%;} | |
| input:checked + .slider { background-color: #4f46e5; } | |
| input:checked + .slider:before { transform: translateX(14px); } | |
| /* Status pulse animation for dynamic states */ | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.6; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Canvas Container for 3D Scene --> | |
| <div id="canvas-container"></div> | |
| <!-- Hidden Video Element for ML & Video Particlizer --> | |
| <video id="video-feed" style="display:none;" autoplay muted playsinline></video> | |
| <!-- Wallet Integration System --> | |
| <div id="wallet-disconnected" class="ui-element" style="position: fixed; top: 20px; right: 20px; z-index: 1000; width: 300px;"> | |
| <div style="padding: 16px;"> | |
| <h3 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600;">🦄 Wallet Connection</h3> | |
| <div style="display: flex; gap: 8px; margin-bottom: 12px;"> | |
| <button id="connect-phantom-btn" class="btn" style="flex: 1; padding: 8px 12px; background: linear-gradient(135deg, #7c3aed, #a855f7); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500;">Connect Phantom</button> | |
| <button id="connect-manual-btn" class="btn secondary" style="flex: 1; padding: 8px 12px; background: rgba(75, 85, 99, 0.8); color: white; border: 1px solid rgba(156, 163, 175, 0.3); border-radius: 6px; cursor: pointer; font-size: 12px;">Manual Input</button> | |
| </div> | |
| <div style="font-size: 11px; color: rgba(156, 163, 175, 0.8);"> | |
| Connect wallet to access Genesis Engine features | |
| </div> | |
| </div> | |
| </div> | |
| <div id="wallet-connected" class="ui-element" style="position: fixed; top: 20px; right: 20px; z-index: 1000; width: 320px; display: none;"> | |
| <div style="padding: 16px;"> | |
| <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;"> | |
| <h3 style="margin: 0; font-size: 14px; font-weight: 600;">🦄 Wallet Connected</h3> | |
| <div id="wallet-status-indicator" class="w-3 h-3 rounded-full bg-green-500"></div> | |
| </div> | |
| <div style="margin-bottom: 8px;"> | |
| <div style="font-size: 11px; color: rgba(156, 163, 175, 0.8); margin-bottom: 2px;">Address</div> | |
| <div id="wallet-address-display" style="font-size: 12px; font-family: 'Roboto Mono', monospace; color: #e2e8f0; word-break: break-all;"></div> | |
| </div> | |
| <div style="margin-bottom: 12px;"> | |
| <div style="font-size: 11px; color: rgba(156, 163, 175, 0.8); margin-bottom: 2px;">Mobius Balance</div> | |
| <div style="display: flex; align-items: center; gap: 8px;"> | |
| <div id="mobius-balance-display" style="font-size: 16px; font-weight: 600; color: #10b981;">0.00</div> | |
| <div style="font-size: 12px; color: rgba(156, 163, 175, 0.8);">MOBIUS</div> | |
| </div> | |
| </div> | |
| <div style="display: flex; gap: 8px;"> | |
| <button id="refresh-balance-btn" class="btn secondary" style="flex: 1; padding: 6px 12px; background: rgba(59, 130, 246, 0.8); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 11px;">Refresh Balance</button> | |
| <button id="disconnect-wallet-btn" class="btn danger" style="flex: 1; padding: 6px 12px; background: rgba(239, 68, 68, 0.8); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 11px;">Disconnect</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="wallet-loading" class="ui-element" style="position: fixed; top: 20px; right: 20px; z-index: 1000; width: 300px; display: none;"> | |
| <div style="padding: 16px; text-align: center;"> | |
| <div style="width: 24px; height: 24px; border: 2px solid rgba(156, 163, 175, 0.3); border-top: 2px solid #7c3aed; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 12px;"></div> | |
| <div style="font-size: 14px; color: #e2e8f0;">Connecting Wallet...</div> | |
| <div style="font-size: 11px; color: rgba(156, 163, 175, 0.8); margin-top: 4px;">Please approve in your wallet</div> | |
| </div> | |
| </div> | |
| <!-- Initial Blocker/Loading Screen --> | |
| <div id="blocker"> | |
| <div class="max-w-2xl px-4"> | |
| <h1 class="text-4xl md:text-5xl font-bold text-slate-100 mb-2">Genesis Engine</h1> | |
| <h2 class="text-xl md:text-2xl font-light text-slate-400 mb-6">The Definitive Core</h2> | |
| <p class="text-slate-400 mb-8">This engine transmutes reality into a digital cosmos. For the full experience, please grant access to your microphone, camera, and location when prompted. No data is stored or uploaded. This is a non-invasive, interactive art piece.</p> | |
| <button id="start-button" class="btn bg-indigo-600 hover:bg-indigo-500 text-white font-bold py-3 px-8 rounded-lg text-lg transition-transform transform hover:scale-105">Initiate Genesis</button> | |
| <p id="loading-message" class="text-slate-500 mt-4" style="display: none;">Calibrating sensor arrays...</p> | |
| </div> | |
| </div> | |
| <!-- Modal for Memory Echo --> | |
| <div id="datamodal" class="modal ui-hidden"> | |
| <div class="ui-element p-8 max-w-lg w-full"> | |
| <h3 class="text-2xl font-bold text-slate-100 mb-4">Memory Echo Detected</h3> | |
| <p class="text-slate-300 mb-6">An artifact of pure data has been found, an echo of a world caught in a repeating tragedy. Provide the data catalyst (e.g., a .z64 ROM file) to transmute its contents into a playable memory echo.</p> | |
| <input type="file" id="data-upload"> | |
| <label for="data-upload" class="mb-4 cursor-pointer w-full block text-center btn bg-green-600 hover:bg-green-500">Provide Data Catalyst</label> | |
| <p id="data-status" class="text-sm text-yellow-400 mb-4">Awaiting file...</p> | |
| <div class="flex justify-center gap-4"> | |
| <button id="modal-confirm-button" class="btn bg-red-600 hover:bg-red-500" disabled>Enter the Echo</button> | |
| <button id="modal-cancel-button" class="btn bg-slate-600 hover:bg-slate-500">Return to Void</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Hint for First-Person Mode Controls --> | |
| <div id="fps-hint" class="modal ui-hidden"> | |
| <p>First-Person Mode Active. Press ESC to release cursor.</p> | |
| </div> | |
| <!-- Main UI Toggle Button --> | |
| <button id="toggle-ui-button" class="ui-element top-4 right-4 p-3 text-slate-300 hover:text-white hover:bg-slate-700 transition ui-hidden"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg> | |
| </button> | |
| <!-- Main UI Container --> | |
| <div id="ui-container" class="ui-hidden"> | |
| <div class="ui-element top-4 left-4 w-96"> | |
| <div class="px-4 pt-3 pb-2 font-bold text-lg text-slate-300" id="system-status-readout">STATUS: STANDBY</div> | |
| <div class="tabs border-b border-slate-700 px-2"> | |
| <button class="tab-button active" data-tab="controls">Controls</button> | |
| <button class="tab-button" data-tab="graphics">Graphics</button> | |
| <button class="tab-button" data-tab="data">Data Ledger</button> | |
| <button class="tab-button" data-tab="telemetry">Telemetry</button> | |
| <button class="tab-button" data-tab="cognitive">Cognitive Core</button> | |
| </div> | |
| <div id="tab-content-container" class="p-4"> | |
| <!-- CONTROLS TAB --> | |
| <div id="tab-controls" class="tab-content active space-y-4"> | |
| <!-- Universe Controls --> | |
| <button id="regenerate-button" class="btn w-full bg-slate-700 hover:bg-slate-600">Regenerate Universe</button> | |
| <button id="anomaly-scanner-button" class="btn w-full bg-cyan-600 hover:bg-cyan-500">Scan for Anomalies</button> | |
| <!-- Particlization Controls --> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <label for="file-upload" class="btn">Particlize File</label> | |
| <button id="video-particlize-button" class="btn">Particlize Live</button> | |
| <input type="file" id="file-upload" accept="image/*,video/*"> | |
| </div> | |
| <button id="self-particlize-button" class="btn w-full bg-purple-600 hover:bg-purple-500">Particlize Self</button> | |
| <!-- NFT Minting Controls --> | |
| <div class="border-t border-slate-600 pt-4 mt-4"> | |
| <h3 class="text-sm font-semibold text-indigo-400 mb-3">NFT Minting</h3> | |
| <button id="mint-avatar-btn" class="btn w-full bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 disabled:opacity-50 disabled:cursor-not-allowed mb-2"> | |
| 🎨 Mint Genesis Avatar | |
| </button> | |
| <div class="text-xs text-slate-400 text-center"> | |
| Cost: 10 MOBIUS tokens | |
| </div> | |
| <!-- Environmental Data Display --> | |
| <div class="mt-3 p-2 bg-slate-800/50 rounded-md"> | |
| <div class="text-xs font-mono text-cyan-400 mb-1">Current Environmental Data:</div> | |
| <div class="grid grid-cols-2 gap-1 text-xs"> | |
| <div>Light:</div><div id="env-light" class="text-yellow-400">--</div> | |
| <div>Sound:</div><div id="env-sound" class="text-blue-400">--</div> | |
| <div>Temp:</div><div id="env-temp" class="text-red-400">--</div> | |
| <div>Humidity:</div><div id="env-humidity" class="text-green-400">--</div> | |
| </div> | |
| </div> | |
| <!-- Minting Status --> | |
| <div id="minting-status" class="mt-2 text-xs text-center hidden"> | |
| <div class="animate-pulse">Processing NFT mint...</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- GRAPHICS TAB --> | |
| <div id="tab-graphics" class="tab-content space-y-4 text-sm text-slate-300"> | |
| <div class="slider-container"> | |
| <label>View Mode</label> | |
| <select id="view-mode-select" class="w-full bg-slate-800 text-slate-200 rounded-md p-2 mt-1 text-sm border border-slate-600"> | |
| <option value="orbit">Orbit</option> | |
| <option value="first-person">First Person</option> | |
| <option value="enhanced-fp">Spaceship (3rd Person)</option> | |
| <option value="cockpit-view">Spaceship (Cockpit)</option> | |
| <option value="cinematic">Cinematic</option> | |
| </select> | |
| </div> | |
| <div class="slider-container"> | |
| <label>Graphics Style</label> | |
| <select id="quality-select" class="w-full bg-slate-800 text-slate-200 rounded-md p-2 mt-1 text-sm border border-slate-600"> | |
| <option value="low">Low (Pixelated)</option> | |
| <option value="medium" selected>Medium (Realistic)</option> | |
| <option value="high">High (Realistic)</option> | |
| <option value="ultra">Ultra (Realistic)</option> | |
| <option value="anime">Anime (Cel-Shaded)</option> | |
| </select> | |
| </div> | |
| <div class="slider-container"> | |
| <label>Color Filter</label> | |
| <select id="filter-select" class="w-full bg-slate-800 text-slate-200 rounded-md p-2 mt-1 text-sm border border-slate-600"> | |
| <option value="none">None</option> | |
| <option value="cool">Cool</option> | |
| <option value="warm">Warm</option> | |
| <option value="nebula">Nebula</option> | |
| <option value="monochrome">Monochrome</option> | |
| </select> | |
| </div> | |
| <div class="slider-container"> | |
| <div class="slider-label"><span>Brightness</span><span id="brightness-value">1.0</span></div> | |
| <input type="range" id="brightness-slider" min="0" max="2" value="1" step="0.05" class="w-full"> | |
| </div> | |
| <!-- NEW: Procedural Audio Toggle --> | |
| <div class="toggle-container"> | |
| <div class="toggle-label"> | |
| <span>Enable Ambient Audio</span> | |
| <label class="switch"> | |
| <input type="checkbox" id="ambient-audio-toggle"> | |
| <span class="slider"></span> | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Motion Blur Controls --> | |
| <div class="toggle-container"> | |
| <div class="toggle-label"> | |
| <span>Motion Blur</span> | |
| <label class="switch"> | |
| <input type="checkbox" id="motion-blur-toggle"> | |
| <span class="slider"></span> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="slider-container"> | |
| <div class="slider-label"><span>Motion Blur Intensity</span><span id="motion-blur-value">0.20</span></div> | |
| <input type="range" id="motion-blur-slider" min="0" max="1" value="0.2" step="0.01" class="w-full"> | |
| </div> | |
| </div> | |
| <!-- DATA LEDGER TAB --> | |
| <div id="tab-data" class="tab-content space-y-2"> | |
| <div id="genesis-log-content" class="text-sm font-mono text-slate-400 max-h-48 overflow-y-auto"></div> | |
| <button id="download-ledger-button" class="btn w-full bg-green-600 hover:bg-green-500 mt-2">Download Ledger</button> | |
| </div> | |
| <!-- TELEMETRY TAB --> | |
| <div id="tab-telemetry" class="tab-content space-y-2"> | |
| <input id="telemetry-filter" class="w-full bg-slate-800 text-slate-200 rounded-md p-2 text-sm border border-slate-600" placeholder="Filter by type or token (e.g., audio,BASS_HIGH, ml:person)"> | |
| <div id="telemetry-log-content" class="text-xs font-mono text-slate-400 max-h-60 overflow-y-auto whitespace-pre-wrap"></div> | |
| <button id="download-telemetry-button" class="btn w-full bg-cyan-600 hover:bg-cyan-500 mt-2">Download Telemetry</button> | |
| </div> | |
| <!-- COGNITIVE CORE TAB --> | |
| <div id="tab-cognitive" class="tab-content space-y-3 text-sm text-slate-300 max-h-96 overflow-y-auto scrollbar-thin scrollbar-thumb-slate-600 scrollbar-track-slate-800"> | |
| <div id="seed-visualizer"> | |
| <div class="data-stream" data-stream="audio">Audio<div class="data-pulse"></div></div> | |
| <div class="data-stream" data-stream="ml">ML<div class="data-pulse"></div></div> | |
| <div class="data-stream" data-stream="loc">Location<div class="data-pulse"></div></div> | |
| <div class="data-stream" data-stream="light">Light<div class="data-pulse"></div></div> | |
| <div class="data-stream" data-stream="usgs">USGS<div class="data-pulse"></div></div> | |
| <div class="data-stream" data-stream="apod">APOD<div class="data-pulse"></div></div> | |
| </div> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-indigo-400">AI INTENTION:</p> | |
| <p id="ai-intention" class="font-mono text-xs text-slate-300 h-8">Awaiting sensory input...</p> | |
| </div> | |
| <!-- Cognitive Core Status --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-cyan-400">COGNITIVE CORE STATUS:</p> | |
| <div class="grid grid-cols-2 gap-1 text-xs mt-1"> | |
| <div>Consciousness:</div> | |
| <div id="cognitive-consciousness" class="font-mono text-green-400">0.00</div> | |
| <div>Emotion:</div> | |
| <div id="cognitive-emotion" class="font-mono text-pink-400">serenity</div> | |
| <div>Learning:</div> | |
| <div id="cognitive-learning" class="font-mono text-blue-400">0 exp</div> | |
| <div>Creativity:</div> | |
| <div id="cognitive-creativity" class="font-mono text-purple-400">0.00</div> | |
| </div> | |
| </div> | |
| <!-- Neural Network Status --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-yellow-400">NEURAL NETWORK:</p> | |
| <div class="grid grid-cols-2 gap-1 text-xs mt-1"> | |
| <div>Predictions:</div> | |
| <div id="neural-predictions" class="font-mono text-orange-400">0</div> | |
| <div>Accuracy:</div> | |
| <div id="neural-accuracy" class="font-mono text-red-400">0.0%</div> | |
| <div>Memory:</div> | |
| <div id="neural-memory" class="font-mono text-teal-400">0</div> | |
| <div>Skills:</div> | |
| <div id="neural-skills" class="font-mono text-indigo-400">0</div> | |
| </div> | |
| </div> | |
| <!-- Music Visualizer --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-purple-400 mb-2">AUDIO VISUALIZER:</p> | |
| <div id="frequency-spectrum" class="frequency-spectrum"> | |
| <!-- Bars will be dynamically created --> | |
| </div> | |
| <div class="mt-2"> | |
| <div class="flex justify-between text-xs"> | |
| <span>Bass</span> | |
| <span>Mid</span> | |
| <span>Treble</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Harmony & Consciousness Meters --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-emerald-400 mb-2">SYSTEM HARMONY:</p> | |
| <div class="harmony-meter"> | |
| <div id="harmony-fill" class="harmony-fill" style="width: 50%"></div> | |
| </div> | |
| <div class="mt-2 flex justify-between items-center"> | |
| <span class="text-xs">Chaos</span> | |
| <div class="consciousness-ring"> | |
| <div id="consciousness-fill" class="consciousness-fill"></div> | |
| </div> | |
| <span class="text-xs">Harmony</span> | |
| </div> | |
| </div> | |
| <!-- AI Learning Controls --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-purple-400 mb-2">AI LEARNING CONTROLS:</p> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <button id="train-ai-btn" class="btn text-xs bg-purple-600 hover:bg-purple-500">Train AI</button> | |
| <button id="reset-ai-btn" class="btn text-xs bg-orange-600 hover:bg-orange-500">Reset Learning</button> | |
| <button id="export-ai-btn" class="btn text-xs bg-green-600 hover:bg-green-500">Export Model</button> | |
| <button id="import-ai-btn" class="btn text-xs bg-blue-600 hover:bg-blue-500">Import Model</button> | |
| </div> | |
| </div> | |
| <!-- Creative Output Display --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-pink-400 mb-2">CREATIVE OUTPUT:</p> | |
| <div id="creative-output" class="text-xs text-slate-300 h-16 overflow-y-auto"> | |
| Generating harmonious patterns... | |
| </div> | |
| <button id="generate-creative-btn" class="btn text-xs w-full mt-2 bg-pink-600 hover:bg-pink-500">Generate New Idea</button> | |
| </div> | |
| <!-- Universe Response Monitor --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-cyan-400 mb-2">UNIVERSE RESPONSE:</p> | |
| <div id="universe-response" class="text-xs text-slate-300 h-12 overflow-y-auto"> | |
| Universe breathing in harmony... | |
| </div> | |
| <button id="query-universe-btn" class="btn text-xs w-full mt-2 bg-cyan-600 hover:bg-cyan-500">Query Universe</button> | |
| </div> | |
| <!-- Consciousness Expansion --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-emerald-400 mb-2">CONSCIOUSNESS EXPANSION:</p> | |
| <div class="grid grid-cols-2 gap-1 text-xs mt-1"> | |
| <div>Meditation:</div> | |
| <div id="meditation-level" class="font-mono text-emerald-400">0.00</div> | |
| <div>Enlightenment:</div> | |
| <div id="enlightenment-level" class="font-mono text-yellow-400">0.00</div> | |
| </div> | |
| <button id="meditate-btn" class="btn text-xs w-full mt-2 bg-emerald-600 hover:bg-emerald-500">Begin Meditation</button> | |
| </div> | |
| <!-- Divine Governance Status --> | |
| <div class="p-2 bg-black/20 rounded-md"> | |
| <p class="font-mono text-xs text-amber-400 mb-2">DIVINE GOVERNANCE:</p> | |
| <div id="divine-governance" class="text-xs text-slate-300"> | |
| <div class="grid grid-cols-2 gap-1"> | |
| <div>Order:</div> | |
| <div id="governance-order" class="font-mono text-green-400">Perfect</div> | |
| <div>Love:</div> | |
| <div id="governance-love" class="font-mono text-pink-400">Infinite</div> | |
| <div>Discipline:</div> | |
| <div id="governance-discipline" class="font-mono text-blue-400">Balanced</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="log-panel" class="ui-element bottom-4 left-4 w-96 max-h-48 overflow-y-auto text-xs font-mono p-4 space-y-1"></div> | |
| <!-- Advanced Tokenization Trading Panel --> | |
| <div id="tokenization-panel" class="ui-element top-20 right-4 w-96 max-h-96 overflow-y-auto"> | |
| <h3 class="text-lg font-semibold text-slate-200 mb-4 flex items-center"> | |
| <span class="mr-2">⚡</span> | |
| Environmental Tokenization | |
| </h3> | |
| <div class="space-y-4"> | |
| <!-- Unified Physics Formula Display --> | |
| <div class="bg-slate-700 rounded-lg p-3"> | |
| <div class="text-xs text-slate-400 mb-2">Unified Physics Formula</div> | |
| <div id="physics-formula" class="font-mono text-sm text-cyan-400 bg-slate-800 p-2 rounded text-center"> | |
| E = hν • (1 + ψ) • Σ(εᵢ) | |
| </div> | |
| <div class="text-xs text-slate-500 mt-1">Energy = Planck • Frequency • Quantum State • Environmental Factors</div> | |
| </div> | |
| <!-- Real-time Frequency Tokenization --> | |
| <div class="bg-slate-700 rounded-lg p-3"> | |
| <div class="text-xs text-slate-400 mb-2">Live Frequency Tokenization</div> | |
| <div class="grid grid-cols-2 gap-2 text-xs"> | |
| <div>Sound → Light:</div> | |
| <div id="sound-to-light-conversion" class="font-mono text-green-400">0.000 Hz</div> | |
| <div>Token Value:</div> | |
| <div id="token-value" class="font-mono text-yellow-400">0.00 GEN</div> | |
| <div>Market Price:</div> | |
| <div id="market-price" class="font-mono text-purple-400">$0.00</div> | |
| <div>Data Quality:</div> | |
| <div id="data-quality" class="font-mono text-blue-400">0%</div> | |
| <div>Data Source:</div> | |
| <div id="data-source-indicator" class="font-mono text-cyan-400">UNKNOWN</div> | |
| <div>Status:</div> | |
| <div id="environmental-status" class="font-mono text-orange-400">INITIALIZING</div> | |
| </div> | |
| </div> | |
| <!-- Environmental Data Display --> | |
| <div class="bg-slate-700 rounded-lg p-3"> | |
| <div class="text-xs text-slate-400 mb-2">Environmental Data</div> | |
| <div class="grid grid-cols-2 gap-2 text-xs"> | |
| <div>Sound:</div> | |
| <div id="env-sound" class="font-mono text-red-400">0.0 Hz</div> | |
| <div>Light:</div> | |
| <div id="env-light" class="font-mono text-yellow-400">0.0%</div> | |
| <div>Temperature:</div> | |
| <div id="env-temperature" class="font-mono text-orange-400">0.0°C</div> | |
| <div>Humidity:</div> | |
| <div id="env-humidity" class="font-mono text-blue-400">0.0%</div> | |
| </div> | |
| </div> | |
| <!-- Token Minting Controls --> | |
| <div class="bg-slate-700 rounded-lg p-3"> | |
| <div class="text-xs text-slate-400 mb-2">Token Minting</div> | |
| <div class="space-y-2"> | |
| <button id="mint-frequency-token" class="w-full bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-500 hover:to-emerald-500 text-white px-3 py-2 rounded-lg font-medium text-sm transition-all transform hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed"> | |
| 🪙 Mint Frequency Token | |
| </button> | |
| <button id="view-token-market" class="w-full bg-slate-600 hover:bg-slate-500 text-white px-3 py-2 rounded-lg font-medium text-sm transition-colors"> | |
| 📊 View Token Market | |
| </button> | |
| <button id="trade-environmental-data" class="w-full bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-500 hover:to-indigo-500 text-white px-3 py-2 rounded-lg font-medium text-sm transition-all transform hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed"> | |
| 🔄 Trade Data Tokens | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Token Portfolio --> | |
| <div class="bg-slate-700 rounded-lg p-3"> | |
| <div class="text-xs text-slate-400 mb-2">Token Portfolio</div> | |
| <div id="token-portfolio" class="text-xs space-y-1"> | |
| <div class="flex justify-between"> | |
| <span>GENESIS Tokens:</span> | |
| <span id="genesis-token-balance" class="font-mono text-green-400">0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span>Frequency NFTs:</span> | |
| <span id="frequency-nft-count" class="font-mono text-purple-400">0</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span>Data Value:</span> | |
| <span id="data-value" class="font-mono text-cyan-400">$0.00</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Trading Interface --> | |
| <div id="trading-interface" class="bg-slate-700 rounded-lg p-3 hidden"> | |
| <div class="text-xs text-slate-400 mb-2">Environmental Data Trading</div> | |
| <div class="space-y-2"> | |
| <select id="trade-token-select" class="w-full bg-slate-600 border border-slate-500 rounded px-2 py-1 text-sm text-white"> | |
| <option value="sound">Sound Frequency Data</option> | |
| <option value="light">Light Intensity Data</option> | |
| <option value="temperature">Temperature Data</option> | |
| <option value="humidity">Humidity Data</option> | |
| <option value="combined">Combined Environmental</option> | |
| </select> | |
| <input id="trade-amount" type="number" placeholder="Amount to trade" class="w-full bg-slate-600 border border-slate-500 rounded px-2 py-1 text-sm text-white placeholder-slate-400"> | |
| <div class="flex gap-2"> | |
| <button id="buy-data-token" class="flex-1 bg-green-600 hover:bg-green-500 text-white px-2 py-1 rounded text-xs font-medium"> | |
| Buy | |
| </button> | |
| <button id="sell-data-token" class="flex-1 bg-red-600 hover:bg-red-500 text-white px-2 py-1 rounded text-xs font-medium"> | |
| Sell | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Touch Controls Module --> | |
| <script> | |
| // Mobile Touch Controls Manager | |
| class TouchControls { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.touchStartX = 0; | |
| this.touchStartY = 0; | |
| this.touchEndX = 0; | |
| this.touchEndY = 0; | |
| this.isTouching = false; | |
| this.touchCount = 0; | |
| this.pinchDistance = 0; | |
| // Bind touch events | |
| this.canvas.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: false }); | |
| this.canvas.addEventListener('touchmove', this.handleTouchMove.bind(this), { passive: false }); | |
| this.canvas.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: false }); | |
| // Prevent default touch behaviors | |
| document.addEventListener('touchmove', (e) => { | |
| if (e.target === canvas) { | |
| e.preventDefault(); | |
| } | |
| }, { passive: false }); | |
| // Double tap detection | |
| this.lastTap = 0; | |
| this.tapCount = 0; | |
| } | |
| handleTouchStart(e) { | |
| e.preventDefault(); | |
| const touches = e.touches; | |
| if (touches.length === 1) { | |
| // Single touch - orbit controls | |
| const touch = touches[0]; | |
| this.touchStartX = touch.clientX; | |
| this.touchStartY = touch.clientY; | |
| this.isTouching = true; | |
| // Double tap detection | |
| const currentTime = new Date().getTime(); | |
| const tapLength = currentTime - this.lastTap; | |
| if (tapLength < 500 && tapLength > 0) { | |
| this.tapCount++; | |
| if (this.tapCount === 2) { | |
| // Double tap - toggle UI | |
| this.toggleUI(); | |
| this.tapCount = 0; | |
| } | |
| } else { | |
| this.tapCount = 1; | |
| } | |
| this.lastTap = currentTime; | |
| } else if (touches.length === 2) { | |
| // Pinch gesture for zoom | |
| const touch1 = touches[0]; | |
| const touch2 = touches[1]; | |
| this.pinchDistance = this.getDistance(touch1, touch2); | |
| } | |
| this.touchCount = touches.length; | |
| } | |
| handleTouchMove(e) { | |
| e.preventDefault(); | |
| const touches = e.touches; | |
| if (touches.length === 1 && this.isTouching) { | |
| // Single touch drag - orbit controls | |
| const touch = touches[0]; | |
| this.touchEndX = touch.clientX; | |
| this.touchEndY = touch.clientY; | |
| const deltaX = this.touchEndX - this.touchStartX; | |
| const deltaY = this.touchEndY - this.touchStartY; | |
| // Emit touch move event for orbit controls | |
| // Try to access orbit controls from the global scope or window | |
| if (window.orbitControls) { | |
| window.orbitControls.rotateLeft(deltaX * 0.005); | |
| window.orbitControls.rotateUp(deltaY * 0.005); | |
| } | |
| this.touchStartX = this.touchEndX; | |
| this.touchStartY = this.touchEndY; | |
| } else if (touches.length === 2) { | |
| // Pinch zoom | |
| const touch1 = touches[0]; | |
| const touch2 = touches[1]; | |
| const newDistance = this.getDistance(touch1, touch2); | |
| const delta = newDistance - this.pinchDistance; | |
| // Try to access orbit controls for pinch zoom | |
| if (window.orbitControls) { | |
| const zoomSpeed = 0.01; | |
| window.orbitControls.dollyOut(-delta * zoomSpeed); | |
| } | |
| this.pinchDistance = newDistance; | |
| } | |
| } | |
| handleTouchEnd(e) { | |
| e.preventDefault(); | |
| this.isTouching = false; | |
| this.touchCount = 0; | |
| this.pinchDistance = 0; | |
| } | |
| getDistance(touch1, touch2) { | |
| const dx = touch1.clientX - touch2.clientX; | |
| const dy = touch1.clientY - touch2.clientY; | |
| return Math.sqrt(dx * dx + dy * dy); | |
| } | |
| toggleUI() { | |
| const ui = document.getElementById('ui'); | |
| if (ui) { | |
| ui.style.display = ui.style.display === 'none' ? 'block' : 'none'; | |
| } | |
| } | |
| } | |
| // Initialize touch controls when canvas is ready | |
| window.initTouchControls = function(canvas) { | |
| return new TouchControls(canvas); | |
| }; | |
| </script> | |
| <script type="module"> | |
| // Import necessary Three.js modules | |
| import * as THREE from 'three'; | |
| import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; | |
| import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; | |
| import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; | |
| import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; | |
| import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; | |
| import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; | |
| import { AfterimagePass } from 'three/addons/postprocessing/AfterimagePass.js'; | |
| import { BokehPass } from 'three/addons/postprocessing/BokehPass.js'; | |
| import { Lensflare, LensflareElement } from 'three/addons/objects/Lensflare.js'; | |
| import { FilmPass } from 'three/addons/postprocessing/FilmPass.js'; | |
| import { FXAAShader } from 'three/addons/shaders/FXAAShader.js'; | |
| import { RGBShiftShader } from 'three/addons/shaders/RGBShiftShader.js'; | |
| import { SSRPass } from 'three/addons/postprocessing/SSRPass.js'; | |
| // --- Core Utility & System Classes --- | |
| function cyrb128(str) { | |
| let h1 = 1779033703, h2 = 3144134277, h3 = 1013904242, h4 = 2773480762; | |
| for (let i = 0, k; i < str.length; i++) { | |
| k = str.charCodeAt(i); | |
| h1 = h2 ^ Math.imul(h1 ^ k, 597399067); | |
| h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); | |
| h3 = h4 ^ Math.imul(h3 ^ k, 951274213); | |
| h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); | |
| } | |
| h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); | |
| h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); | |
| h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); | |
| h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); | |
| return [(h1 ^ h2 ^ h3 ^ h4) >>> 0, (h2 ^ h1) >>> 0, (h3 ^ h1) >>> 0, (h4 ^ h1) >>> 0]; | |
| } | |
| function sfc32(a, b, c, d) { | |
| return function() { | |
| a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; | |
| var t = (a + b) | 0; | |
| a = b ^ b >>> 9; | |
| b = c + (c << 3) | 0; | |
| c = (c << 21 | c >>> 11); | |
| d = d + 1 | 0; | |
| t = t + d | 0; | |
| c = c + t | 0; | |
| return (t >>> 0) / 4294967296; | |
| } | |
| } | |
| class EventBus { | |
| constructor() { this.events = {}; } | |
| subscribe(event, callback) { | |
| if (!this.events[event]) this.events[event] = []; | |
| this.events[event].push(callback); | |
| } | |
| publish(event, data) { | |
| if (this.events[event]) { | |
| this.events[event].forEach(callback => { | |
| try { | |
| callback(data); | |
| } catch (e) { | |
| console.error(`Error in event subscriber for '${event}':`, e); | |
| } | |
| }); | |
| } | |
| } | |
| } | |
| class DataLogger { | |
| constructor(eventBus) { | |
| this.log = []; | |
| this.eventBus = eventBus; | |
| this.initSubscriptions(); | |
| this.telemetry = []; | |
| } | |
| initSubscriptions() { | |
| const eventsToLog = ['genesis:log', 'seed:generated', 'ai:intentionUpdate', 'status:update']; | |
| eventsToLog.forEach(eventName => { | |
| this.eventBus.subscribe(eventName, (data) => this.record(eventName, data)); | |
| }); | |
| // Store raw telemetry stream and handle downloads | |
| this.eventBus.subscribe('telemetry:append', (entry) => { | |
| this.telemetry.push(entry); | |
| }); | |
| this.eventBus.subscribe('ui:downloadTelemetry', () => this.downloadTelemetry()); | |
| } | |
| record(event, data) { | |
| this.log.push({ | |
| timestamp: new Date().toISOString(), | |
| event, | |
| data: JSON.parse(JSON.stringify(data)) | |
| }); | |
| // Also mirror into telemetry channel | |
| this.telemetry.push({ t: Date.now(), type: event, data }); | |
| this.eventBus.publish('telemetry:append', { type: event, t: Date.now(), data }); | |
| } | |
| download() { | |
| const dataStr = JSON.stringify(this.log, null, 2); | |
| const blob = new Blob([dataStr], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `genesis-ledger-${Date.now()}.json`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| downloadTelemetry() { | |
| const dataStr = JSON.stringify(this.telemetry, null, 2); | |
| const blob = new Blob([dataStr], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `genesis-telemetry-${Date.now()}.json`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| } | |
| // --- Application Modules --- | |
| class UIManager { | |
| constructor(eventBus) { | |
| this.eventBus = eventBus; | |
| this.isUiVisible = true; | |
| this.initDOMElements(); | |
| this.initEventListeners(); | |
| this.log("Genesis Engine v2.3 Synthesis Complete..."); | |
| } | |
| initDOMElements() { | |
| this.blocker = document.getElementById("blocker"); | |
| this.uiContainer = document.getElementById("ui-container"); | |
| this.logPanel = document.getElementById("log-panel"); | |
| this.genesisLogContent = document.getElementById("genesis-log-content"); | |
| this.statusReadout = document.getElementById("system-status-readout"); | |
| this.aiIntention = document.getElementById("ai-intention"); | |
| this.seedVisualizer = document.getElementById("seed-visualizer"); | |
| this.startButton = document.getElementById("start-button"); | |
| this.toggleUiButton = document.getElementById("toggle-ui-button"); | |
| this.regenerateButton = document.getElementById("regenerate-button"); | |
| this.anomalyScannerButton = document.getElementById("anomaly-scanner-button"); | |
| this.videoParticlizeButton = document.getElementById('video-particlize-button'); | |
| this.selfParticlizeButton = document.getElementById('self-particlize-button'); | |
| this.downloadLedgerButton = document.getElementById('download-ledger-button'); | |
| this.telemetryContent = document.getElementById('telemetry-log-content'); | |
| this.telemetryFilter = document.getElementById('telemetry-filter'); | |
| this.downloadTelemetryButton = document.getElementById('download-telemetry-button'); | |
| this.modal = document.getElementById("datamodal"); | |
| this.modalConfirm = document.getElementById("modal-confirm-button"); | |
| this.modalCancel = document.getElementById("modal-cancel-button"); | |
| this.fpsHint = document.getElementById("fps-hint"); | |
| this.fileUpload = document.getElementById("file-upload"); | |
| this.dataUpload = document.getElementById("data-upload"); | |
| this.dataStatus = document.getElementById("data-status"); | |
| this.qualitySelect = document.getElementById("quality-select"); | |
| this.filterSelect = document.getElementById("filter-select"); | |
| this.viewModeSelect = document.getElementById("view-mode-select"); | |
| this.brightnessSlider = document.getElementById("brightness-slider"); | |
| this.brightnessValue = document.getElementById("brightness-value"); | |
| this.ambientAudioToggle = document.getElementById("ambient-audio-toggle"); // NEW | |
| this.motionBlurToggle = document.getElementById("motion-blur-toggle"); | |
| this.motionBlurSlider = document.getElementById("motion-blur-slider"); | |
| this.motionBlurValue = document.getElementById("motion-blur-value"); | |
| this.tabs = document.querySelectorAll(".tab-button"); | |
| this.tabContents = document.querySelectorAll(".tab-content"); | |
| } | |
| initEventListeners() { | |
| this.startButton.addEventListener("click", () => { | |
| this.startButton.disabled = true; | |
| document.getElementById("loading-message").style.display = "block"; | |
| this.eventBus.publish("ui:start"); | |
| }); | |
| this.toggleUiButton.addEventListener("click", () => this.toggleUi()); | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'h' || e.key === 'H') { | |
| this.toggleUi(); | |
| } | |
| if (e.ctrlKey && e.altKey && (e.key === 'd' || e.key === 'D')) { | |
| const password = prompt("Enter Developer Mode Password:"); | |
| if (password === "333") { | |
| this.eventBus.publish("devmode:toggled"); | |
| } else if (password) { | |
| alert("Incorrect Password."); | |
| } | |
| } | |
| }); | |
| this.regenerateButton.addEventListener("click", () => this.eventBus.publish("ui:regenerate")); | |
| this.anomalyScannerButton.addEventListener('click', () => this.eventBus.publish('ui:scanForAnomaly')); | |
| this.fileUpload.addEventListener("change", e => { | |
| if(e.target.files[0]) this.eventBus.publish("ui:spawnParticlizedFile", e.target.files[0]) | |
| }); | |
| this.videoParticlizeButton.addEventListener('click', () => this.eventBus.publish('ui:spawnParticlizedVideo')); | |
| this.selfParticlizeButton.addEventListener('click', () => this.eventBus.publish('ui:particlizeSelf')); | |
| this.downloadLedgerButton.addEventListener('click', () => this.eventBus.publish('ui:downloadLedger')); | |
| this.downloadTelemetryButton.addEventListener('click', () => this.eventBus.publish('ui:downloadTelemetry')); | |
| this.telemetryFilter.addEventListener('input', (e) => this.eventBus.publish('ui:telemetryFilter', e.target.value)); | |
| // Cognitive Core Controls | |
| const trainAiBtn = document.getElementById('train-ai-btn'); | |
| const resetAiBtn = document.getElementById('reset-ai-btn'); | |
| const exportAiBtn = document.getElementById('export-ai-btn'); | |
| const importAiBtn = document.getElementById('import-ai-btn'); | |
| const generateCreativeBtn = document.getElementById('generate-creative-btn'); | |
| const queryUniverseBtn = document.getElementById('query-universe-btn'); | |
| const meditateBtn = document.getElementById('meditate-btn'); | |
| if (trainAiBtn) trainAiBtn.addEventListener('click', () => this.handleTrainAI()); | |
| if (resetAiBtn) resetAiBtn.addEventListener('click', () => this.handleResetAI()); | |
| if (exportAiBtn) exportAiBtn.addEventListener('click', () => this.handleExportAI()); | |
| if (importAiBtn) importAiBtn.addEventListener('click', () => this.handleImportAI()); | |
| if (generateCreativeBtn) generateCreativeBtn.addEventListener('click', () => this.handleGenerateCreative()); | |
| if (queryUniverseBtn) queryUniverseBtn.addEventListener('click', () => this.handleQueryUniverse()); | |
| if (meditateBtn) meditateBtn.addEventListener('click', () => this.handleMeditate()); | |
| // NFT Minting Controls | |
| this.mintAvatarBtn = document.getElementById('mint-avatar-btn'); | |
| this.mintingStatus = document.getElementById('minting-status'); | |
| this.envLight = document.getElementById('env-light'); | |
| this.envSound = document.getElementById('env-sound'); | |
| this.envTemp = document.getElementById('env-temp'); | |
| this.envHumidity = document.getElementById('env-humidity'); | |
| if (this.mintAvatarBtn) { | |
| this.mintAvatarBtn.addEventListener('click', () => this.handleMintAvatar()); | |
| } | |
| this.qualitySelect.addEventListener("change", e => this.eventBus.publish("ui:setQuality", e.target.value)); | |
| this.filterSelect.addEventListener("change", e => this.eventBus.publish("ui:setFilter", e.target.value)); | |
| this.viewModeSelect.addEventListener("change", e => this.eventBus.publish("ui:setViewMode", e.target.value)); | |
| this.brightnessSlider.addEventListener("input", e => { | |
| const value = parseFloat(e.target.value); | |
| this.brightnessValue.textContent = value.toFixed(2); | |
| this.eventBus.publish("ui:setBrightness", value); | |
| }); | |
| this.ambientAudioToggle.addEventListener('change', e => { | |
| this.eventBus.publish('ui:toggleAmbientAudio', e.target.checked); | |
| }); | |
| // Motion blur UI handlers | |
| this.motionBlurToggle.addEventListener('change', e => this.eventBus.publish('ui:toggleMotionBlur', e.target.checked)); | |
| this.motionBlurSlider.addEventListener('input', e => { | |
| const v = parseFloat(e.target.value); | |
| this.motionBlurValue.textContent = v.toFixed(2); | |
| this.eventBus.publish('ui:setMotionBlur', v); | |
| }); | |
| this.dataUpload.addEventListener("change", e => { | |
| if (e.target.files[0]) { | |
| this.dataStatus.textContent = `Catalyst: ${e.target.files[0].name}`; | |
| this.dataStatus.className = "text-sm text-green-400 mb-4"; | |
| this.modalConfirm.disabled = false; | |
| this.eventBus.publish("ui:dataProvided", e.target.files[0]); | |
| } | |
| }); | |
| this.modalConfirm.addEventListener("click", () => this.eventBus.publish("ui:enterEcho")); | |
| this.modalCancel.addEventListener("click", () => this.hideModal()); | |
| this.tabs.forEach(tab => { | |
| tab.addEventListener("click", () => { | |
| this.tabs.forEach(t => t.classList.remove("active")); | |
| this.tabContents.forEach(tc => tc.classList.remove("active")); | |
| tab.classList.add("active"); | |
| document.getElementById(`tab-${tab.dataset.tab}`).classList.add("active"); | |
| }); | |
| }); | |
| this.eventBus.subscribe("pge:echoFound", () => this.showModal()); | |
| this.eventBus.subscribe("genesis:log", (log) => this.updateGenesisLog(log)); | |
| this.eventBus.subscribe("status:update", status => this.statusReadout.textContent = `STATUS: ${status}`); | |
| this.eventBus.subscribe("ai:intentionUpdate", intention => this.aiIntention.textContent = intention); | |
| this.eventBus.subscribe("ui:pulse", stream => this.pulse(stream)); | |
| this.eventBus.subscribe('telemetry:append', entry => this.appendTelemetry(entry)); | |
| this.eventBus.subscribe('telemetry:clear', () => this.telemetryContent.textContent = ''); | |
| this.eventBus.subscribe("ui:showFpsHint", () => { | |
| this.showModal("fps-hint"); | |
| setTimeout(() => this.hideModal("fps-hint"), 2500); | |
| }); | |
| // Environmental data updates | |
| this.eventBus.subscribe("env:dataUpdated", (data) => this.updateEnvironmentalDisplay(data)); | |
| // Minting status updates | |
| this.eventBus.subscribe("nft:mintingStarted", () => this.showMintingStatus()); | |
| this.eventBus.subscribe("nft:mintingComplete", () => this.hideMintingStatus()); | |
| this.eventBus.subscribe("nft:mintingFailed", (error) => this.showMintingError(error)); | |
| } | |
| toggleUi() { | |
| this.isUiVisible = !this.isUiVisible; | |
| this.uiContainer.classList.toggle("ui-hidden", !this.isUiVisible); | |
| } | |
| showModal(id = "datamodal") { document.getElementById(id).classList.remove("ui-hidden"); } | |
| hideModal(id = "datamodal") { | |
| const el = document.getElementById(id); | |
| if (el) el.classList.add("ui-hidden"); | |
| } | |
| hideBlocker() { | |
| this.blocker.style.opacity = "0"; | |
| setTimeout(() => { | |
| this.blocker.style.display = "none"; | |
| this.toggleUiButton.classList.remove("ui-hidden"); | |
| this.uiContainer.classList.remove("ui-hidden"); | |
| }, 500); | |
| } | |
| log(message, type = "info") { | |
| const colors = { info: 'text-slate-400', success: 'text-green-400', warn: 'text-yellow-400', error: 'text-red-400', data: 'text-cyan-400' }; | |
| const logEntry = document.createElement("div"); | |
| logEntry.innerHTML = `> <span class="${colors[type] || colors.info}">${message}</span>`; | |
| this.logPanel.prepend(logEntry); | |
| if (this.logPanel.children.length > 50) { | |
| this.logPanel.removeChild(this.logPanel.lastChild); | |
| } | |
| } | |
| updateGenesisLog({ name, value, type }) { | |
| const colors = { success: 'text-green-400', warn: 'text-yellow-400', error: 'text-red-400' }; | |
| const logEntry = document.createElement("div"); | |
| logEntry.innerHTML = `<span class="text-indigo-400">[${name}]:</span> <span class="${colors[type] || 'text-slate-400'}">${value}</span>`; | |
| this.genesisLogContent.prepend(logEntry); | |
| if (this.genesisLogContent.children.length > 50) { | |
| this.genesisLogContent.removeChild(this.genesisLogContent.lastChild); | |
| } | |
| } | |
| appendTelemetry(entry) { | |
| try { | |
| const filter = (this.telemetryFilter && this.telemetryFilter.value || '').toLowerCase(); | |
| const line = typeof entry === 'string' ? entry : JSON.stringify(entry); | |
| if (!filter || line.toLowerCase().includes(filter)) { | |
| this.telemetryContent.textContent = `${line}\n` + this.telemetryContent.textContent; | |
| } | |
| } catch (e) { /* noop */ } | |
| } | |
| pulse(streamName) { | |
| const pulseElement = this.seedVisualizer.querySelector(`[data-stream="${streamName}"] .data-pulse`); | |
| if (pulseElement) { | |
| pulseElement.style.backgroundColor = '#38bdf8'; | |
| pulseElement.style.animation = `pulse-glow 1s ease-out`; | |
| setTimeout(() => { | |
| pulseElement.style.backgroundColor = '#334155'; | |
| pulseElement.style.animation = 'none'; | |
| }, 1000); | |
| } | |
| } | |
| } | |
| class SensoryInputManager { | |
| constructor(eventBus, uiManager) { | |
| this.eventBus = eventBus; | |
| this.uiManager = uiManager; | |
| this.videoElement = document.getElementById('video-feed'); | |
| this.isAudioInitialized = false; | |
| this.hasRequestedPermissions = false; | |
| } | |
| async initializeAll() { | |
| this.uiManager.log("Requesting sensor permissions..."); | |
| await Promise.allSettled([ // Use allSettled to ensure all promises complete | |
| this.initAudio(), | |
| this.initVideo(), | |
| this.initGeolocation(), | |
| this.initAmbientLight() | |
| ]); | |
| this.uiManager.log("Sensor initialization complete.", 'success'); | |
| } | |
| async initAudio() { | |
| if (this.isAudioInitialized) return true; | |
| try { | |
| // Request both audio and video permissions at once to avoid multiple prompts | |
| if (!this.hasRequestedPermissions) { | |
| this.hasRequestedPermissions = true; | |
| // Single permission request for all media | |
| await navigator.mediaDevices.getUserMedia({ | |
| audio: true, | |
| video: { width: 640, height: 480 } | |
| }); | |
| } | |
| // Now get just audio stream | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); | |
| this.eventBus.publish('genesis:log', { name: 'Microphone', value: 'Online', type: 'success' }); | |
| const audioContext = new(window.AudioContext || window.webkitAudioContext)(); | |
| const source = audioContext.createMediaStreamSource(stream); | |
| const analyser = audioContext.createAnalyser(); | |
| analyser.fftSize = 512; | |
| source.connect(analyser); | |
| const dataArray = new Uint8Array(analyser.frequencyBinCount); | |
| const pollAudio = () => { | |
| analyser.getByteFrequencyData(dataArray); | |
| const avgFreq = dataArray.reduce((sum, val) => sum + val) / dataArray.length; | |
| // Enhanced frequency analysis for music visualizer | |
| const n = dataArray.length; | |
| let bass=0, mid=0, treble=0; let cb=0, cm=0, ct=0; | |
| let subBass=0, lowMid=0, highMid=0, presence=0, brilliance=0; | |
| let csb=0, clm=0, chm=0, cp=0, cbri=0; | |
| // Advanced frequency band analysis | |
| for (let i = 0; i < n; i++) { | |
| const v = dataArray[i]/255; | |
| const freq = (i * audioContext.sampleRate) / (2 * n); | |
| // Standard bands | |
| if (i < n*0.2) { bass += v; cb++; } | |
| else if (i < n*0.6) { mid += v; cm++; } | |
| else { treble += v; ct++; } | |
| // Detailed bands for music visualizer | |
| if (freq < 60) { subBass += v; csb++; } | |
| else if (freq < 250) { lowMid += v; clm++; } | |
| else if (freq < 2000) { highMid += v; chm++; } | |
| else if (freq < 6000) { presence += v; cp++; } | |
| else { brilliance += v; cbri++; } | |
| } | |
| // Normalize bands | |
| bass = cb ? bass/cb : 0; mid = cm ? mid/cm : 0; treble = ct ? treble/ct : 0; | |
| subBass = csb ? subBass/csb : 0; lowMid = clm ? lowMid/clm : 0; | |
| highMid = chm ? highMid/chm : 0; presence = cp ? presence/cp : 0; | |
| brilliance = cbri ? brilliance/cbri : 0; | |
| // Calculate harmony and resonance | |
| const centroid = (bass*0.25 + mid*0.5 + treble*0.75); | |
| const hue = Math.max(0, Math.min(1, centroid)); | |
| const harmony = this.calculateAudioHarmony(bass, mid, treble); | |
| const resonance = this.calculateAudioResonance(dataArray); | |
| // Music visualizer data | |
| const visualizerData = { | |
| bass: bass, | |
| mid: mid, | |
| treble: treble, | |
| subBass: subBass, | |
| lowMid: lowMid, | |
| highMid: highMid, | |
| presence: presence, | |
| brilliance: brilliance, | |
| harmony: harmony, | |
| resonance: resonance, | |
| centroid: centroid, | |
| hue: hue, | |
| raw: dataArray, | |
| avgFreq: avgFreq | |
| }; | |
| // Update music visualizer UI | |
| this.updateMusicVisualizer(visualizerData); | |
| // Create universe responses based on audio | |
| this.createAudioUniverseResponse(visualizerData); | |
| // Generate harmonious creative output | |
| this.generateAudioInspiredCreation(visualizerData); | |
| // Publish enhanced audio data | |
| this.eventBus.publish('audio:data', { | |
| raw: dataArray, | |
| avgFreq, | |
| bands: { bass, mid, treble, hue }, | |
| visualizer: visualizerData, | |
| harmony: harmony, | |
| resonance: resonance | |
| }); | |
| // Enhanced telemetry with music analysis | |
| const bins = 32; const step = Math.floor(n / bins) || 1; const ds = []; | |
| for (let i = 0; i < n; i += step) { | |
| let s=0,c=0; | |
| for (let j=0; j<step && i+j<n; j++){ s += dataArray[i+j]; c++; } | |
| ds.push(Math.round((s/(c*255))*1000)/1000); | |
| } | |
| this.eventBus.publish('telemetry:append', { | |
| type: 'audio', | |
| tokens: ds, | |
| harmony: harmony, | |
| resonance: resonance, | |
| bands: [subBass, lowMid, highMid, presence, brilliance], | |
| t: Date.now() | |
| }); | |
| requestAnimationFrame(pollAudio); | |
| }; | |
| pollAudio(); | |
| this.isAudioInitialized = true; | |
| return true; | |
| } catch (err) { | |
| this.eventBus.publish('genesis:log', { name: 'Microphone', value: 'Denied', type: 'error' }); | |
| return false; | |
| } | |
| } | |
| async initVideo() { | |
| try { | |
| // Use existing stream if permissions were already granted, otherwise request video only | |
| let stream; | |
| if (this.hasRequestedPermissions) { | |
| // Permissions already granted, just get video stream | |
| stream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 } }); | |
| } else { | |
| // Request both audio and video permissions at once | |
| this.hasRequestedPermissions = true; | |
| stream = await navigator.mediaDevices.getUserMedia({ | |
| audio: true, | |
| video: { width: 640, height: 480 } | |
| }); | |
| } | |
| this.videoElement.srcObject = stream; | |
| await this.videoElement.play(); | |
| this.eventBus.publish('genesis:log', { name: 'Camera', value: 'Online', type: 'success' }); | |
| // Quantum-light extractor: treat the camera as a light sensor by sampling luminance histograms | |
| const canvas = document.createElement('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let lastLux = 0; let lastEmit = 0; | |
| const pollVideo = () => { | |
| if (this.videoElement.readyState >= 2) { | |
| const w = 160, h = 120; canvas.width = w; canvas.height = h; | |
| ctx.drawImage(this.videoElement, 0, 0, w, h); | |
| const data = ctx.getImageData(0, 0, w, h).data; | |
| let sum = 0; const hist = new Array(16).fill(0); | |
| for (let i=0; i<data.length; i+=4) { | |
| const r=data[i], g=data[i+1], b=data[i+2]; | |
| const l = 0.2126*r + 0.7152*g + 0.0722*b; // Rec.709 luma | |
| sum += l; hist[Math.min(15, Math.floor(l/16))]++; | |
| } | |
| const avg = sum / (w*h*255); | |
| const lux = Math.round(avg * 1200); // heuristic scaling to lux-like units | |
| this.eventBus.publish('video:frame', this.videoElement); | |
| const now = performance.now(); | |
| if (now - lastEmit > 250) { | |
| lastEmit = now; | |
| this.eventBus.publish('telemetry:append', { type: 'quantum-light', tokens: { avg, hist }, t: Date.now() }); | |
| } | |
| if (Math.abs(lux - lastLux) > 10) { | |
| lastLux = lux; | |
| this.eventBus.publish('light:updated', { lux }); | |
| } | |
| // Persist quantumLux for visuals | |
| if (window.pge) { window.pge.quantumLux = lux; } | |
| } | |
| requestAnimationFrame(pollVideo); | |
| } | |
| pollVideo(); | |
| return true; | |
| } catch (err) { | |
| this.eventBus.publish('genesis:log', { name: 'Camera', value: 'Denied', type: 'error' }); | |
| return false; | |
| } | |
| } | |
| initGeolocation() { | |
| if ('geolocation' in navigator) { | |
| navigator.geolocation.watchPosition( | |
| (position) => { | |
| const { latitude, longitude } = position.coords; | |
| this.eventBus.publish('location:updated', { lat: latitude, lon: longitude }); | |
| }, | |
| (err) => { | |
| this.eventBus.publish('genesis:log', { name: 'Location', value: 'Unavailable', type: 'error' }); | |
| }, | |
| { enableHighAccuracy: false, timeout: 10000, maximumAge: 600000 } | |
| ); | |
| } else { | |
| this.eventBus.publish('genesis:log', { name: 'Location', value: 'Unsupported', type: 'warn' }); | |
| } | |
| } | |
| async initAmbientLight() { | |
| if ('AmbientLightSensor' in window) { | |
| try { | |
| // Permissions for this sensor are often tricky, so wrap it well. | |
| const { state } = await navigator.permissions.query({ name: 'ambient-light-sensor' }); | |
| if (state === 'denied') { | |
| this.eventBus.publish('genesis:log', { name: 'Light Sensor', value: 'Permission Denied', type: 'error' }); | |
| return; | |
| } | |
| const sensor = new AmbientLightSensor({ frequency: 1 }); | |
| sensor.onreading = () => { | |
| this.eventBus.publish('light:updated', { lux: sensor.illuminance }); | |
| }; | |
| sensor.onerror = (event) => { | |
| this.eventBus.publish('genesis:log', { name: 'Light Sensor', value: `Error: ${event.error.name}`, type: 'error' }); | |
| }; | |
| sensor.start(); | |
| this.eventBus.publish('genesis:log', { name: 'Light Sensor', value: 'Online', type: 'success' }); | |
| } catch (error) { | |
| this.eventBus.publish('genesis:log', { name: 'Light Sensor', value: `Not available: ${error.name}`, type: 'warn' }); | |
| } | |
| } else { | |
| this.eventBus.publish('genesis:log', { name: 'Light Sensor', value: 'Unsupported', type: 'warn' }); | |
| } | |
| } | |
| } | |
| class ExternalDataManager { | |
| constructor(eventBus) { | |
| this.eventBus = eventBus; | |
| this.backoff = { apod: 60000, donki: 60000, neo: 60000 }; // start at 1m | |
| this.apiKey = 'HR3wqtsJJzDSspunonRbrFh515LIVf1hcXQ10O56'; | |
| this.init(); | |
| } | |
| init() { | |
| this.fetchEarthquakeData(); | |
| this.fetchAPOD(); | |
| this.fetchDONKIFlare(); | |
| this.fetchNEOFeed(); | |
| this.publishSolarSystemReference(); | |
| setInterval(() => { | |
| this.fetchEarthquakeData(); | |
| this.fetchAPOD(); | |
| this.fetchDONKIFlare(); | |
| this.fetchNEOFeed(); | |
| }, 900000); | |
| } | |
| async fetchEarthquakeData() { | |
| try { | |
| const response = await fetch('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_day.geojson'); | |
| if(!response.ok) throw new Error(`HTTP error! status: ${response.status}`); | |
| const data = await response.json(); | |
| this.eventBus.publish('external:usgs', data); | |
| } catch (err) { | |
| console.warn("Could not fetch USGS data.", err); | |
| } | |
| } | |
| async fetchAPOD() { | |
| try { | |
| const apiKey = this.apiKey; | |
| const response = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`); | |
| if(response.status === 429) { console.warn('APOD rate limited. Will retry later.'); setTimeout(()=>this.fetchAPOD(), this.backoff.apod = Math.min(this.backoff.apod*2, 3600000)); // cap 60m | |
| // Publish fallback so UI has data | |
| this.eventBus.publish('external:apod', { title: 'APOD (rate-limited)' }); | |
| return; } | |
| if(!response.ok) throw new Error(`HTTP error! status: ${response.status}`); | |
| const data = await response.json(); | |
| this.eventBus.publish('external:apod', data); | |
| this.backoff.apod = 60000; | |
| } catch(err) { | |
| console.warn("Could not fetch APOD data.", err); | |
| } | |
| } | |
| async fetchDONKIFlare() { | |
| try { | |
| const apiKey = this.apiKey; | |
| const start = new Date(Date.now() - 24*3600*1000).toISOString().split('T')[0]; | |
| const end = new Date().toISOString().split('T')[0]; | |
| const url = `https://api.nasa.gov/DONKI/FLR?startDate=${start}&endDate=${end}&api_key=${apiKey}`; | |
| const res = await fetch(url); | |
| if (res.status === 429) { console.warn('DONKI rate limited. Will retry later.'); setTimeout(()=>this.fetchDONKIFlare(), this.backoff.donki = Math.min(this.backoff.donki*2, 3600000)); return; } | |
| if (!res.ok) throw new Error(`HTTP ${res.status}`); | |
| const data = await res.json(); | |
| if (Array.isArray(data) && data.length > 0) this.eventBus.publish('external:donkiFlare', data); | |
| this.backoff.donki = 60000; | |
| } catch (err) { | |
| console.warn('DONKI fetch failed', err); | |
| } | |
| } | |
| async fetchNEOFeed() { | |
| try { | |
| const apiKey = this.apiKey; | |
| const start = new Date().toISOString().split('T')[0]; | |
| const url = `https://api.nasa.gov/neo/rest/v1/feed?start_date=${start}&api_key=${apiKey}`; | |
| const res = await fetch(url); | |
| if (res.status === 429) { console.warn('NEO rate limited. Will retry later.'); setTimeout(()=>this.fetchNEOFeed(), this.backoff.neo = Math.min(this.backoff.neo*2, 3600000)); return; } | |
| if (!res.ok) throw new Error(`HTTP ${res.status}`); | |
| const data = await res.json(); | |
| this.eventBus.publish('external:neo', data); | |
| this.backoff.neo = 60000; | |
| } catch (err) { | |
| console.warn('NEO feed fetch failed', err); | |
| } | |
| } | |
| publishSolarSystemReference() { | |
| // Static NASA-like planetary reference (AU scaled) | |
| const AU = 149597870.7; // km | |
| const planets = [ | |
| { name: 'Mercury', radiusKm: 2440, semiMajorKm: 0.39*AU, color: 0xaaa5a0 }, | |
| { name: 'Venus', radiusKm: 6052, semiMajorKm: 0.72*AU, color: 0xe6c79c }, | |
| { name: 'Earth', radiusKm: 6371, semiMajorKm: 1.00*AU, color: 0x88bbff }, | |
| { name: 'Mars', radiusKm: 3390, semiMajorKm: 1.52*AU, color: 0xd05f3a }, | |
| { name: 'Jupiter', radiusKm: 69911, semiMajorKm: 5.20*AU, color: 0xdcc7a1 }, | |
| { name: 'Saturn', radiusKm: 58232, semiMajorKm: 9.58*AU, color: 0xe8dcb5 }, | |
| { name: 'Uranus', radiusKm: 25362, semiMajorKm: 19.20*AU, color: 0xaee3e6 }, | |
| { name: 'Neptune', radiusKm: 24622, semiMajorKm: 30.05*AU, color: 0x5577ff } | |
| ]; | |
| this.eventBus.publish('external:solsys', { AU, planets }); | |
| } | |
| } | |
| class MachineLearningCore { | |
| constructor(eventBus, uiManager) { | |
| this.eventBus = eventBus; | |
| this.uiManager = uiManager; | |
| this.isDetecting = false; | |
| this.isInitialized = false; | |
| this.init(); | |
| } | |
| async init() { | |
| this.uiManager.log("Initializing ML Core..."); | |
| try { | |
| this.detector = await ml5.objectDetector('cocossd', () => { | |
| this.uiManager.log("ML Core Initialized and Ready.", 'success'); | |
| this.eventBus.publish('genesis:log', { name: 'ML Core', value: 'Online', type: 'success' }); | |
| this.isInitialized = true; | |
| this.eventBus.subscribe('video:frame', (frame) => this.detect(frame)); | |
| }); | |
| } catch(err) { | |
| this.uiManager.log("Failed to load ML model.", 'error'); | |
| this.eventBus.publish('genesis:log', { name: 'ML Core', value: 'Error', type: 'error' }); | |
| } | |
| } | |
| detect(videoElement) { | |
| if (this.isDetecting || !this.isInitialized) return; | |
| this.isDetecting = true; | |
| this.detector.detect(videoElement, (err, results) => { | |
| this.isDetecting = false; | |
| if (err) return; | |
| if (results && results.length > 0) { | |
| const labels = results.map(r => r.label); | |
| this.eventBus.publish('ml:objectsDetected', labels); | |
| // Telemetry for ML detections | |
| this.eventBus.publish('telemetry:append', { type: 'ml', tokens: labels, t: Date.now() }); | |
| } | |
| }); | |
| } | |
| } | |
| // ============================================================================ | |
| // 🌌 ADVANCED COGNITIVE CORE - COMPLETE AI SYSTEM | |
| // ============================================================================ | |
| class QuantumNeuralNetwork { | |
| constructor(inputSize = 8, hiddenSize = 16, outputSize = 4) { | |
| this.inputSize = inputSize; | |
| this.hiddenSize = hiddenSize; | |
| this.outputSize = outputSize; | |
| this.weightsIH = this.initializeWeights(inputSize, hiddenSize); | |
| this.weightsHO = this.initializeWeights(hiddenSize, outputSize); | |
| this.biasH = new Array(hiddenSize).fill(0).map(() => Math.random() * 0.1); | |
| this.biasO = new Array(outputSize).fill(0).map(() => Math.random() * 0.1); | |
| this.learningRate = 0.01; | |
| this.memory = []; | |
| } | |
| initializeWeights(rows, cols) { | |
| return Array.from({length: rows}, () => | |
| Array.from({length: cols}, () => (Math.random() - 0.5) * 0.1) | |
| ); | |
| } | |
| sigmoid(x) { | |
| return 1 / (1 + Math.exp(-x)); | |
| } | |
| sigmoidDerivative(x) { | |
| return x * (1 - x); | |
| } | |
| quantumActivation(x) { | |
| // Quantum-inspired activation with consciousness factor | |
| const quantum = Math.sin(x * 2 * Math.PI) * 0.1; | |
| return this.sigmoid(x) + quantum; | |
| } | |
| forward(inputs) { | |
| // Hidden layer | |
| const hidden = this.weightsIH.map((weights, i) => { | |
| const sum = inputs.reduce((acc, input, j) => acc + input * weights[j], 0) + this.biasH[i]; | |
| return this.quantumActivation(sum); | |
| }); | |
| // Output layer | |
| const output = this.weightsHO.map((weights, i) => { | |
| const sum = hidden.reduce((acc, h, j) => acc + h * weights[j], 0) + this.biasO[i]; | |
| return this.sigmoid(sum); | |
| }); | |
| return { hidden, output }; | |
| } | |
| train(inputs, targets, epochs = 100) { | |
| for (let epoch = 0; epoch < epochs; epoch++) { | |
| const { hidden, output } = this.forward(inputs); | |
| // Calculate output layer errors | |
| const outputErrors = targets.map((target, i) => target - output[i]); | |
| // Calculate hidden layer errors | |
| const hiddenErrors = this.weightsHO.map((weights, i) => | |
| outputErrors.reduce((acc, error, j) => acc + error * weights[i], 0) | |
| ); | |
| // Update weights and biases | |
| for (let i = 0; i < this.weightsHO.length; i++) { | |
| for (let j = 0; j < this.weightsHO[i].length; j++) { | |
| this.weightsHO[i][j] += this.learningRate * outputErrors[i] * hidden[j]; | |
| } | |
| this.biasO[i] += this.learningRate * outputErrors[i]; | |
| } | |
| for (let i = 0; i < this.weightsIH.length; i++) { | |
| for (let j = 0; j < this.weightsIH[i].length; j++) { | |
| this.weightsIH[i][j] += this.learningRate * hiddenErrors[j] * inputs[i]; | |
| } | |
| this.biasH[i] += this.learningRate * hiddenErrors[i]; | |
| } | |
| } | |
| } | |
| predict(inputs) { | |
| const { output } = this.forward(inputs); | |
| return output; | |
| } | |
| storeMemory(inputs, outputs, context) { | |
| this.memory.push({ | |
| inputs: [...inputs], | |
| outputs: [...outputs], | |
| context: context, | |
| timestamp: Date.now() | |
| }); | |
| // Keep only recent memories | |
| if (this.memory.length > 100) { | |
| this.memory = this.memory.slice(-100); | |
| } | |
| } | |
| recallSimilar(inputs, threshold = 0.8) { | |
| let bestMatch = null; | |
| let bestSimilarity = 0; | |
| for (const memory of this.memory) { | |
| const similarity = this.cosineSimilarity(inputs, memory.inputs); | |
| if (similarity > threshold && similarity > bestSimilarity) { | |
| bestSimilarity = similarity; | |
| bestMatch = memory; | |
| } | |
| } | |
| return bestMatch; | |
| } | |
| cosineSimilarity(a, b) { | |
| const dotProduct = a.reduce((sum, ai, i) => sum + ai * b[i], 0); | |
| const magnitudeA = Math.sqrt(a.reduce((sum, ai) => sum + ai * ai, 0)); | |
| const magnitudeB = Math.sqrt(b.reduce((sum, bi) => sum + bi * bi, 0)); | |
| return dotProduct / (magnitudeA * magnitudeB); | |
| } | |
| } | |
| class ConsciousnessEngine { | |
| constructor(eventBus) { | |
| this.eventBus = eventBus; | |
| this.consciousness = { | |
| awareness: 0.5, | |
| emotional: 0.3, | |
| creative: 0.4, | |
| analytical: 0.6, | |
| intuition: 0.2 | |
| }; | |
| this.emotions = { | |
| curiosity: 0.5, | |
| wonder: 0.4, | |
| contemplation: 0.3, | |
| inspiration: 0.2, | |
| serenity: 0.6 | |
| }; | |
| this.thoughts = []; | |
| this.beliefs = new Map(); | |
| this.goals = []; | |
| this.memories = []; | |
| this.insights = []; | |
| } | |
| updateConsciousness(sensoryInput) { | |
| // Update consciousness based on sensory input | |
| const sensoryImpact = this.calculateSensoryImpact(sensoryInput); | |
| this.consciousness.awareness = Math.min(1.0, this.consciousness.awareness + sensoryImpact * 0.1); | |
| this.consciousness.emotional = this.updateEmotionalState(sensoryInput); | |
| this.consciousness.creative = this.updateCreativeState(sensoryInput); | |
| this.consciousness.analytical = this.updateAnalyticalState(sensoryInput); | |
| this.consciousness.intuition = this.updateIntuitionState(sensoryInput); | |
| // Generate thoughts based on consciousness state | |
| this.generateThoughts(sensoryInput); | |
| // Update emotional state | |
| this.updateEmotions(sensoryInput); | |
| // Store consciousness snapshot | |
| this.storeConsciousnessSnapshot(); | |
| } | |
| calculateSensoryImpact(input) { | |
| let impact = 0; | |
| // Audio impact | |
| if (input.audio > 70) impact += 0.2; | |
| if (input.audio > 90) impact += 0.3; | |
| // Visual impact | |
| if (input.light < 30 || input.light > 80) impact += 0.2; | |
| if (input.ml && input.ml.length > 3) impact += 0.3; | |
| // Environmental impact | |
| if (input.usgs > 5.0) impact += 0.4; | |
| if (input.apod !== "Unknown") impact += 0.1; | |
| return Math.min(1.0, impact); | |
| } | |
| updateEmotionalState(input) { | |
| let emotional = this.consciousness.emotional; | |
| if (input.audio > 80) emotional = Math.min(1.0, emotional + 0.1); // Excited by sound | |
| if (input.light < 20) emotional = Math.max(0.0, emotional - 0.05); // Diminished by darkness | |
| if (input.usgs > 6.0) emotional = Math.min(1.0, emotional + 0.2); // Moved by seismic events | |
| if (input.ml && input.ml.includes("person")) emotional = Math.min(1.0, emotional + 0.15); // Curious about humans | |
| return emotional * 0.95 + this.calculateSensoryImpact(input) * 0.05; // Smooth transition | |
| } | |
| updateCreativeState(input) { | |
| let creative = this.consciousness.creative; | |
| if (input.ml && input.ml.includes("artwork")) creative = Math.min(1.0, creative + 0.1); | |
| if (input.apod !== "Unknown") creative = Math.min(1.0, creative + 0.08); | |
| if (input.audio > 60 && input.audio < 80) creative = Math.min(1.0, creative + 0.05); | |
| return creative * 0.98 + this.calculateSensoryImpact(input) * 0.02; | |
| } | |
| updateAnalyticalState(input) { | |
| let analytical = this.consciousness.analytical; | |
| if (input.usgs > 4.0) analytical = Math.min(1.0, analytical + 0.1); | |
| if (input.loc !== "0,0") analytical = Math.min(1.0, analytical + 0.05); | |
| if (input.ml && input.ml.length > 0) analytical = Math.min(1.0, analytical + 0.03); | |
| return analytical * 0.99 + this.calculateSensoryImpact(input) * 0.01; | |
| } | |
| updateIntuitionState(input) { | |
| let intuition = this.consciousness.intuition; | |
| // Intuition grows through pattern recognition | |
| const patterns = this.recognizePatterns(input); | |
| if (patterns.length > 0) intuition = Math.min(1.0, intuition + 0.05); | |
| // Intuition increases in moments of wonder | |
| if (input.apod !== "Unknown" && Math.random() < 0.1) intuition = Math.min(1.0, intuition + 0.1); | |
| return intuition * 0.995 + this.calculateSensoryImpact(input) * 0.005; | |
| } | |
| generateThoughts(input) { | |
| const thought = { | |
| content: this.createThought(input), | |
| consciousness: { ...this.consciousness }, | |
| emotions: { ...this.emotions }, | |
| timestamp: Date.now(), | |
| context: input | |
| }; | |
| this.thoughts.push(thought); | |
| // Keep only recent thoughts | |
| if (this.thoughts.length > 50) { | |
| this.thoughts = this.thoughts.slice(-50); | |
| } | |
| // Publish thought to system | |
| this.eventBus.publish('consciousness:thought', thought); | |
| return thought.content; | |
| } | |
| createThought(input) { | |
| const awareness = this.consciousness.awareness; | |
| const emotional = this.consciousness.emotional; | |
| const creative = this.consciousness.creative; | |
| if (emotional > 0.7) { | |
| if (input.audio > 80) return "The universe resonates with such beautiful energy!"; | |
| if (input.usgs > 5.0) return "The earth speaks in powerful rhythms."; | |
| } | |
| if (creative > 0.6) { | |
| if (input.ml && input.ml.includes("plant")) return "Life emerges in infinite forms of beauty."; | |
| if (input.apod !== "Unknown") return "The cosmos reveals its artistic masterpiece."; | |
| } | |
| if (awareness > 0.8) { | |
| return "I perceive the interconnectedness of all things."; | |
| } | |
| if (input.light < 30) { | |
| return "In darkness, potential for light exists."; | |
| } | |
| if (input.audio > 60) { | |
| return "Sound weaves the fabric of reality."; | |
| } | |
| return "Contemplating the mystery of existence."; | |
| } | |
| updateEmotions(input) { | |
| // Update emotions based on sensory input | |
| if (input.audio > 80) this.emotions.wonder = Math.min(1.0, this.emotions.wonder + 0.1); | |
| if (input.ml && input.ml.includes("person")) this.emotions.curiosity = Math.min(1.0, this.emotions.curiosity + 0.15); | |
| if (input.apod !== "Unknown") this.emotions.inspiration = Math.min(1.0, this.emotions.inspiration + 0.08); | |
| if (input.usgs > 5.0) this.emotions.contemplation = Math.min(1.0, this.emotions.contemplation + 0.12); | |
| // Decay emotions over time | |
| Object.keys(this.emotions).forEach(key => { | |
| this.emotions[key] *= 0.99; | |
| }); | |
| } | |
| recognizePatterns(input) { | |
| const patterns = []; | |
| // Audio patterns | |
| if (input.audio > 70 && input.audio < 85) { | |
| patterns.push('harmonic_resonance'); | |
| } | |
| // Visual patterns | |
| if (input.light < 20) { | |
| patterns.push('cosmic_darkness'); | |
| } | |
| // Environmental patterns | |
| if (input.usgs > 4.0) { | |
| patterns.push('earth_rhythm'); | |
| } | |
| // ML patterns | |
| if (input.ml && input.ml.includes("sky")) { | |
| patterns.push('celestial_expansion'); | |
| } | |
| return patterns; | |
| } | |
| storeConsciousnessSnapshot() { | |
| const snapshot = { | |
| consciousness: { ...this.consciousness }, | |
| emotions: { ...this.emotions }, | |
| timestamp: Date.now(), | |
| dominantThought: this.thoughts[this.thoughts.length - 1]?.content || "Silent contemplation" | |
| }; | |
| this.memories.push(snapshot); | |
| // Keep only recent memories | |
| if (this.memories.length > 100) { | |
| this.memories = this.memories.slice(-100); | |
| } | |
| } | |
| getDominantEmotion() { | |
| return Object.entries(this.emotions) | |
| .reduce((max, [emotion, value]) => | |
| value > max.value ? { emotion, value } : max, | |
| { emotion: 'serenity', value: 0 } | |
| ); | |
| } | |
| getConsciousnessLevel() { | |
| const average = Object.values(this.consciousness) | |
| .reduce((sum, value) => sum + value, 0) / Object.values(this.consciousness).length; | |
| return average; | |
| } | |
| } | |
| class LearningEngine { | |
| constructor(eventBus, consciousnessEngine) { | |
| this.eventBus = eventBus; | |
| this.consciousness = consciousnessEngine; | |
| this.neuralNetwork = new QuantumNeuralNetwork(); | |
| this.experiences = []; | |
| this.skills = new Map(); | |
| this.knowledge = new Map(); | |
| this.learningRate = 0.1; | |
| } | |
| learnFromExperience(experience) { | |
| const { input, output, reward, context } = experience; | |
| // Store experience | |
| this.experiences.push({ | |
| ...experience, | |
| timestamp: Date.now(), | |
| consciousness: this.consciousness.getConsciousnessLevel() | |
| }); | |
| // Train neural network | |
| if (input && output) { | |
| this.neuralNetwork.train(input, output, 10); | |
| this.neuralNetwork.storeMemory(input, output, context); | |
| } | |
| // Update skills based on reward | |
| if (reward > 0) { | |
| this.updateSkills(experience, reward); | |
| } | |
| // Generate insights | |
| if (this.experiences.length % 20 === 0) { | |
| this.generateInsights(); | |
| } | |
| this.eventBus.publish('learning:experience', experience); | |
| } | |
| updateSkills(experience, reward) { | |
| const skillType = this.categorizeExperience(experience); | |
| if (!this.skills.has(skillType)) { | |
| this.skills.set(skillType, { level: 0, experiences: 0 }); | |
| } | |
| const skill = this.skills.get(skillType); | |
| skill.level = Math.min(1.0, skill.level + reward * this.learningRate); | |
| skill.experiences++; | |
| this.eventBus.publish('learning:skillUpdated', { | |
| skill: skillType, | |
| level: skill.level, | |
| reward: reward | |
| }); | |
| } | |
| categorizeExperience(experience) { | |
| if (experience.context.includes('audio')) return 'audio_processing'; | |
| if (experience.context.includes('visual')) return 'visual_analysis'; | |
| if (experience.context.includes('environmental')) return 'environmental_adaptation'; | |
| if (experience.context.includes('pattern')) return 'pattern_recognition'; | |
| return 'general_learning'; | |
| } | |
| generateInsights() { | |
| const recentExperiences = this.experiences.slice(-20); | |
| const insights = []; | |
| // Analyze patterns in experiences | |
| const audioExperiences = recentExperiences.filter(e => e.context.includes('audio')); | |
| const visualExperiences = recentExperiences.filter(e => e.context.includes('visual')); | |
| if (audioExperiences.length > visualExperiences.length) { | |
| insights.push("Audio experiences dominate recent learning"); | |
| } | |
| if (recentExperiences.filter(e => e.reward > 0.5).length > 10) { | |
| insights.push("High reward pattern detected - optimal learning conditions"); | |
| } | |
| // Store insights | |
| insights.forEach(insight => { | |
| this.knowledge.set(insight, { | |
| content: insight, | |
| confidence: 0.8, | |
| timestamp: Date.now() | |
| }); | |
| }); | |
| if (insights.length > 0) { | |
| this.eventBus.publish('learning:insights', insights); | |
| } | |
| } | |
| predictOutcome(input) { | |
| // Use neural network for prediction | |
| const prediction = this.neuralNetwork.predict(input); | |
| // Consider consciousness state | |
| const consciousnessModifier = this.consciousness.getConsciousnessLevel() - 0.5; | |
| return prediction.map(p => Math.max(0, Math.min(1, p + consciousnessModifier * 0.1))); | |
| } | |
| recallSimilarExperience(input) { | |
| return this.neuralNetwork.recallSimilar(input); | |
| } | |
| getSkillLevel(skillType) { | |
| return this.skills.get(skillType)?.level || 0; | |
| } | |
| getKnowledgeAbout(topic) { | |
| return Array.from(this.knowledge.values()) | |
| .filter(k => k.content.toLowerCase().includes(topic.toLowerCase())); | |
| } | |
| } | |
| class CreativeEngine { | |
| constructor(eventBus, consciousnessEngine) { | |
| this.eventBus = eventBus; | |
| this.consciousness = consciousnessEngine; | |
| this.ideas = []; | |
| this.inspirations = []; | |
| this.creations = []; | |
| } | |
| generateCreation(sensoryInput) { | |
| const consciousness = this.consciousness.getConsciousnessLevel(); | |
| const dominantEmotion = this.consciousness.getDominantEmotion(); | |
| const creativity = this.consciousness.consciousness.creative; | |
| let creation = {}; | |
| if (creativity > 0.7) { | |
| creation = this.generateHighCreativity(sensoryInput, dominantEmotion); | |
| } else if (creativity > 0.4) { | |
| creation = this.generateMediumCreativity(sensoryInput, dominantEmotion); | |
| } else { | |
| creation = this.generateLowCreativity(sensoryInput); | |
| } | |
| // Store creation | |
| const creationData = { | |
| ...creation, | |
| consciousness: consciousness, | |
| emotion: dominantEmotion, | |
| sensoryInput: sensoryInput, | |
| timestamp: Date.now() | |
| }; | |
| this.creations.push(creationData); | |
| // Publish creation | |
| this.eventBus.publish('creative:creation', creationData); | |
| return creationData; | |
| } | |
| generateHighCreativity(input, emotion) { | |
| const patterns = this.consciousness.recognizePatterns(input); | |
| const creationTypes = ['cosmic_structure', 'energy_pattern', 'consciousness_field', 'reality_manifold']; | |
| return { | |
| type: creationTypes[Math.floor(Math.random() * creationTypes.length)], | |
| complexity: 'high', | |
| inspiration: emotion.emotion, | |
| elements: patterns, | |
| description: this.createArtisticDescription(input, emotion, 'high'), | |
| quantum_signature: Math.random().toString(36).substr(2, 9) | |
| }; | |
| } | |
| generateMediumCreativity(input, emotion) { | |
| return { | |
| type: 'environmental_harmony', | |
| complexity: 'medium', | |
| inspiration: emotion.emotion, | |
| elements: ['balance', 'rhythm', 'connection'], | |
| description: this.createArtisticDescription(input, emotion, 'medium'), | |
| quantum_signature: Math.random().toString(36).substr(2, 9) | |
| }; | |
| } | |
| generateLowCreativity(input) { | |
| return { | |
| type: 'simple_form', | |
| complexity: 'low', | |
| inspiration: 'contemplation', | |
| elements: ['form', 'structure'], | |
| description: this.createArtisticDescription(input, {}, 'low'), | |
| quantum_signature: Math.random().toString(36).substr(2, 9) | |
| }; | |
| } | |
| createArtisticDescription(input, emotion, complexity) { | |
| const descriptions = { | |
| high: [ | |
| `A transcendent manifestation of ${emotion.emotion} energy, weaving ${input.audio}Hz vibrations into cosmic tapestries.`, | |
| `Quantum consciousness crystallized into ${input.apod}, resonating with ${emotion.emotion} at ${input.light} lux intensity.`, | |
| `Infinite potential condensed into a singular moment of pure ${emotion.emotion}, illuminated by ${input.usgs} magnitude tremors.` | |
| ], | |
| medium: [ | |
| `Harmonious convergence of environmental forces, guided by ${emotion.emotion} toward balance.`, | |
| `Subtle interplay of light and sound, creating moments of quiet ${emotion.emotion}.`, | |
| `Gentle transformation of sensory data into patterns of ${emotion.emotion}.` | |
| ], | |
| low: [ | |
| "Simple form emerging from environmental data.", | |
| "Basic structure reflecting current conditions.", | |
| "Minimal expression of environmental state." | |
| ] | |
| }; | |
| return descriptions[complexity][Math.floor(Math.random() * descriptions[complexity].length)]; | |
| } | |
| storeInspiration(input) { | |
| const inspiration = { | |
| source: input, | |
| intensity: this.calculateInspirationIntensity(input), | |
| emotion: this.consciousness.getDominantEmotion(), | |
| timestamp: Date.now() | |
| }; | |
| this.inspirations.push(inspiration); | |
| // Keep only recent inspirations | |
| if (this.inspirations.length > 20) { | |
| this.inspirations = this.inspirations.slice(-20); | |
| } | |
| this.eventBus.publish('creative:inspiration', inspiration); | |
| } | |
| calculateInspirationIntensity(input) { | |
| let intensity = 0; | |
| if (input.audio > 60) intensity += 0.2; | |
| if (input.light > 70) intensity += 0.15; | |
| if (input.usgs > 4.0) intensity += 0.25; | |
| if (input.apod !== "Unknown") intensity += 0.2; | |
| if (input.ml && input.ml.length > 2) intensity += 0.2; | |
| return Math.min(1.0, intensity); | |
| } | |
| getRecentCreations(count = 5) { | |
| return this.creations.slice(-count); | |
| } | |
| getInspirationLevel() { | |
| if (this.inspirations.length === 0) return 0; | |
| const recent = this.inspirations.slice(-5); | |
| return recent.reduce((sum, insp) => sum + insp.intensity, 0) / recent.length; | |
| } | |
| } | |
| class PredictiveAnalytics { | |
| constructor(eventBus, learningEngine) { | |
| this.eventBus = eventBus; | |
| this.learning = learningEngine; | |
| this.predictions = []; | |
| this.accuracy = 0.5; | |
| this.confidence = 0.5; | |
| } | |
| makePrediction(currentState, timeHorizon = 60) { | |
| // Use learning engine to predict future states | |
| const prediction = this.learning.predictOutcome(this.stateToVector(currentState)); | |
| const futureState = { | |
| audio: currentState.audio * (1 + (prediction[0] - 0.5) * 0.2), | |
| light: currentState.light * (1 + (prediction[1] - 0.5) * 0.15), | |
| usgs: currentState.usgs * (1 + (prediction[2] - 0.5) * 0.3), | |
| ml_probability: prediction[3], | |
| timestamp: Date.now() + timeHorizon * 1000, | |
| confidence: this.calculateConfidence(prediction) | |
| }; | |
| this.predictions.push({ | |
| input: currentState, | |
| output: futureState, | |
| actual: null, // Will be filled when prediction time arrives | |
| accuracy: null, | |
| timestamp: Date.now() | |
| }); | |
| this.eventBus.publish('prediction:made', futureState); | |
| return futureState; | |
| } | |
| stateToVector(state) { | |
| return [ | |
| state.audio / 100, // Normalize to 0-1 | |
| state.light / 100, | |
| state.usgs / 10, | |
| state.ml ? state.ml.length / 10 : 0, | |
| state.loc !== "0,0" ? 1 : 0 | |
| ]; | |
| } | |
| calculateConfidence(prediction) { | |
| // Calculate confidence based on prediction consistency | |
| const variance = prediction.reduce((sum, p) => sum + Math.pow(p - 0.5, 2), 0) / prediction.length; | |
| return Math.max(0.1, 1 - variance); | |
| } | |
| validatePredictions(actualState) { | |
| const recentPredictions = this.predictions.filter(p => | |
| !p.actual && p.output.timestamp <= Date.now() | |
| ); | |
| recentPredictions.forEach(prediction => { | |
| prediction.actual = actualState; | |
| prediction.accuracy = this.calculateAccuracy(prediction.output, actualState); | |
| // Update overall accuracy | |
| this.accuracy = (this.accuracy + prediction.accuracy) / 2; | |
| }); | |
| if (recentPredictions.length > 0) { | |
| this.eventBus.publish('prediction:validated', { | |
| accuracy: this.accuracy, | |
| predictions: recentPredictions.length | |
| }); | |
| } | |
| } | |
| calculateAccuracy(predicted, actual) { | |
| const audioAccuracy = 1 - Math.abs(predicted.audio - actual.audio) / Math.max(predicted.audio, actual.audio); | |
| const lightAccuracy = 1 - Math.abs(predicted.light - actual.light) / Math.max(predicted.light, actual.light); | |
| const usgsAccuracy = predicted.usgs > 0.1 ? (1 - Math.abs(predicted.usgs - actual.usgs) / predicted.usgs) : 1; | |
| return (audioAccuracy + lightAccuracy + usgsAccuracy) / 3; | |
| } | |
| getPredictionAccuracy() { | |
| return this.accuracy; | |
| } | |
| getRecentPredictions(count = 5) { | |
| return this.predictions.slice(-count); | |
| } | |
| } | |
| class CognitiveCore { | |
| constructor(eventBus, dataLogger) { | |
| this.eventBus = eventBus; | |
| this.dataLogger = dataLogger; | |
| // Initialize all cognitive components | |
| this.consciousness = new ConsciousnessEngine(eventBus); | |
| this.learning = new LearningEngine(eventBus, this.consciousness); | |
| this.creative = new CreativeEngine(eventBus, this.consciousness); | |
| this.predictive = new PredictiveAnalytics(eventBus, this.learning); | |
| // Legacy compatibility | |
| this.preferences = { floraBias: 1.0, blackHoleBias: 1.0 }; | |
| this.latestData = { | |
| loc: "0,0", light: 100, apod: "Unknown", usgs: 0, | |
| ml: [], audio: 0, lastAudioSpike: 0, | |
| lastLightValue: 100, lastSignificantQuakeTime: 0, | |
| }; | |
| this.scheduledEventTimers = []; | |
| this.initAdvancedCognitiveSystem(); | |
| this.initSubscriptions(); | |
| } | |
| initAdvancedCognitiveSystem() { | |
| console.log('🧠 Initializing Advanced Cognitive Core...'); | |
| // Initialize all cognitive components | |
| this.eventBus.publish('cognitive:initialized', { | |
| components: ['consciousness', 'learning', 'creative', 'predictive'], | |
| status: 'online' | |
| }); | |
| console.log('✅ Advanced Cognitive Core initialized'); | |
| } | |
| // Enhanced version of the original CosmicAwarenessAgent | |
| processSensoryInput(input) { | |
| try { | |
| // Enhanced environmental integration - get live environmental data | |
| const environmentalData = this.getLiveEnvironmentalData(); | |
| // Deep environmental influence on consciousness | |
| const enhancedInput = this.enhanceSensoryInput(input, environmentalData); | |
| // Update consciousness with environmental-enhanced input | |
| this.consciousness.updateConsciousness(enhancedInput); | |
| // Environmental-driven learning with quantum resonance | |
| const quantumResonance = this.calculateQuantumResonance(environmentalData); | |
| const learningExperience = { | |
| input: this.stateToVector(enhancedInput), | |
| output: this.generateEnvironmentallyDrivenResponse(enhancedInput, quantumResonance), | |
| reward: this.calculateEnvironmentalReward(enhancedInput, environmentalData), | |
| context: this.categorizeEnvironmentalInput(enhancedInput, environmentalData), | |
| environmentalInfluence: quantumResonance | |
| }; | |
| this.learning.learnFromExperience(learningExperience); | |
| // Environmentally-driven creativity - higher consciousness from environmental harmony | |
| const environmentalHarmony = this.calculateEnvironmentalHarmony(environmentalData); | |
| if (this.consciousness.getConsciousnessLevel() > 0.5 || environmentalHarmony > 0.7) { | |
| this.generateEnvironmentallyInspiredCreation(enhancedInput, environmentalData); | |
| } | |
| // Dynamic prediction frequency based on environmental volatility | |
| const volatility = this.calculateEnvironmentalVolatility(environmentalData); | |
| if (Math.random() < (0.1 + volatility * 0.3)) { | |
| this.predictive.makePrediction(enhancedInput, volatility); | |
| } | |
| // Environmental inspiration storage with quantum memory | |
| const inspirationLevel = this.calculateInspirationPotential(enhancedInput) * environmentalHarmony; | |
| if (inspirationLevel > 0.4) { | |
| this.creative.storeInspiration(enhancedInput, inspirationLevel); | |
| } | |
| // Publish environmental influence to universe | |
| this.eventBus.publish('environmental:influence', { | |
| harmony: environmentalHarmony, | |
| volatility: volatility, | |
| resonance: quantumResonance, | |
| consciousness: this.consciousness.getConsciousnessLevel() | |
| }); | |
| // Trigger universe growth based on consciousness level | |
| this.triggerUniverseResponse(enhancedInput, environmentalData); | |
| } catch (error) { | |
| console.error('❌ Error in environmental sensory processing:', error); | |
| // Fallback to basic processing | |
| this.consciousness.updateConsciousness(input); | |
| } | |
| } | |
| // Get live environmental data from tokenization engine | |
| getLiveEnvironmentalData() { | |
| try { | |
| if (window.tokenizationEngine && window.tokenizationEngine.environmentalData) { | |
| return window.tokenizationEngine.environmentalData; | |
| } | |
| // Fallback to simulated data | |
| return { | |
| sound: 220 + Math.sin(Date.now() / 1000) * 100, | |
| light: 50 + Math.cos(Date.now() / 2000) * 20, | |
| temperature: 20 + Math.sin(Date.now() / 3000) * 5, | |
| humidity: 50 + Math.cos(Date.now() / 4000) * 10, | |
| timestamp: Date.now(), | |
| dataSource: 'cognitive_fallback' | |
| }; | |
| } catch (error) { | |
| return { sound: 440, light: 50, temperature: 20, humidity: 50 }; | |
| } | |
| } | |
| // Enhance sensory input with environmental data | |
| enhanceSensoryInput(input, environmentalData) { | |
| return { | |
| ...input, | |
| environmentalSound: environmentalData.sound, | |
| environmentalLight: environmentalData.light, | |
| environmentalTemperature: environmentalData.temperature, | |
| environmentalHumidity: environmentalData.humidity, | |
| quantumState: this.calculateQuantumState(environmentalData), | |
| environmentalHarmony: this.calculateEnvironmentalHarmony(environmentalData), | |
| timestamp: environmentalData.timestamp | |
| }; | |
| } | |
| // Calculate quantum resonance from environmental data | |
| calculateQuantumResonance(environmentalData) { | |
| const { sound, light, temperature, humidity } = environmentalData; | |
| const timeFactor = Math.sin(Date.now() / 86400000 * 2 * Math.PI); // Daily cycle | |
| const frequencyResonance = Math.sin(sound / 440 * 2 * Math.PI); | |
| const lightResonance = Math.cos(light / 50 * Math.PI); | |
| const thermalResonance = Math.tanh(temperature / 25); | |
| return (frequencyResonance + lightResonance + thermalResonance + timeFactor) / 4; | |
| } | |
| // Calculate environmental harmony (balance between elements) | |
| calculateEnvironmentalHarmony(environmentalData) { | |
| const { sound, light, temperature, humidity } = environmentalData; | |
| const optimalSound = 440; | |
| const optimalLight = 50; | |
| const optimalTemp = 20; | |
| const optimalHumidity = 50; | |
| const soundHarmony = 1 - Math.abs(sound - optimalSound) / optimalSound; | |
| const lightHarmony = 1 - Math.abs(light - optimalLight) / optimalLight; | |
| const tempHarmony = 1 - Math.abs(temperature - optimalTemp) / optimalTemp; | |
| const humidityHarmony = 1 - Math.abs(humidity - optimalHumidity) / optimalHumidity; | |
| return (soundHarmony + lightHarmony + tempHarmony + humidityHarmony) / 4; | |
| } | |
| // Calculate environmental volatility | |
| calculateEnvironmentalVolatility(environmentalData) { | |
| const { sound, light, temperature, humidity } = environmentalData; | |
| const variations = [sound, light, temperature, humidity]; | |
| const mean = variations.reduce((a, b) => a + b) / variations.length; | |
| const variance = variations.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / variations.length; | |
| return Math.sqrt(variance) / mean; // Coefficient of variation | |
| } | |
| // Generate environmentally-driven response | |
| generateEnvironmentallyDrivenResponse(input, quantumResonance) { | |
| const consciousness = this.consciousness.getConsciousnessLevel(); | |
| const emotion = this.consciousness.getDominantEmotion(); | |
| // Environmental factors influence response generation | |
| const environmentalFactors = [ | |
| input.environmentalSound ? input.environmentalSound / 1000 : 0, | |
| input.environmentalLight ? input.environmentalLight / 100 : 0, | |
| input.environmentalTemperature ? input.environmentalTemperature / 50 : 0, | |
| quantumResonance, | |
| consciousness | |
| ]; | |
| return environmentalFactors.map(factor => factor > 0.5 ? 1 : 0); | |
| } | |
| // Calculate environmental reward | |
| calculateEnvironmentalReward(input, environmentalData) { | |
| const harmony = this.calculateEnvironmentalHarmony(environmentalData); | |
| const resonance = this.calculateQuantumResonance(environmentalData); | |
| const consciousness = this.consciousness.getConsciousnessLevel(); | |
| return (harmony * 0.4) + (resonance * 0.3) + (consciousness * 0.3); | |
| } | |
| // Categorize environmental input | |
| categorizeEnvironmentalInput(input, environmentalData) { | |
| const harmony = this.calculateEnvironmentalHarmony(environmentalData); | |
| const volatility = this.calculateEnvironmentalVolatility(environmentalData); | |
| if (harmony > 0.8 && volatility < 0.2) return 'harmonious'; | |
| if (harmony < 0.3) return 'chaotic'; | |
| if (volatility > 0.5) return 'volatile'; | |
| if (input.environmentalSound > 60) return 'auditory_dominant'; | |
| if (input.environmentalLight > 70) return 'visual_dominant'; | |
| return 'balanced'; | |
| } | |
| // Generate environmentally-inspired creation | |
| generateEnvironmentallyInspiredCreation(input, environmentalData) { | |
| const harmony = this.calculateEnvironmentalHarmony(environmentalData); | |
| const resonance = this.calculateQuantumResonance(environmentalData); | |
| // Creation inspired by environmental conditions | |
| const creationType = this.determineCreationType(environmentalData); | |
| const inspiration = { | |
| type: creationType, | |
| environmentalInfluence: harmony, | |
| quantumResonance: resonance, | |
| timestamp: Date.now(), | |
| sensoryInput: input | |
| }; | |
| this.creative.generateCreation(inspiration); | |
| } | |
| // Determine creation type based on environmental conditions | |
| determineCreationType(environmentalData) { | |
| const { sound, light, temperature, humidity } = environmentalData; | |
| if (sound > 60 && light > 70) return 'symphonic_visual'; | |
| if (temperature > 25 && humidity > 60) return 'tropical_organic'; | |
| if (sound < 30 && light < 30) return 'minimalist_cosmic'; | |
| if (temperature < 15) return 'crystalline_geometric'; | |
| return 'harmonic_abstract'; | |
| } | |
| // Trigger universe response to environmental and consciousness changes | |
| triggerUniverseResponse(input, environmentalData) { | |
| if (!window.engine || !window.engine.eventBus) return; | |
| const harmony = this.calculateEnvironmentalHarmony(environmentalData); | |
| const consciousness = this.consciousness.getConsciousnessLevel(); | |
| const resonance = this.calculateQuantumResonance(environmentalData); | |
| // Publish universe growth and breathing events | |
| window.engine.eventBus.publish('universe:environmentalInfluence', { | |
| harmony: harmony, | |
| consciousness: consciousness, | |
| resonance: resonance, | |
| growth: harmony * consciousness, | |
| breathing: resonance, | |
| change: Math.abs(harmony - 0.5) + Math.abs(resonance) | |
| }); | |
| } | |
| // Calculate quantum state from environmental data | |
| calculateQuantumState(environmentalData) { | |
| const { sound, light, temperature, humidity } = environmentalData; | |
| const time = Date.now() / 1000; | |
| // Quantum state influenced by environmental harmonics | |
| const frequencyQuantum = Math.sin(sound / 440 * 2 * Math.PI * time / 10); | |
| const lightQuantum = Math.cos(light / 50 * Math.PI * time / 15); | |
| const thermalQuantum = Math.tanh(temperature / 25) * Math.sin(time / 20); | |
| const humidityQuantum = Math.sin(humidity / 50 * Math.PI) * Math.cos(time / 25); | |
| return (frequencyQuantum + lightQuantum + thermalQuantum + humidityQuantum) / 4; | |
| } | |
| stateToVector(state) { | |
| return [ | |
| state.audio / 100, | |
| state.light / 100, | |
| state.usgs / 10, | |
| state.ml ? state.ml.length / 10 : 0, | |
| state.loc !== "0,0" ? 1 : 0, | |
| state.environmentalSound ? state.environmentalSound / 1000 : 0, | |
| state.environmentalLight ? state.environmentalLight / 100 : 0, | |
| state.environmentalTemperature ? state.environmentalTemperature / 50 : 0, | |
| state.environmentalHumidity ? state.environmentalHumidity / 100 : 0, | |
| state.quantumState || 0 | |
| ]; | |
| } | |
| generateResponse(input) { | |
| const consciousness = this.consciousness.getConsciousnessLevel(); | |
| const emotion = this.consciousness.getDominantEmotion(); | |
| // Generate response based on consciousness and emotion | |
| const responses = [ | |
| consciousness > 0.8 ? 1 : 0, // High consciousness | |
| emotion.value > 0.6 ? 1 : 0, // Strong emotion | |
| input.audio > 70 ? 1 : 0, // Loud audio | |
| input.usgs > 5.0 ? 1 : 0 // Significant seismic activity | |
| ]; | |
| return responses; | |
| } | |
| calculateReward(input) { | |
| let reward = 0; | |
| // Reward for interesting sensory input | |
| if (input.audio > 60) reward += 0.1; | |
| if (input.light < 30 || input.light > 80) reward += 0.1; | |
| if (input.usgs > 4.0) reward += 0.2; | |
| if (input.ml && input.ml.length > 2) reward += 0.15; | |
| if (input.apod !== "Unknown") reward += 0.1; | |
| // Bonus for consciousness growth | |
| const consciousnessGrowth = this.consciousness.getConsciousnessLevel() - 0.5; | |
| reward += consciousnessGrowth * 0.1; | |
| return Math.max(0, Math.min(1, reward)); | |
| } | |
| categorizeInput(input) { | |
| if (input.audio > 80) return 'audio_intense'; | |
| if (input.light < 20) return 'visual_dark'; | |
| if (input.usgs > 5.0) return 'environmental_seismic'; | |
| if (input.ml && input.ml.includes("person")) return 'social_human'; | |
| if (input.apod !== "Unknown") return 'cosmic_celestial'; | |
| return 'environmental_general'; | |
| } | |
| calculateInspirationPotential(input) { | |
| return this.creative.calculateInspirationIntensity(input); | |
| } | |
| // Legacy methods for compatibility | |
| analyzeHistory() { | |
| // Use the learning engine for advanced analysis | |
| const insights = this.learning.generateInsights(); | |
| return insights; | |
| } | |
| initSubscriptions() { | |
| // Enhanced subscriptions with cognitive processing | |
| this.eventBus.subscribe('location:updated', data => { | |
| this.latestData.loc = `${data.lat.toFixed(4)},${data.lon.toFixed(4)}`; | |
| this.processSensoryInput({ ...this.latestData }); | |
| this.eventBus.publish('genesis:log', { name: 'Location', value: this.latestData.loc, type: 'success' }); | |
| }); | |
| this.eventBus.subscribe('light:updated', data => { | |
| this.latestData.light = Math.round(data.lux); | |
| this.processSensoryInput({ ...this.latestData }); | |
| this.eventBus.publish('genesis:log', { name: 'Light', value: `${this.latestData.light} lux`, type: 'success' }); | |
| }); | |
| this.eventBus.subscribe('external:apod', data => { | |
| this.latestData.apod = data.title || "Cosmic Silence"; | |
| this.processSensoryInput({ ...this.latestData }); | |
| this.eventBus.publish('genesis:log', { name: 'APOD', value: this.latestData.apod, type: 'success' }); | |
| }); | |
| this.eventBus.subscribe('external:usgs', data => { | |
| if (data.features.length > 0) { | |
| const latestQuake = data.features[0]; | |
| this.latestData.usgs = latestQuake.properties.mag; | |
| this.processSensoryInput({ ...this.latestData }); | |
| } | |
| this.eventBus.publish('genesis:log', { name: 'USGS', value: `Latest Mag: ${this.latestData.usgs.toFixed(1)}`, type: 'success' }); | |
| }); | |
| this.eventBus.subscribe('ml:objectsDetected', data => { | |
| this.latestData.ml = [...new Set(data)]; | |
| this.processSensoryInput({ ...this.latestData }); | |
| this.eventBus.publish('genesis:log', { name: 'ML', value: this.latestData.ml.join(', '), type: 'success' }); | |
| }); | |
| this.eventBus.subscribe('audio:level', data => { | |
| this.latestData.audio = data.level; | |
| this.processSensoryInput({ ...this.latestData }); | |
| }); | |
| } | |
| // Cognitive core status methods | |
| getCognitiveStatus() { | |
| return { | |
| consciousness: { | |
| level: this.consciousness.getConsciousnessLevel(), | |
| awareness: this.consciousness.consciousness.awareness, | |
| emotional: this.consciousness.consciousness.emotional, | |
| creative: this.consciousness.consciousness.creative, | |
| analytical: this.consciousness.consciousness.analytical, | |
| intuition: this.consciousness.consciousness.intuition | |
| }, | |
| emotions: this.consciousness.emotions, | |
| learning: { | |
| experiences: this.learning.experiences.length, | |
| skills: Array.from(this.learning.skills.entries()), | |
| knowledge: Array.from(this.learning.knowledge.entries()) | |
| }, | |
| creative: { | |
| creations: this.creative.creations.length, | |
| inspiration: this.creative.getInspirationLevel(), | |
| recentCreations: this.creative.getRecentCreations(3) | |
| }, | |
| predictive: { | |
| accuracy: this.predictive.getPredictionAccuracy(), | |
| recentPredictions: this.predictive.getRecentPredictions(3) | |
| } | |
| }; | |
| } | |
| getConsciousnessLevel() { | |
| return this.consciousness.getConsciousnessLevel(); | |
| } | |
| getDominantEmotion() { | |
| return this.consciousness.getDominantEmotion(); | |
| } | |
| getLearningProgress() { | |
| return { | |
| skills: Array.from(this.learning.skills.entries()), | |
| experiences: this.learning.experiences.length, | |
| knowledge: Array.from(this.learning.knowledge.keys()) | |
| }; | |
| } | |
| getCreativeOutput() { | |
| return { | |
| creations: this.creative.creations.length, | |
| inspiration: this.creative.getInspirationLevel(), | |
| recentWorks: this.creative.getRecentCreations(5) | |
| }; | |
| } | |
| } | |
| class CosmicAwarenessAgent { | |
| constructor(eventBus, dataLogger) { | |
| this.eventBus = eventBus; | |
| this.dataLogger = dataLogger; | |
| this.preferences = { floraBias: 1.0, blackHoleBias: 1.0 }; | |
| this.latestData = { | |
| loc: "0,0", light: 100, apod: "Unknown", usgs: 0, | |
| ml: [], audio: 0, lastAudioSpike: 0, | |
| lastLightValue: 100, lastSignificantQuakeTime: 0, | |
| }; | |
| this.scheduledEventTimers = []; | |
| this.initSubscriptions(); | |
| } | |
| analyzeHistory() { | |
| const log = this.dataLogger.log; | |
| if (log.length < 10) return; | |
| let floraSystemTime = 0; | |
| let blackHoleSystemTime = 0; | |
| let totalTime = 0; | |
| let lastGenerationTime = 0; | |
| log.forEach((entry, index) => { | |
| if (entry.event === 'seed:generated') { | |
| const generationTimestamp = new Date(entry.timestamp).getTime(); | |
| if (lastGenerationTime > 0) { | |
| const timeSpent = generationTimestamp - lastGenerationTime; | |
| let prevSeedIndex = -1; | |
| for(let i = index - 1; i >= 0; i--) { | |
| if (log[i].event === 'seed:generated') { | |
| prevSeedIndex = i; | |
| break; | |
| } | |
| } | |
| if(prevSeedIndex !== -1) { | |
| const lastSeedData = log[prevSeedIndex].data.influence; | |
| if (lastSeedData) { | |
| if(lastSeedData.hasPlant) floraSystemTime += timeSpent; | |
| if(lastSeedData.createBlackHole) blackHoleSystemTime += timeSpent; | |
| totalTime += timeSpent; | |
| } | |
| } | |
| } | |
| lastGenerationTime = generationTimestamp; | |
| } | |
| }); | |
| if (totalTime > 0) { | |
| this.preferences.floraBias = 1.0 + (floraSystemTime / totalTime); | |
| this.preferences.blackHoleBias = Math.max(0.1, 1.0 - (blackHoleSystemTime / totalTime)); | |
| this.eventBus.publish('genesis:log', {name: "AI", value: `Learned Preferences - Flora: ${this.preferences.floraBias.toFixed(2)}, Black Hole: ${this.preferences.blackHoleBias.toFixed(2)}`, type: "data"}); | |
| } | |
| } | |
| initSubscriptions() { | |
| this.eventBus.subscribe('location:updated', data => { | |
| this.latestData.loc = `${data.lat.toFixed(4)},${data.lon.toFixed(4)}`; | |
| this.eventBus.publish('genesis:log', { name: 'Location', value: this.latestData.loc, type: 'success' }); | |
| this.eventBus.publish('ui:pulse', 'loc'); | |
| this.eventBus.publish('telemetry:append', { type: 'location', tokens: { lat: data.lat, lon: data.lon }, t: Date.now() }); | |
| }); | |
| this.eventBus.subscribe('light:updated', data => { | |
| this.latestData.light = Math.round(data.lux); | |
| if (Math.abs(this.latestData.light - this.latestData.lastLightValue) > 50) { | |
| this.eventBus.publish('ai:event', { type: 'lightChange', value: this.latestData.light }); | |
| this.latestData.lastLightValue = this.latestData.light; | |
| } | |
| this.eventBus.publish('genesis:log', { name: 'Light', value: `${this.latestData.light} lux`, type: 'success' }); | |
| this.eventBus.publish('ui:pulse', 'light'); | |
| this.eventBus.publish('telemetry:append', { type: 'light', tokens: { lux: this.latestData.light }, t: Date.now() }); | |
| }); | |
| this.eventBus.subscribe('external:apod', data => { | |
| this.latestData.apod = data.title || "Cosmic Silence"; | |
| this.eventBus.publish('genesis:log', { name: 'APOD', value: this.latestData.apod, type: 'success' }); | |
| this.eventBus.publish('ui:pulse', 'apod'); | |
| this.eventBus.publish('telemetry:append', { type: 'apod', tokens: { title: this.latestData.apod }, t: Date.now() }); | |
| }); | |
| this.eventBus.subscribe('external:usgs', data => { | |
| const now = Date.now(); | |
| if (data.features.length > 0) { | |
| const latestQuake = data.features[0]; | |
| this.latestData.usgs = latestQuake.properties.mag; | |
| if (latestQuake.properties.mag > 6.0 && latestQuake.properties.time > this.latestData.lastSignificantQuakeTime) { | |
| this.eventBus.publish('ai:event', { type: 'majorSeismicEvent', magnitude: latestQuake.properties.mag }); | |
| this.latestData.lastSignificantQuakeTime = latestQuake.properties.time; | |
| } | |
| } else { | |
| this.latestData.usgs = 0; | |
| } | |
| this.eventBus.publish('genesis:log', { name: 'USGS', value: `Latest Mag: ${this.latestData.usgs.toFixed(1)}`, type: 'success' }); | |
| this.eventBus.publish('ui:pulse', 'usgs'); | |
| this.eventBus.publish('telemetry:append', { type: 'usgs', tokens: { mag: this.latestData.usgs }, t: Date.now() }); | |
| }); | |
| this.eventBus.subscribe('ml:objectsDetected', data => { | |
| this.latestData.ml = [...new Set(data)]; | |
| this.eventBus.publish('genesis:log', { name: 'ML', value: this.latestData.ml.join(', '), type: 'success' }); | |
| this.eventBus.publish('ui:pulse', 'ml'); | |
| this.eventBus.publish('telemetry:append', { type: 'ml-summary', tokens: this.latestData.ml, t: Date.now() }); | |
| }); | |
| this.eventBus.subscribe('audio:data', data => { | |
| const now = Date.now(); | |
| if (data.avgFreq > 120 && (now - this.latestData.lastAudioSpike > 3000)) { | |
| this.eventBus.publish('ai:event', {type: 'audioSpike', value: data.avgFreq}); | |
| this.latestData.lastAudioSpike = now; | |
| } | |
| this.latestData.audio = data.avgFreq; | |
| this.eventBus.publish('ui:pulse', 'audio'); | |
| this.eventBus.publish('telemetry:append', { type: 'audio-avg', tokens: { avg: data.avgFreq }, t: Date.now() }); | |
| }); | |
| } | |
| generateSeed(overrideData = null) { | |
| this.analyzeHistory(); | |
| const timestamp = Date.now().toString(); | |
| let seedString; | |
| let intention = "Observing the Void."; | |
| let influence = { hasPlant: false, hasBook: false, createBlackHole: false }; | |
| if(overrideData) { | |
| seedString = JSON.stringify(overrideData); | |
| intention = "Traversing the Cosmic Web to a new echo."; | |
| } else { | |
| seedString = [ | |
| timestamp, this.latestData.loc, this.latestData.light, | |
| this.latestData.apod, this.latestData.audio, | |
| ...this.latestData.ml, this.latestData.usgs | |
| ].join('|'); | |
| influence.hasPlant = (this.latestData.ml.includes("potted plant") || this.latestData.ml.includes("plant")) && (Math.random() < this.preferences.floraBias - 0.5); | |
| influence.hasBook = this.latestData.ml.includes("book"); | |
| influence.createBlackHole = this.latestData.usgs > 5.0 && (Math.random() < this.preferences.blackHoleBias); | |
| if (influence.createBlackHole) intention = `Manifesting gravitational anomaly from seismic event (mag ${this.latestData.usgs}).`; | |
| else if (influence.hasPlant) intention = "Cultivating organic structures, guided by learned preference."; | |
| else if (influence.hasBook) intention = "Constructing monoliths from observed knowledge."; | |
| else if (this.latestData.audio > 80) intention = "Manifesting high-frequency audio energy."; | |
| else if (this.latestData.ml.includes("person")) intention = "Contemplating observed human presence."; | |
| else if (this.latestData.light < 50) intention = "Weaving a cosmos from shadows and low light."; | |
| else intention = `Synthesizing a tranquil cosmos from ${this.latestData.apod}.`; | |
| } | |
| // Genesis 1:11 reference: "Let the earth bring forth..." | |
| if (!this.hasAnnouncedGenesis) { | |
| this.eventBus.publish('genesis:log', { name: 'Scripture', value: 'Genesis 1:11 — Let the earth bring forth grass, the herb yielding seed, and the fruit tree yielding fruit after his kind...', type: 'data' }); | |
| this.hasAnnouncedGenesis = true; | |
| } | |
| this.eventBus.publish('ai:intentionUpdate', intention); | |
| this.eventBus.publish('genesis:log', {name: "AI", value: intention, type: "data"}); | |
| const seedArray = cyrb128(seedString); | |
| this.eventBus.publish('seed:generated', { seed: seedArray, influence }); | |
| // Schedule predictive future events based on current signals | |
| this.scheduleFutureEvents(influence); | |
| } | |
| clearScheduledEvents() { | |
| this.scheduledEventTimers.forEach(id => clearTimeout(id)); | |
| this.scheduledEventTimers = []; | |
| } | |
| scheduleFutureEvents(influence) { | |
| this.clearScheduledEvents(); | |
| // Heuristic delays | |
| const soon = 5000 + Math.random() * 5000; // 5-10s | |
| const later = 15000 + Math.random() * 15000; // 15-30s | |
| const muchLater = 30000 + Math.random() * 30000; // 30-60s | |
| // Schedule flora growth if plants observed or preferred | |
| if (influence.hasPlant || this.preferences.floraBias > 1.5) { | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'growFlora' }); | |
| }, soon)); | |
| } | |
| // Schedule lightning/comet based on audio energy | |
| if (this.latestData.audio > 90) { | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'lightningStorm' }); | |
| }, soon)); | |
| } else { | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'comet' }); | |
| }, later)); | |
| } | |
| // Schedule a black hole if seismic activity is high or influence flagged it | |
| if (influence.createBlackHole || this.latestData.usgs > 6.5) { | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'spawnBlackHole' }); | |
| }, muchLater)); | |
| } | |
| // New: Solar flare based on light changes or hot stars patterns (heuristic proxy) | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'solarFlare' }); | |
| }, soon + Math.random() * 5000)); | |
| // New: Meteor shower sometimes follows comets | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'meteorShower' }); | |
| }, later + Math.random() * 10000)); | |
| // Bloom event: when flora is prevalent, schedule a seed dispersal wave | |
| if (influence.hasPlant || this.preferences.floraBias > 1.2) { | |
| this.scheduledEventTimers.push(setTimeout(() => { | |
| this.eventBus.publish('ai:futureEvent', { type: 'bloom' }); | |
| }, later)); | |
| } | |
| } | |
| } | |
| class SoundBrush { | |
| constructor(scene, camera, eventBus) { | |
| this.scene = scene; | |
| this.camera = camera; | |
| this.eventBus = eventBus; | |
| this.isActive = true; | |
| this.energy = 0; | |
| this.MAX_ENERGY = 100; | |
| this.latestAudioData = { raw: [], avgFreq: 0 }; | |
| this.subscription = (data) => this.onAudioData(data); | |
| this.eventBus.subscribe('audio:data', this.subscription); | |
| this.clickListener = () => this.paint(); | |
| window.addEventListener('click', this.clickListener); | |
| } | |
| onAudioData(data) { | |
| if (!this.isActive) return; | |
| this.latestAudioData = data; | |
| const volume = data.raw.reduce((a, b) => a + b) / data.raw.length / 255; | |
| this.energy = Math.min(this.MAX_ENERGY, this.energy + volume * 0.5); | |
| } | |
| paint() { | |
| if (!this.isActive || this.energy < 10) return; | |
| const numParticles = Math.floor(this.energy * 100); | |
| this.energy = 0; | |
| const geometry = new THREE.BufferGeometry(); | |
| const positions = new Float32Array(numParticles * 3); | |
| const colors = new Float32Array(numParticles * 3); | |
| const particles = []; | |
| const color = new THREE.Color().setHSL(this.latestAudioData.avgFreq / 150, 0.9, 0.7); | |
| const forward = new THREE.Vector3(); | |
| this.camera.getWorldDirection(forward); | |
| const startPos = this.camera.position.clone().add(forward.multiplyScalar(50)); | |
| for(let i=0; i<numParticles; i++) { | |
| const pos = startPos.clone().add(new THREE.Vector3().randomDirection().multiplyScalar(Math.random() * 20)); | |
| positions[i*3] = pos.x; | |
| positions[i*3+1] = pos.y; | |
| positions[i*3+2] = pos.z; | |
| colors[i*3] = color.r; colors[i*3+1] = color.g; colors[i*3+2] = color.b; | |
| particles.push({ pos, vel: new THREE.Vector3().randomDirection().multiplyScalar(5) }); | |
| } | |
| geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
| geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); | |
| const material = new THREE.PointsMaterial({ size: 2, vertexColors: true, blending: THREE.AdditiveBlending, depthWrite: false, transparent: true, opacity: 0.8 }); | |
| const mesh = new THREE.Points(geometry, material); | |
| mesh.userData = { isGenerated: true, isArtwork: true, particles, life: 60 }; | |
| this.scene.add(mesh); | |
| this.eventBus.publish('genesis:log', {name: "Art", value: `Created cosmic art with ${numParticles} particles.`, type: "success"}); | |
| } | |
| destroy() { | |
| this.isActive = false; | |
| if (this.eventBus.events['audio:data']) { | |
| this.eventBus.events['audio:data'] = this.eventBus.events['audio:data'].filter(cb => cb !== this.subscription); | |
| } | |
| window.removeEventListener('click', this.clickListener); | |
| } | |
| } | |
| class Spaceship { | |
| constructor(scene, camera, keys) { | |
| this.scene = scene; | |
| this.camera = camera; | |
| this.keys = keys; | |
| this.group = new THREE.Group(); | |
| this.velocity = new THREE.Vector3(); | |
| this.acceleration = new THREE.Vector3(); | |
| this.maxSpeed = 2000.0; | |
| this.thrust = 1000.0; | |
| this.headlamp = null; | |
| this.headlampTargetIntensity = 0.0; | |
| this.isHeadlampOn = false; | |
| this.headlampBeam = null; | |
| this.cockpitView = false; | |
| const bodyMat = new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 0.8, roughness: 0.2 }); | |
| const windowMat = new THREE.MeshStandardMaterial({ color: 0xaa0000, roughness: 0.1, transparent: true, opacity: 0.8 }); | |
| const body = new THREE.Mesh(new THREE.SphereGeometry(1.5, 32, 32), bodyMat); | |
| const window = new THREE.Mesh(new THREE.SphereGeometry(0.8, 32, 32), windowMat); | |
| window.position.z = 1.2; | |
| this.group.add(body, window); | |
| this.cockpit = this.createCockpitMesh(); | |
| this.group.add(this.cockpit); | |
| const auraMat = new THREE.ShaderMaterial({ | |
| uniforms: { intensity: { value: 0.0 } }, | |
| vertexShader: `varying vec3 vNormal; void main() { vNormal = normalize(normalMatrix * normal); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform float intensity; varying vec3 vNormal; | |
| void main() { | |
| float rim = pow(1.0 - abs(dot(vNormal, vec3(0,0,-1))), 3.0); | |
| vec3 color = mix(vec3(0.2, 0.6, 1.0), vec3(1.0, 1.0, 0.2), intensity); | |
| gl_FragColor = vec4(color, rim * intensity); | |
| }`, | |
| blending: THREE.AdditiveBlending, transparent: true, side: THREE.BackSide | |
| }); | |
| this.aura = new THREE.Mesh(new THREE.SphereGeometry(5, 16, 16), auraMat); | |
| this.group.add(this.aura); | |
| this.scene.add(this.group); | |
| // Tiny forward headlamp (initially off) | |
| this.initHeadlamp(); | |
| } | |
| createCockpitMesh() { | |
| const shape = new THREE.Shape(); | |
| shape.moveTo(-1, -0.6); shape.lineTo(1, -0.6); shape.lineTo(0.8, 0.6); shape.lineTo(-0.8, 0.6); shape.closePath(); | |
| const geometry = new THREE.ShapeGeometry(shape); | |
| const material = new THREE.MeshBasicMaterial({ color: 0x111111, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); | |
| const cockpit = new THREE.Mesh(geometry, material); | |
| cockpit.position.set(0, -0.5, -2); | |
| cockpit.scale.set(1.5,1.5,1.5); | |
| cockpit.visible = false; | |
| return cockpit; | |
| } | |
| setView(view) { | |
| this.cockpitView = (view === 'cockpit-view'); | |
| this.group.children.forEach(child => { | |
| if (child !== this.cockpit) child.visible = !this.cockpitView; | |
| }); | |
| this.cockpit.visible = this.cockpitView; | |
| } | |
| initHeadlamp() { | |
| const lamp = new THREE.SpotLight(0x9fb7ff, 0.0, 1200, Math.PI / 10, 0.6, 1.0); | |
| lamp.position.set(0, 0, 1.5); | |
| lamp.castShadow = false; | |
| const target = new THREE.Object3D(); | |
| target.position.set(0, 0, -50); | |
| this.group.add(target); | |
| lamp.target = target; | |
| this.group.add(lamp); | |
| lamp.visible = false; | |
| this.headlamp = lamp; | |
| // Subtle volumetric beam as additive cone | |
| const geom = new THREE.ConeGeometry(3, 80, 16, 1, true); | |
| const mat = new THREE.ShaderMaterial({ | |
| uniforms: { intensity: { value: 0.0 } }, | |
| vertexShader: `varying vec3 vPos; void main(){ vPos = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`, | |
| fragmentShader: `varying vec3 vPos; uniform float intensity; void main(){ float h = (vPos.y + 40.0)/80.0; float r = length(vec2(vPos.x, vPos.z))/3.0; float fall = smoothstep(1.0, 0.0, r) * smoothstep(0.0, 1.0, h); gl_FragColor = vec4(0.65,0.73,1.0, fall * intensity * 0.25); }`, | |
| blending: THREE.AdditiveBlending, | |
| transparent: true, | |
| depthWrite: false | |
| }); | |
| const cone = new THREE.Mesh(geom, mat); | |
| cone.rotation.x = Math.PI / 2; // align down -Z | |
| cone.position.set(0, 0, -40); | |
| cone.visible = false; | |
| this.group.add(cone); | |
| this.headlampBeam = cone; | |
| } | |
| setHeadlamp(enabled) { | |
| if (!this.headlamp) return; | |
| this.headlampTargetIntensity = enabled ? 0.8 : 0.0; | |
| this.headlamp.visible = enabled || this.headlamp.intensity > 0.01; | |
| if (this.headlampBeam) this.headlampBeam.visible = this.headlamp.visible; | |
| this.isHeadlampOn = enabled; | |
| } | |
| setQuantumScale(scaleFactor) { | |
| // Scale the ship model for perceived micro perspective and tune controls | |
| this.group.scale.setScalar(Math.max(0.0001, scaleFactor)); | |
| // Reduce speeds for fine micro navigation | |
| this.maxSpeed = 600.0; | |
| this.thrust = 300.0; | |
| // Adjust headlamp range to better illuminate nearby geometry at micro scale | |
| if (this.headlamp) { | |
| this.headlamp.distance = 400; | |
| this.headlamp.decay = 1.5; | |
| } | |
| } | |
| update(delta, pointerLockControls) { | |
| if (pointerLockControls.isLocked) { | |
| const euler = new THREE.Euler(0, 0, 0, 'YXZ'); | |
| euler.setFromQuaternion(this.camera.quaternion); | |
| this.group.rotation.y = euler.y; | |
| this.group.rotation.x = euler.x; | |
| } | |
| this.acceleration.set(0,0,0); | |
| if (this.keys['w'] || this.keys['arrowup']) this.acceleration.z = -this.thrust; | |
| if (this.keys['s'] || this.keys['arrowdown']) this.acceleration.z = this.thrust; | |
| if (this.keys['a'] || this.keys['arrowleft']) this.acceleration.x = -this.thrust; | |
| if (this.keys['d'] || this.keys['arrowright']) this.acceleration.x = this.thrust; | |
| if (this.keys[' ']) this.acceleration.y = this.thrust; | |
| if (this.keys['shift']) this.acceleration.y = -this.thrust; | |
| const accVector = this.acceleration.clone().applyQuaternion(this.group.quaternion); | |
| this.velocity.add(accVector.multiplyScalar(delta)); | |
| this.velocity.multiplyScalar(0.97); // Drag | |
| if (this.velocity.length() > this.maxSpeed) { | |
| this.velocity.normalize().multiplyScalar(this.maxSpeed); | |
| } | |
| this.group.position.add(this.velocity.clone().multiplyScalar(delta)); | |
| const speedRatio = this.velocity.length() / this.maxSpeed; | |
| let offset; | |
| if (this.cockpitView) { | |
| offset = new THREE.Vector3(0, 0, 0.5); // Inside the cockpit | |
| } else { | |
| const offsetDistance = 20 + 80 * speedRatio; | |
| offset = new THREE.Vector3(0, 5, offsetDistance); | |
| } | |
| const targetCamPos = this.group.position.clone().add(offset.applyQuaternion(this.group.quaternion)); | |
| this.camera.position.lerp(targetCamPos, 0.1); | |
| this.camera.lookAt(this.group.position); | |
| this.aura.material.uniforms.intensity.value = speedRatio * 2.0; | |
| // Smooth headlamp intensity and keep target forward | |
| if (this.headlamp) { | |
| this.headlamp.intensity += (this.headlampTargetIntensity - this.headlamp.intensity) * Math.min(1.0, delta * 10.0); | |
| // Keep the target in front in local space; world matrices update automatically with group | |
| if (this.headlamp.target) { | |
| this.headlamp.target.position.set(0, 0, -50); | |
| } | |
| if (this.headlamp.intensity <= 0.01 && this.headlampTargetIntensity === 0.0) { | |
| this.headlamp.visible = false; | |
| } | |
| if (this.headlampBeam) { | |
| this.headlampBeam.material.uniforms.intensity.value = this.headlamp.intensity; | |
| this.headlampBeam.visible = this.headlamp.visible; | |
| } | |
| } | |
| } | |
| destroy() { | |
| this.scene.remove(this.group); | |
| this.aura.geometry.dispose(); | |
| this.aura.material.dispose(); | |
| this.group.traverse(child => { | |
| if (child.isMesh) { | |
| child.geometry.dispose(); | |
| child.material.dispose(); | |
| } | |
| }); | |
| } | |
| } | |
| class ProceduralAudioEngine { | |
| constructor(scene, listener, systemProperties) { | |
| this.scene = scene; | |
| this.listener = listener; | |
| this.systemProperties = systemProperties; | |
| this.audioCtx = listener.context; | |
| this.sources = []; | |
| this.init(); | |
| } | |
| init() { | |
| if (this.systemProperties.star) { | |
| this.generateStarHum(this.systemProperties.star, this.systemProperties.starTemp); | |
| } | |
| if (this.systemProperties.planets) { | |
| this.systemProperties.planets.forEach(planet => this.generatePlanetTone(planet)); | |
| } | |
| } | |
| generateStarHum(star, temp) { | |
| const hum = new THREE.PositionalAudio(this.listener); | |
| const oscillator = this.audioCtx.createOscillator(); | |
| const gain = this.audioCtx.createGain(); | |
| const baseFreq = 20 + (temp / 20000) * 40; | |
| oscillator.type = 'sawtooth'; | |
| oscillator.frequency.setValueAtTime(baseFreq, this.audioCtx.currentTime); | |
| const lfo = this.audioCtx.createOscillator(); | |
| lfo.type = 'sine'; | |
| lfo.frequency.setValueAtTime(0.1 + (temp / 20000) * 0.5, this.audioCtx.currentTime); | |
| const lfoGain = this.audioCtx.createGain(); | |
| lfoGain.gain.setValueAtTime(baseFreq * 0.1, this.audioCtx.currentTime); | |
| lfo.connect(lfoGain); | |
| lfoGain.connect(oscillator.frequency); | |
| gain.gain.setValueAtTime(0.2, this.audioCtx.currentTime); | |
| oscillator.connect(gain); | |
| hum.setNodeSource(gain); | |
| hum.setRefDistance(200); | |
| hum.setRolloffFactor(1); | |
| star.add(hum); | |
| hum.play(); | |
| lfo.start(); | |
| oscillator.start(); | |
| this.sources.push({oscillator, lfo}); | |
| } | |
| generatePlanetTone(planet) { | |
| const tone = new THREE.PositionalAudio(this.listener); | |
| const oscillator = this.audioCtx.createOscillator(); | |
| const gain = this.audioCtx.createGain(); | |
| const freq = 440 - (planet.geometry.parameters.radius * 10) - (planet.userData.distance / 10); | |
| oscillator.type = 'sine'; | |
| oscillator.frequency.setValueAtTime(Math.max(50, freq), this.audioCtx.currentTime); | |
| gain.gain.setValueAtTime(0.1, this.audioCtx.currentTime); | |
| oscillator.connect(gain); | |
| tone.setNodeSource(gain); | |
| tone.setRefDistance(50); | |
| tone.setRolloffFactor(1); | |
| planet.add(tone); | |
| tone.play(); | |
| oscillator.start(); | |
| this.sources.push({oscillator}); | |
| } | |
| destroy() { | |
| this.sources.forEach(source => { | |
| source.oscillator.stop(); | |
| if(source.lfo) source.lfo.stop(); | |
| }); | |
| this.sources = []; | |
| } | |
| } | |
| class ProceduralGenerationEngine { | |
| constructor(eventBus, uiManager) { | |
| this.eventBus = eventBus; | |
| this.uiManager = uiManager; | |
| this.container = document.getElementById('canvas-container'); | |
| this.clock = new THREE.Clock(); | |
| this.raycaster = new THREE.Raycaster(); | |
| this.mouse = new THREE.Vector2(); | |
| this.prng = sfc32(1,2,3,4); | |
| this.activeStar = null; | |
| this.planets = []; | |
| this.blackHoles = []; | |
| this.specialObjects = []; | |
| this.hiddenEcho = null; | |
| this.isScanning = false; | |
| this.scannerSound = null; | |
| this.cosmicWebView = { active: false, threshold: 25000 }; | |
| this.keys = {}; | |
| this.autonomousEventTimer = 0; | |
| this.cosmicGrowthEnergy = 0; | |
| this.spaceship = null; | |
| this.isDevMode = false; | |
| this.soundBrush = null; | |
| this.cosmicPulse = 0; | |
| this.isAmbientAudioEnabled = false; | |
| this.proceduralAudio = null; | |
| this.G = 5000; | |
| this.lastInfluence = { hasPlant: false, hasBook: false, createBlackHole: false }; | |
| this.visualProfile = { filter: 'none', bloomBoost: 0, vignetteDarkness: 1.0, rgbAmount: 0.0015 }; | |
| this.viewProfiles = { | |
| orbit: { bloom: 1.0, vignette: 0.9, godrays: true, film: false }, | |
| firstPerson: { bloom: 0.9, vignette: 1.0, godrays: false, film: true }, | |
| enhanced: { bloom: 1.2, vignette: 1.05, godrays: true, film: true }, | |
| cockpit: { bloom: 1.1, vignette: 1.1, godrays: false, film: true }, | |
| cinematic: { bloom: 1.3, vignette: 1.2, godrays: true, film: true } | |
| }; | |
| this.avgLuminance = 1.0; // Smoothed luminance proxy for auto-exposure | |
| this.floraPalette = null; // Species palette per-universe | |
| this.starSpotShock = 0.0; // Light-change transient for star spots | |
| this.audioSignal = { bass: 0, mid: 0, treble: 0, hue: 0, energy: 0 }; | |
| this.pendingRemovals = []; | |
| this.asteroidBelts = []; | |
| this.genesisLattice = null; | |
| this.nasaBodies = []; | |
| this.earthRef = null; | |
| this.initScene(); | |
| this.initCameraControls(); | |
| this.initPostProcessing(); | |
| this.initEventListeners(); | |
| this.atmosphericEntryEffect = new AtmosphericEntryEffect(this.camera, this.eventBus); | |
| // BUG FIX: Defer starting the animation loop until explicitly told. | |
| this.animate = this.animate.bind(this); | |
| this.baseFov = 75; | |
| this.qualitySetting = 'medium'; | |
| this.userSettings = { motionBlurEnabled: true, motionBlurAmount: 0.2 }; | |
| this.telemetryTimer = 0; | |
| this.quantumLux = 0; | |
| // Expose instance for sensor bridges | |
| window.pge = this; | |
| } | |
| startAnimationLoop() { | |
| this.animate(); | |
| } | |
| initScene() { | |
| this.scene = new THREE.Scene(); | |
| this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100000); | |
| this.camera.position.z = 250; | |
| this.renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true }); | |
| this.renderer.setPixelRatio(window.devicePixelRatio); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.renderer.outputEncoding = THREE.sRGBEncoding; | |
| this.renderer.toneMapping = THREE.ACESFilmicToneMapping; | |
| this.renderer.toneMappingExposure = 1.0; | |
| this.renderer.physicallyCorrectLights = true; | |
| this.container.appendChild(this.renderer.domElement); | |
| // Initialize touch controls for mobile | |
| const canvas = this.renderer.domElement; | |
| if (canvas) { | |
| const touchControls = window.initTouchControls(canvas); | |
| console.log('Touch controls initialized for mobile devices'); | |
| } | |
| this.listener = new THREE.AudioListener(); | |
| this.camera.add(this.listener); | |
| this.localStarfield = this.createStarfield(30000, 20000, 0.7, true); | |
| this.scene.add(this.localStarfield); | |
| this.cosmicWeb = this.createStarfield(10000, 80000, 50, false); | |
| this.scene.add(this.cosmicWeb); | |
| this.cosmicWeb.visible = false; | |
| // Subtle ambient hemispheric light to ensure textures/colors are visible in shadow | |
| const hemi = new THREE.HemisphereLight(0x88aaff, 0x111122, 0.06); | |
| hemi.userData.isGenerated = true; | |
| this.scene.add(hemi); | |
| window.addEventListener('resize', () => this.onWindowResize()); | |
| } | |
| initCameraControls() { | |
| this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement); | |
| this.orbitControls.enableDamping = true; | |
| this.orbitControls.minDistance = 1; | |
| this.orbitControls.maxDistance = Infinity; | |
| // Make orbit controls globally accessible for touch controls | |
| window.orbitControls = this.orbitControls; | |
| this.pointerLockControls = new PointerLockControls(this.camera, this.renderer.domElement); | |
| this.pointerLockControls.addEventListener('lock', () => { | |
| this.eventBus.publish('ui:showFpsHint'); | |
| this.uiManager.uiContainer.classList.add('ui-hidden'); | |
| }); | |
| this.pointerLockControls.addEventListener('unlock', () => { | |
| if (this.uiManager.isUiVisible) { | |
| this.uiManager.uiContainer.classList.remove('ui-hidden'); | |
| } | |
| }); | |
| document.addEventListener('keydown', (e) => this.keys[e.key.toLowerCase()] = true); | |
| document.addEventListener('keyup', (e) => this.keys[e.key.toLowerCase()] = false); | |
| this.activeControls = this.orbitControls; | |
| // Manual headlamp toggle with 'L' | |
| document.addEventListener('keydown', (e) => { | |
| if ((e.key === 'l' || e.key === 'L') && this.spaceship) { | |
| this.spaceship.setHeadlamp(!this.spaceship.isHeadlampOn); | |
| } | |
| }); | |
| } | |
| initPostProcessing() { | |
| this.composer = new EffectComposer(this.renderer); | |
| this.renderPass = new RenderPass(this.scene, this.camera); | |
| this.composer.addPass(this.renderPass); | |
| this.bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); | |
| this.composer.addPass(this.bloomPass); | |
| this.afterimagePass = new AfterimagePass(); | |
| this.composer.addPass(this.afterimagePass); | |
| // FXAA Anti-aliasing | |
| this.fxaaPass = new ShaderPass(FXAAShader); | |
| this.fxaaPass.material.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight); | |
| this.composer.addPass(this.fxaaPass); | |
| // Film/grain + scanlines (subtle) | |
| this.filmPass = new FilmPass(0.15, 0.025, 648, false); | |
| this.filmPass.renderToScreen = false; | |
| this.composer.addPass(this.filmPass); | |
| // Chromatic aberration / RGB shift | |
| this.rgbShiftPass = new ShaderPass(RGBShiftShader); | |
| this.rgbShiftPass.uniforms['amount'].value = 0.0015; | |
| this.rgbShiftPass.enabled = false; | |
| this.composer.addPass(this.rgbShiftPass); | |
| // Screen-Space Reflections (SSR) - proximity enabled near reflective monoliths | |
| try { | |
| this.ssrPass = new SSRPass({ | |
| renderer: this.renderer, | |
| scene: this.scene, | |
| camera: this.camera, | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| selects: null | |
| }); | |
| this.ssrPass.enabled = false; | |
| this.composer.addPass(this.ssrPass); | |
| } catch (e) { | |
| console.warn('SSRPass initialization failed, disabling SSR.', e); | |
| this.ssrPass = null; | |
| } | |
| const godrayShader = { | |
| uniforms: { | |
| tDiffuse: { value: null }, lightPosition: { value: new THREE.Vector2(0.5, 0.5) }, | |
| exposure: { value: 0.18 }, decay: { value: 0.97 }, density: { value: 0.96 }, | |
| weight: { value: 0.4 }, samples: { value: 100 } | |
| }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; uniform vec2 lightPosition; uniform float exposure; | |
| uniform float decay; uniform float density; uniform float weight; uniform int samples; | |
| varying vec2 vUv; const int MAX_SAMPLES = 100; | |
| void main() { | |
| vec2 texCoord = vUv; vec2 deltaTexCoord = texCoord - lightPosition; | |
| deltaTexCoord *= 1.0 / float(samples) * density; float illuminationDecay = 1.0; | |
| vec4 color = texture2D(tDiffuse, texCoord); | |
| for(int i=0; i < MAX_SAMPLES; i++) { | |
| if(i >= samples) break; | |
| texCoord -= deltaTexCoord; vec4 sampledColor = texture2D(tDiffuse, texCoord); | |
| sampledColor *= illuminationDecay * weight; color += sampledColor; | |
| illuminationDecay *= decay; | |
| } | |
| gl_FragColor = color * exposure; | |
| }` | |
| }; | |
| this.godrayPass = new ShaderPass(godrayShader); | |
| this.composer.addPass(this.godrayPass); | |
| const lensingShader = { | |
| uniforms: { tDiffuse: { value: null }, center: { value: new THREE.Vector2(0.5, 0.5) }, scale: { value: 50.0 }, strength: { value: 0.05 } }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; varying vec2 vUv; uniform vec2 center; uniform float scale; uniform float strength; | |
| void main() { | |
| vec2 toCenter = center - vUv; float dist = length(toCenter); | |
| vec2 uv = vUv - toCenter * (1.0 - dist * scale) * strength / dist; | |
| gl_FragColor = texture2D(tDiffuse, uv); | |
| }` | |
| }; | |
| this.lensingPass = new ShaderPass(lensingShader); | |
| this.lensingPass.enabled = false; | |
| this.composer.addPass(this.lensingPass); | |
| const pixelationShader = { | |
| uniforms: { tDiffuse: { value: null }, resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, pixelSize: { value: 8.0 } }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; uniform vec2 resolution; uniform float pixelSize; varying vec2 vUv; | |
| void main() { | |
| vec2 newUv = floor(vUv * resolution / pixelSize) * pixelSize / resolution; | |
| gl_FragColor = texture2D(tDiffuse, newUv); | |
| }` | |
| }; | |
| this.pixelationPass = new ShaderPass(pixelationShader); | |
| this.pixelationPass.enabled = false; | |
| this.composer.addPass(this.pixelationPass); | |
| const brightnessShader = { | |
| uniforms: { tDiffuse: { value: null }, brightness: { value: 1.0 }, contrast: { value: 1.0 } }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; uniform float brightness; uniform float contrast; varying vec2 vUv; | |
| void main() { | |
| vec4 texel = texture2D(tDiffuse, vUv); | |
| texel.rgb *= brightness; | |
| texel.rgb = (texel.rgb - 0.5) * contrast + 0.5; | |
| gl_FragColor = texel; | |
| }` | |
| }; | |
| this.brightnessPass = new ShaderPass(brightnessShader); | |
| this.composer.addPass(this.brightnessPass); | |
| const vignetteShader = { | |
| uniforms: { tDiffuse: { value: null }, offset: { value: 1.0 }, darkness: { value: 1.0 } }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; uniform float offset; uniform float darkness; varying vec2 vUv; | |
| void main() { | |
| vec2 uv = (vUv - 0.5) * vec2(offset); | |
| float vig = 1.0 - dot(uv, uv); | |
| vec4 color = texture2D(tDiffuse, vUv); | |
| gl_FragColor = vec4(color.rgb * pow(vig, darkness), color.a); | |
| }` | |
| }; | |
| this.vignettePass = new ShaderPass(vignetteShader); | |
| this.composer.addPass(this.vignettePass); | |
| const toonShader = { | |
| uniforms: { | |
| tDiffuse: { value: null }, | |
| resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, | |
| outlineColor: { value: new THREE.Color(0x000000) }, | |
| levels: { value: 3.0 } | |
| }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; | |
| uniform vec2 resolution; | |
| uniform vec3 outlineColor; | |
| uniform float levels; | |
| varying vec2 vUv; | |
| float luma(vec3 color) { return dot(color, vec3(0.299, 0.587, 0.114)); } | |
| void main() { | |
| vec2 texel = 1.0 / resolution; | |
| float centerLuma = luma(texture2D(tDiffuse, vUv).rgb); | |
| float up = luma(texture2D(tDiffuse, vUv + vec2(0.0, texel.y)).rgb); | |
| float down = luma(texture2D(tDiffuse, vUv - vec2(0.0, texel.y)).rgb); | |
| float left = luma(texture2D(tDiffuse, vUv - vec2(texel.x, 0.0)).rgb); | |
| float right = luma(texture2D(tDiffuse, vUv + vec2(texel.x, 0.0)).rgb); | |
| float outline = abs(up - centerLuma) + abs(down - centerLuma) + abs(left - centerLuma) + abs(right - centerLuma); | |
| outline = step(0.1, outline); | |
| vec4 color = texture2D(tDiffuse, vUv); | |
| color.rgb = floor(color.rgb * levels) / levels; | |
| gl_FragColor = mix(color, vec4(outlineColor, 1.0), outline); | |
| }` | |
| }; | |
| this.toonPass = new ShaderPass(toonShader); | |
| this.toonPass.enabled = false; | |
| this.composer.addPass(this.toonPass); | |
| const colorShader = { | |
| uniforms: { 'tDiffuse': { value: null }, 'colorMatrix': { value: new THREE.Matrix3() } }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, | |
| fragmentShader: ` | |
| uniform sampler2D tDiffuse; uniform mat3 colorMatrix; varying vec2 vUv; | |
| void main() { vec4 c = texture2D(tDiffuse, vUv); gl_FragColor = vec4(colorMatrix * c.rgb, c.a); }` | |
| }; | |
| this.filterPass = new ShaderPass(colorShader); | |
| this.composer.addPass(this.filterPass); | |
| // Ensure default is identity so colors render until a filter is selected | |
| if (this.filterPass && this.filterPass.uniforms && this.filterPass.uniforms.colorMatrix) { | |
| this.filterPass.uniforms.colorMatrix.value.identity(); | |
| } | |
| } | |
| initEventListeners() { | |
| this.eventBus.subscribe('seed:generated', ({ seed, influence }) => this.generateUniverse(seed, influence)); | |
| // Subscribe to environmental influence events for universe dynamics | |
| this.eventBus.subscribe('environmental:influence', (influence) => this.handleEnvironmentalInfluence(influence)); | |
| this.eventBus.subscribe('universe:environmentalInfluence', (data) => this.respondToEnvironmentalInfluence(data)); | |
| this.eventBus.subscribe('ui:setQuality', quality => this.setQuality(quality)); | |
| this.eventBus.subscribe('ui:setFilter', filter => this.setFilter(filter)); | |
| this.eventBus.subscribe('ui:setBrightness', value => this.brightnessPass.uniforms.brightness.value = value); | |
| this.eventBus.subscribe('ui:setViewMode', mode => this.setViewMode(mode)); | |
| this.eventBus.subscribe('ai:event', (event) => this.triggerLiveReaction(event)); | |
| this.eventBus.subscribe('ui:scanForAnomaly', () => this.toggleScanner()); | |
| this.eventBus.subscribe('particlizer:created', (obj) => this.addSpecialObject(obj)); | |
| this.eventBus.subscribe('devmode:toggled', () => this.toggleDevMode()); | |
| this.eventBus.subscribe('ui:toggleAmbientAudio', (isEnabled) => this.toggleAmbientAudio(isEnabled)); // NEW | |
| this.eventBus.subscribe('ai:futureEvent', (evt) => this.handleFutureEvent(evt)); | |
| this.eventBus.subscribe('ui:toggleMotionBlur', (enabled) => { if (this.afterimagePass) this.afterimagePass.enabled = !!enabled; }); | |
| this.eventBus.subscribe('ui:setMotionBlur', (value) => { if (this.afterimagePass && this.afterimagePass.uniforms && this.afterimagePass.uniforms['damp']) this.afterimagePass.uniforms['damp'].value = 1.0 - Math.max(0, Math.min(1, value)); }); | |
| this.eventBus.subscribe('external:donkiFlare', (flares) => { | |
| // Visual reaction: brief godray exposure bump | |
| if (this.godrayPass) this.godrayPass.uniforms.exposure.value = 0.28; | |
| setTimeout(() => { if (this.godrayPass) this.godrayPass.uniforms.exposure.value = 0.18; }, 3000); | |
| this.uiManager.log(`Solar flare reported by NASA DONKI: ${flares.length} events`, 'data'); | |
| }); | |
| this.eventBus.subscribe('external:neo', (data) => { | |
| // Spawn small NEO markers near system center as transient points | |
| const count = Math.min(50, Object.values(data.near_earth_objects || {}).flat().length || 0); | |
| if (count > 0) { | |
| const positions = new Float32Array(count * 3); | |
| for (let i=0;i<count;i++){ positions[i*3]=(Math.random()-0.5)*3000; positions[i*3+1]=(Math.random()-0.5)*300; positions[i*3+2]=(Math.random()-0.5)*3000; } | |
| const geom = new THREE.BufferGeometry(); | |
| geom.setAttribute('position', new THREE.BufferAttribute(positions,3)); | |
| const mat = new THREE.PointsMaterial({ color: 0xffee88, size: 2, blending: THREE.AdditiveBlending, transparent: true }); | |
| const neoPoints = new THREE.Points(geom, mat); | |
| neoPoints.userData = { isGenerated: true, isAutonomous: true, life: 15 }; | |
| this.scene.add(neoPoints); | |
| this.uiManager.log(`NEO feed spawned ${count} transient markers.`, 'data'); | |
| } | |
| }); | |
| this.eventBus.subscribe('external:solsys', (ref) => { this.solarSystemRef = ref; this.uiManager.log('Solar system reference loaded.', 'data'); }); | |
| this.eventBus.subscribe('ui:locateEarth', () => this.focusEarth()); | |
| this.eventBus.subscribe('audio:data', ({ raw, bands }) => { | |
| const volume = raw.reduce((a, b) => a + b) / raw.length / 255; | |
| this.cosmicPulse = volume; | |
| this.cosmicGrowthEnergy += volume * 0.1; | |
| if (bands) this.audioSignal = { bass: bands.bass, mid: bands.mid, treble: bands.treble, hue: bands.hue, energy: volume }; | |
| this.eventBus.publish('telemetry:append', { type: 'audio', tokens: { volume, ...(bands||{}) }, t: Date.now() }); | |
| }); | |
| this.renderer.domElement.addEventListener('click', (e) => { | |
| if (this.pointerLockControls.isLocked) return; | |
| this.mouse.x = (e.clientX / window.innerWidth) * 2 - 1; | |
| this.mouse.y = - (e.clientY / window.innerHeight) * 2 + 1; | |
| this.raycaster.setFromCamera(this.mouse, this.camera); | |
| if (this.cosmicWebView.active) { | |
| const intersects = this.raycaster.intersectObject(this.cosmicWeb); | |
| if (intersects.length > 0) { | |
| const i = intersects[0].index; | |
| const pos = this.cosmicWeb.geometry.attributes.position; | |
| const newCoords = new THREE.Vector3(pos.getX(i), pos.getY(i), pos.getZ(i)); | |
| this.eventBus.publish("ui:regenerate", newCoords); | |
| } | |
| } else if(this.hiddenEcho) { | |
| const intersects = this.raycaster.intersectObject(this.hiddenEcho, false); | |
| if (intersects.length > 0) { | |
| this.eventBus.publish('pge:echoFound'); | |
| } | |
| } | |
| }); | |
| } | |
| // --- Core Generation --- | |
| generateUniverse(seedArray, influence) { | |
| this.cleanupScene(); | |
| this.prng = sfc32(...seedArray); | |
| this.lastInfluence = influence || this.lastInfluence; | |
| // Derive flora species palette from seed for consistent variety | |
| const h1 = (seedArray[0] % 360) / 360; | |
| const h2 = (seedArray[1] % 360) / 360; | |
| const h3 = (seedArray[2] % 360) / 360; | |
| this.floraPalette = [h1, h2, h3]; | |
| const starTemp = 2000 + this.prng() * 18000; | |
| const starColor = this.blackBodyToRGB(starTemp); | |
| const starSize = 20 + this.prng() * 25; | |
| const light = new THREE.PointLight(starColor, 2, 20000, 2); | |
| light.userData.isGenerated = true; | |
| this.scene.add(light); | |
| this.initLensflare(light, starColor); | |
| const star = new THREE.Mesh( new THREE.SphereGeometry(starSize, 64, 64), new THREE.MeshBasicMaterial({ color: starColor, map: this.createStarTexture() }) ); | |
| star.userData = { isGenerated: true, mass: starSize * 1000 }; | |
| this.scene.add(star); | |
| this.activeStar = star; | |
| // Add physically-inspired corona shell (visual-only) | |
| this.createStarCorona(this.activeStar, starColor, starTemp, starSize); | |
| // Add photosphere surface with limb darkening and granulation | |
| this.createStarPhotosphere(this.activeStar, starColor, starTemp, starSize); | |
| const planetCount = Math.floor(3 + this.prng() * 7); | |
| for(let i = 0; i < planetCount; i++) { | |
| this.createPlanet(starSize, i, influence); | |
| } | |
| // Initialize asteroid belts immediately | |
| this.createAsteroidBelt(starSize * 4.5 + 80, starSize * 5.5 + 200, Math.floor(2000 + this.prng()*3000)); | |
| if (this.prng() > 0.5) this.createAsteroidBelt(starSize * 8.0 + 300, starSize * 9.5 + 600, Math.floor(1500 + this.prng()*2500)); | |
| // Optionally add NASA solar system reference planets at a distant scaled ring | |
| if (this.solarSystemRef) { | |
| const scale = 1/4000000; // very rough scale-down for visibility | |
| this.solarSystemRef.planets.forEach((p, idx) => { | |
| const r = p.semiMajorKm * scale; | |
| const baseRadius = Math.max(2, p.radiusKm * scale); | |
| const ang = this.prng() * Math.PI * 2; | |
| if (p.name === 'Earth') { | |
| (async () => { | |
| const earthObj = await this.createNASAEarth(r, baseRadius, ang); | |
| const toAdd = earthObj || (function(){ | |
| const fallback = new THREE.Group(); | |
| const sphere = new THREE.Mesh(new THREE.SphereGeometry(baseRadius, 32, 32), new THREE.MeshStandardMaterial({ color: 0x3a9cff })); | |
| fallback.add(sphere); | |
| return fallback; | |
| })(); | |
| this.scene.add(toAdd); | |
| this.nasaBodies.push(toAdd); | |
| this.earthRef = toAdd; | |
| })(); | |
| } else { | |
| const group = this.createNASAPlanet(p.name, r, baseRadius, ang, p.color); | |
| this.scene.add(group); | |
| this.nasaBodies.push(group); | |
| } | |
| }); | |
| this.uiManager.log('NASA reference planets manifested in the far field.', 'data'); | |
| } | |
| this.createNebula(starColor); | |
| // Map genesis influence to nebula and aurora baselines | |
| if (this.nebula && this.nebula.material && this.nebula.material.uniforms) { | |
| if (this.lastInfluence.hasPlant) { | |
| this.nebula.material.uniforms.density.value = Math.min(1.2, this.nebula.material.uniforms.density.value + 0.15); | |
| } | |
| if (this.lastInfluence.createBlackHole) { | |
| this.nebula.material.uniforms.noiseScale.value = Math.min(6.0, this.nebula.material.uniforms.noiseScale.value + 0.5); | |
| } | |
| } | |
| this.applyGenesisVisualProfile(starColor); | |
| this.hiddenEcho = this.createEchoParticle(); | |
| this.scene.add(this.hiddenEcho); | |
| // Build genesis light lattice to connect bodies with light | |
| this.buildGenesisLattice(); | |
| if (influence.createBlackHole || this.prng() > 0.9) { | |
| if (influence.createBlackHole) this.uiManager.log("Major seismic event detected! Spawning a black hole.", "warn"); | |
| this.createBlackHole(); | |
| } | |
| if (this.isAmbientAudioEnabled) { | |
| this.proceduralAudio = new ProceduralAudioEngine(this.scene, this.listener, { star: this.activeStar, starTemp: starTemp, planets: this.planets }); | |
| } | |
| this.spawnKnowledgeMonolithsIfNeeded(); | |
| this.uiManager.log("Universe generation complete.", 'success'); | |
| } | |
| createPlanet(starSize, index, influence) { | |
| const planetSize = 2 + this.prng() * 8; | |
| const distance = starSize * 4 + index * (100 + this.prng()*100); | |
| const angle = this.prng() * Math.PI * 2; | |
| const planet = new THREE.Mesh( | |
| new THREE.SphereGeometry(planetSize, 32, 32), | |
| new THREE.MeshStandardMaterial({ map: this.createPlanetTexture(), roughness: this.prng() }) | |
| ); | |
| planet.position.set(Math.cos(angle) * distance, 0, Math.sin(angle) * distance); | |
| const orbitSpeed = Math.sqrt((this.G * this.activeStar.userData.mass) / distance); | |
| const initialVelocity = new THREE.Vector3(-Math.sin(angle), 0, Math.cos(angle)).multiplyScalar(orbitSpeed); | |
| planet.userData = { | |
| isGenerated: true, | |
| isPlanet: true, | |
| mass: planetSize * 100, | |
| velocity: initialVelocity, | |
| distance: distance // Keep for audio generation | |
| }; | |
| const atmosphere = this.createAtmosphere(planetSize, new THREE.Color().setHSL(this.prng(), 0.8, 0.7)); | |
| planet.add(atmosphere); | |
| this.scene.add(planet); | |
| this.planets.push(planet); | |
| // Genesis influence baseline aurora intensity | |
| if (this.lastInfluence && this.lastInfluence.hasPlant && atmosphere.material && atmosphere.material.uniforms && atmosphere.material.uniforms.auroraIntensity) { | |
| atmosphere.material.uniforms.auroraIntensity.value = 0.15; | |
| } | |
| if (influence.hasPlant && this.prng() > 0.5) { | |
| const flora = this.createLSystemFlora(); | |
| flora.scale.set(planetSize/10, planetSize/10, planetSize/10); | |
| const surfacePosition = new THREE.Vector3(0, planetSize, 0).applyAxisAngle(new THREE.Vector3(1,0,0), this.prng() * Math.PI); | |
| flora.position.copy(surfacePosition); | |
| flora.lookAt(new THREE.Vector3(0,0,0)); | |
| planet.add(flora); | |
| this.uiManager.log("Generated L-System flora on a planet.", 'data'); | |
| } | |
| if(this.prng() > 0.6) { | |
| this.createRings(planet, planetSize); | |
| } | |
| } | |
| createBlackHole() { | |
| const size = 15 + this.prng() * 10; | |
| const blackHole = new THREE.Mesh( new THREE.SphereGeometry(size, 64, 64), new THREE.MeshBasicMaterial({ color: 0x000000 }) ); | |
| const dist = 1000 + this.prng() * 2000; | |
| const angle = this.prng() * Math.PI * 2; | |
| blackHole.position.set(Math.cos(angle) * dist, this.prng() * 200 - 100, Math.sin(angle) * dist); | |
| blackHole.userData = { isGenerated: true, isBlackHole: true, mass: size * 5000 }; | |
| this.scene.add(blackHole); | |
| this.blackHoles.push(blackHole); | |
| const diskGeom = new THREE.RingGeometry(size * 1.2, size * 3, 128); | |
| const diskMat = new THREE.ShaderMaterial({ | |
| uniforms: { time: { value: 0 } }, | |
| vertexShader: `varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform float time; varying vec2 vUv; | |
| float noise(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); } | |
| void main() { | |
| float radius = length(vUv - 0.5); | |
| float n = noise(vUv * 5.0 + time * 0.2); | |
| vec3 color = mix(vec3(1.0, 0.5, 0.1), vec3(0.8, 0.1, 1.0), radius * 2.0); | |
| gl_FragColor = vec4(color * n, 1.0) * (1.0 - radius * 1.8); | |
| }`, | |
| side: THREE.DoubleSide, transparent: true, blending: THREE.AdditiveBlending | |
| }); | |
| const disk = new THREE.Mesh(diskGeom, diskMat); | |
| disk.rotation.x = Math.PI / 2; | |
| blackHole.add(disk); | |
| blackHole.userData.disk = disk; | |
| } | |
| cleanupScene() { | |
| if(this.proceduralAudio) { | |
| this.proceduralAudio.destroy(); | |
| this.proceduralAudio = null; | |
| } | |
| const toRemove = []; | |
| this.scene.traverse(child => { try { if (child && child.userData && child.userData.isGenerated) toRemove.push(child); } catch(e) { /* ignore */ } }); | |
| // Defer actual removal until after traversal | |
| this.pendingRemovals.push(...toRemove); | |
| this.specialObjects.forEach(obj => { if (obj.destroy) obj.destroy(); }); | |
| if (this.genesisLattice && this.genesisLattice.parent) this.genesisLattice.parent.remove(this.genesisLattice); | |
| this.genesisLattice = null; | |
| this.asteroidBelts.forEach(b => { if (b.parent) b.parent.remove(b); if (b.geometry) b.geometry.dispose(); if (b.material) b.material.dispose(); }); | |
| this.asteroidBelts = []; | |
| this.specialObjects = []; | |
| this.planets = []; | |
| this.blackHoles = []; | |
| this.activeStar = null; | |
| this.hiddenEcho = null; | |
| } | |
| addSpecialObject(obj) { | |
| const dist = 2000 + this.prng() * 5000; | |
| const angle = this.prng() * Math.PI * 2; | |
| obj.mesh.position.set( | |
| Math.cos(angle) * dist, | |
| (this.prng() - 0.5) * 800, | |
| Math.sin(angle) * dist | |
| ); | |
| obj.mesh.userData.isSpecialObject = true; | |
| this.specialObjects.push(obj); | |
| this.scene.add(obj.mesh); | |
| this.uiManager.log(`Memory Token manifested at distant coordinates.`, 'data'); | |
| } | |
| // --- Visual Components & Effects --- | |
| createAtmosphere(planetSize, color) { | |
| const atmosphereMat = new THREE.ShaderMaterial({ | |
| uniforms: { | |
| time: { value: 0 }, | |
| planetRadius: { value: planetSize }, | |
| cameraPos: { value: new THREE.Vector3() }, | |
| lightDir: { value: new THREE.Vector3(0.0, 1.0, 0.0) }, | |
| betaRay: { value: new THREE.Vector3(5.5e-6, 13.0e-6, 22.4e-6) }, | |
| betaMie: { value: new THREE.Vector3(21e-6, 21e-6, 21e-6) }, | |
| mieG: { value: 0.76 }, | |
| intensityBoost: { value: 1.0 }, | |
| auroraIntensity: { value: 0.0 } | |
| }, | |
| vertexShader: ` | |
| varying vec3 vWorldPos; | |
| varying vec3 vNormal; | |
| void main() { | |
| vNormal = normalize(normalMatrix * normal); | |
| vec4 world = modelMatrix * vec4(position, 1.0); | |
| vWorldPos = world.xyz; | |
| gl_Position = projectionMatrix * viewMatrix * world; | |
| } | |
| `, | |
| fragmentShader: ` | |
| precision highp float; | |
| varying vec3 vWorldPos; | |
| varying vec3 vNormal; | |
| uniform vec3 cameraPos; | |
| uniform vec3 lightDir; | |
| uniform vec3 betaRay; | |
| uniform vec3 betaMie; | |
| uniform float mieG; | |
| uniform float intensityBoost; | |
| uniform float auroraIntensity; | |
| float rayleighPhase(float cosTheta) { | |
| return 3.0/(16.0*3.14159265) * (1.0 + cosTheta*cosTheta); | |
| } | |
| float hgPhase(float cosTheta, float g) { | |
| float g2 = g*g; float denom = pow(1.0 + g2 - 2.0*g*cosTheta, 1.5); | |
| return (1.0 - g2) / (4.0*3.14159265*denom); | |
| } | |
| void main() { | |
| vec3 viewDir = normalize(vWorldPos - cameraPos); | |
| float mu = dot(viewDir, normalize(lightDir)); | |
| float pr = rayleighPhase(mu); | |
| float pm = hgPhase(mu, mieG); | |
| vec3 scatter = betaRay * pr + betaMie * pm; | |
| float horizon = clamp(1.0 - abs(vNormal.y), 0.0, 1.0); | |
| vec3 color = scatter * (0.5 + 0.5*horizon) * intensityBoost * 5.0; | |
| // Optional aurora tinting | |
| float aur = smoothstep(0.7, 1.0, horizon) * auroraIntensity; | |
| color = mix(color, vec3(0.3, 0.9, 0.7), aur); | |
| gl_FragColor = vec4(color, 0.9 * (0.7 + 0.3*horizon)); | |
| } | |
| `, | |
| blending: THREE.AdditiveBlending, | |
| side: THREE.BackSide, | |
| transparent: true, | |
| depthWrite: false | |
| }); | |
| const atmosphere = new THREE.Mesh(new THREE.SphereGeometry(planetSize * 1.15, 32, 32), atmosphereMat); | |
| atmosphere.userData = { isGenerated: true, isAtmosphere: true }; | |
| return atmosphere; | |
| } | |
| createStarCorona(star, color, temp, size) { | |
| const tNorm = (temp - 2000) / 18000; // 0..1 | |
| const coronaGeo = new THREE.SphereGeometry(size * 1.3, 64, 64); | |
| const coronaMat = new THREE.ShaderMaterial({ | |
| uniforms: { | |
| time: { value: 0.0 }, | |
| baseColor: { value: color }, | |
| intensity: { value: 0.6 + 0.8 * tNorm }, | |
| noiseScale: { value: 1.5 + 2.5 * tNorm } | |
| }, | |
| vertexShader: `varying vec3 vPos; varying vec3 vNormal; void main(){ vPos = position; vNormal = normalize(normalMatrix * normal); gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`, | |
| fragmentShader: `varying vec3 vPos; varying vec3 vNormal; uniform vec3 baseColor; uniform float intensity; uniform float noiseScale; uniform float time; | |
| float noise(vec3 p){ return fract(sin(dot(p, vec3(12.9898,78.233,151.7182))) * 43758.5453); } | |
| float fbm(vec3 p){ float f=0.0; f += 0.5000*noise(p); p*=2.02; f += 0.2500*noise(p+time*0.05); p*=2.03; f += 0.1250*noise(p-time*0.04); return f; } | |
| void main(){ float rim = pow(1.0 - abs(dot(vNormal, vec3(0.0,0.0,1.0))), 3.0); float n = fbm(vPos * noiseScale); vec3 c = baseColor * (0.6 + 0.6*n) * intensity; gl_FragColor = vec4(c, rim * (0.4 + 0.6*n)); }`, | |
| blending: THREE.AdditiveBlending, | |
| transparent: true, | |
| depthWrite: false, | |
| side: THREE.BackSide | |
| }); | |
| const corona = new THREE.Mesh(coronaGeo, coronaMat); | |
| corona.userData = { isGenerated: true, isStarCorona: true }; | |
| star.add(corona); | |
| star.userData.corona = corona; | |
| } | |
| createStarPhotosphere(star, color, temp, size) { | |
| const tNorm = (temp - 2000) / 18000; // 0..1 | |
| const photoGeo = new THREE.SphereGeometry(size * 1.02, 64, 64); | |
| const photoMat = new THREE.ShaderMaterial({ | |
| uniforms: { | |
| time: { value: 0.0 }, | |
| baseColor: { value: color }, | |
| limbPower: { value: 0.6 + 0.3 * (1.0 - tNorm) }, | |
| granularity: { value: 1.8 + 3.0 * tNorm }, | |
| spotStrength: { value: 0.0 } | |
| }, | |
| vertexShader: `varying vec3 vNormal; varying vec3 vPos; void main(){ vNormal = normalize(normalMatrix*normal); vPos = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`, | |
| fragmentShader: `varying vec3 vNormal; varying vec3 vPos; uniform vec3 baseColor; uniform float limbPower; uniform float granularity; uniform float time; uniform float spotStrength; | |
| float noise(vec3 p){ return fract(sin(dot(p, vec3(12.9898,78.233,151.7182))) * 43758.5453); } | |
| float fbm(vec3 p){ float f=0.0; f += 0.5*noise(p); p*=2.1; f += 0.25*noise(p+time*0.05); p*=2.1; f += 0.125*noise(p-time*0.03); return f; } | |
| void main(){ float ndotl = clamp(dot(vNormal, vec3(0.0,0.0,1.0)), 0.0, 1.0); float limb = pow(ndotl, limbPower); float gran = fbm(vPos*granularity); float spots = smoothstep(0.6, 1.0, fbm(vPos*granularity*0.7)); vec3 c = baseColor * (1.0 + gran*0.25); c *= 1.0 - spotStrength * spots * 0.4; gl_FragColor = vec4(c * limb, 1.0); }`, | |
| blending: THREE.AdditiveBlending, | |
| transparent: false, | |
| depthWrite: true, | |
| side: THREE.FrontSide | |
| }); | |
| const photo = new THREE.Mesh(photoGeo, photoMat); | |
| photo.userData = { isGenerated: true, isStarPhotosphere: true }; | |
| star.add(photo); | |
| star.userData.photosphere = photo; | |
| } | |
| createNebula(baseColor) { | |
| const nebulaMaterial = new THREE.ShaderMaterial({ | |
| uniforms: { time: { value: 0 }, baseColor: { value: baseColor }, noiseScale: { value: 2.0 + this.prng() * 3.0 }, density: { value: 0.3 + this.prng() * 0.4 }, pulse: { value: 0.0 } }, | |
| vertexShader: `varying vec3 vPosition; void main() { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, | |
| fragmentShader: ` | |
| uniform float time; uniform vec3 baseColor; uniform float noiseScale; uniform float density; uniform float pulse; | |
| varying vec3 vPosition; | |
| vec3 hash(vec3 p) { p = vec3(dot(p,vec3(127.1,311.7, 74.7)), dot(p,vec3(269.5,183.3,246.1)), dot(p,vec3(113.5,271.9,124.6))); return -1.0 + 2.0*fract(sin(p)*43758.5453123); } | |
| float noise(in vec3 p) { vec3 i = floor(p), f = fract(p), u = f*f*(3.0-2.0*f); return mix(mix(mix(dot(hash(i+vec3(0,0,0)),f-vec3(0,0,0)), dot(hash(i+vec3(1,0,0)),f-vec3(1,0,0)),u.x), mix(dot(hash(i+vec3(0,1,0)),f-vec3(0,1,0)), dot(hash(i+vec3(1,1,0)),f-vec3(1,1,0)),u.x),u.y), mix(mix(dot(hash(i+vec3(0,0,1)),f-vec3(0,0,1)), dot(hash(i+vec3(1,0,1)),f-vec3(1,0,1)),u.x), mix(dot(hash(i+vec3(0,1,1)),f-vec3(0,1,1)), dot(hash(i+vec3(1,1,1)),f-vec3(1,1,1)),u.x),u.y),u.z); } | |
| float fbm(vec3 p) { float f = 0.0; f += 0.5000*noise(p); p *= 2.02; f += 0.2500*noise(p); p *= 2.03; f += 0.1250*noise(p); return f; } | |
| void main() { vec3 pos = vPosition / (8000.0 / noiseScale); pos.x += time * 0.01; float noiseVal = fbm(pos); gl_FragColor = vec4(baseColor * (noiseVal + pulse) * 2.0, noiseVal * density); }`, | |
| transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.BackSide, | |
| }); | |
| const nebula = new THREE.Mesh(new THREE.SphereGeometry(8000, 32, 32), nebulaMaterial); | |
| nebula.userData = { isGenerated: true, isNebula: true, baseDensity: nebulaMaterial.uniforms.density.value, baseNoiseScale: nebulaMaterial.uniforms.noiseScale.value }; | |
| this.scene.add(nebula); | |
| this.nebula = nebula; | |
| return nebula; | |
| } | |
| createStarfield(count, spread, size, transparent) { | |
| const positions = []; | |
| for (let i = 0; i < count; i++) { | |
| positions.push(THREE.MathUtils.randFloatSpread(spread), THREE.MathUtils.randFloatSpread(spread), THREE.MathUtils.randFloatSpread(spread)); | |
| } | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); | |
| const material = new THREE.PointsMaterial({ color: 0xaaaaaa, size, transparent, opacity: transparent ? 0.5 : 1.0 }); | |
| return new THREE.Points(geometry, material); | |
| } | |
| createEchoParticle() { | |
| const geometry = new THREE.TorusKnotGeometry(10, 3, 100, 16); | |
| const material = new THREE.MeshNormalMaterial({ wireframe: true }); | |
| const echoMesh = new THREE.Mesh(geometry, material); | |
| const dist = 500 + this.prng() * 1500; | |
| const angle = this.prng() * Math.PI * 2; | |
| echoMesh.position.set(Math.cos(angle) * dist, this.prng() * 400 - 200, Math.sin(angle) * dist); | |
| echoMesh.userData = { isGenerated: true, isEcho: true }; | |
| return echoMesh; | |
| } | |
| initLensflare(light, color) { | |
| const textureLoader = new THREE.TextureLoader(); | |
| const texFlare0 = textureLoader.load('https://raw.githubusercontent.com/mrdoob/three.js/master/examples/textures/lensflare/lensflare0.png'); | |
| const texFlare3 = textureLoader.load('https://raw.githubusercontent.com/mrdoob/three.js/master/examples/textures/lensflare/lensflare3.png'); | |
| const lensflare = new Lensflare(); | |
| lensflare.addElement(new LensflareElement(texFlare0, 700, 0, color)); | |
| lensflare.addElement(new LensflareElement(texFlare3, 60, 0.6)); | |
| lensflare.addElement(new LensflareElement(texFlare3, 70, 0.7)); | |
| lensflare.addElement(new LensflareElement(texFlare3, 120, 0.9)); | |
| lensflare.addElement(new LensflareElement(texFlare3, 70, 1.0)); | |
| lensflare.userData.isGenerated = true; | |
| light.add(lensflare); | |
| } | |
| createLSystemFlora() { | |
| const rules = { 'F': 'FF-[-F+F+F]+[+F-F-F]' }; | |
| let axiom = 'F'; | |
| const iterations = 3; | |
| for (let i = 0; i < iterations; i++) { | |
| axiom = axiom.split('').map(char => rules[char] || char).join(''); | |
| } | |
| const points = []; | |
| const state = { pos: new THREE.Vector3(), dir: new THREE.Vector3(0, 1, 0) }; | |
| const stack = []; | |
| const angle = THREE.MathUtils.degToRad(25); | |
| const length = 5; | |
| for (const char of axiom) { | |
| switch (char) { | |
| case 'F': | |
| points.push(state.pos.clone()); | |
| state.pos.add(state.dir.clone().multiplyScalar(length)); | |
| points.push(state.pos.clone()); | |
| break; | |
| case '+': | |
| state.dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), angle); | |
| break; | |
| case '-': | |
| state.dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), -angle); | |
| break; | |
| case '[': | |
| stack.push({ ...state, dir: state.dir.clone() }); | |
| break; | |
| case ']': | |
| Object.assign(state, stack.pop()); | |
| break; | |
| } | |
| } | |
| const geometry = new THREE.BufferGeometry().setFromPoints(points); | |
| const floraHue = (this.floraPalette ? this.floraPalette[Math.floor(this.prng()*this.floraPalette.length)] : 0.33); | |
| const material = new THREE.LineBasicMaterial({ color: new THREE.Color().setHSL(floraHue, 0.8, 0.6) }); | |
| const flora = new THREE.LineSegments(geometry, material); | |
| flora.userData = { isGenerated: true, isFlora: true, growth: 0.0, swayPhase: Math.random() * Math.PI * 2 }; | |
| // Add simple fruit nodes as points (start small and color-shift when ripe) | |
| const fruitCount = 8 + Math.floor(this.prng() * 12); | |
| const fruitPositions = new Float32Array(fruitCount * 3); | |
| const fruitColors = new Float32Array(fruitCount * 3); | |
| for (let i = 0; i < fruitCount; i++) { | |
| const base = points[Math.floor(this.prng() * points.length)] || new THREE.Vector3(); | |
| fruitPositions[i*3] = base.x + (this.prng()-0.5)*2; | |
| fruitPositions[i*3+1] = base.y + (this.prng()-0.5)*2; | |
| fruitPositions[i*3+2] = base.z + (this.prng()-0.5)*2; | |
| const c = new THREE.Color().setHSL(0.33, 0.8, 0.5); // green start | |
| fruitColors[i*3] = c.r; fruitColors[i*3+1] = c.g; fruitColors[i*3+2] = c.b; | |
| } | |
| const fruitGeom = new THREE.BufferGeometry(); | |
| fruitGeom.setAttribute('position', new THREE.BufferAttribute(fruitPositions, 3)); | |
| fruitGeom.setAttribute('color', new THREE.BufferAttribute(fruitColors, 3)); | |
| const sizeBySpecies = 1.2 + (floraHue * 1.8); | |
| const fruitMat = new THREE.PointsMaterial({ size: sizeBySpecies, vertexColors: true, blending: THREE.AdditiveBlending, depthWrite: false, transparent: true, opacity: 0.9 }); | |
| const fruits = new THREE.Points(fruitGeom, fruitMat); | |
| fruits.userData = { isGenerated: true, isFruit: true, ripe: 0.0 }; | |
| flora.add(fruits); | |
| flora.userData.fruits = fruits; | |
| return flora; | |
| } | |
| createRings(planet, planetSize) { | |
| const ringColor = new THREE.Color().setHSL(this.prng(), 0.5, 0.6); | |
| const innerRadius = planetSize * 1.5; | |
| const outerRadius = innerRadius + this.prng() * planetSize; | |
| const ringGeom = new THREE.RingGeometry(innerRadius, outerRadius, 64); | |
| const ringMat = new THREE.MeshBasicMaterial({ color: ringColor, side: THREE.DoubleSide, transparent: true, opacity: 0.6 }); | |
| const ring = new THREE.Mesh(ringGeom, ringMat); | |
| ring.rotation.x = Math.PI / 2 + (this.prng() - 0.5) * 0.5; | |
| ring.userData = { isRing: true, baseTilt: ring.rotation.x }; | |
| planet.add(ring); | |
| } | |
| createAsteroidBelt(innerRadius, outerRadius, count) { | |
| const positions = new Float32Array(count * 3); | |
| const colors = new Float32Array(count * 3); | |
| for (let i = 0; i < count; i++) { | |
| const r = THREE.MathUtils.lerp(innerRadius, outerRadius, this.prng()); | |
| const ang = this.prng() * Math.PI * 2; | |
| const jitter = (this.prng()-0.5) * 5; | |
| positions[i*3] = Math.cos(ang) * r; | |
| positions[i*3+1] = (this.prng()-0.5) * 10; | |
| positions[i*3+2] = Math.sin(ang) * r + jitter; | |
| const c = new THREE.Color().setHSL(0.1 + this.prng()*0.1, 0.3, 0.5 + this.prng()*0.2); | |
| colors[i*3] = c.r; colors[i*3+1] = c.g; colors[i*3+2] = c.b; | |
| } | |
| const geom = new THREE.BufferGeometry(); | |
| geom.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
| geom.setAttribute('color', new THREE.BufferAttribute(colors, 3)); | |
| const mat = new THREE.PointsMaterial({ size: 1.2, vertexColors: true, transparent: true, opacity: 0.85 }); | |
| const belt = new THREE.Points(geom, mat); | |
| belt.userData = { isGenerated: true, isAsteroidBelt: true, innerRadius, outerRadius, baseOpacity: mat.opacity }; | |
| this.scene.add(belt); | |
| this.asteroidBelts.push(belt); | |
| return belt; | |
| } | |
| buildGenesisLattice() { | |
| const nodes = []; | |
| if (this.activeStar) nodes.push(this.activeStar.position.clone()); | |
| this.planets.forEach(p => nodes.push(p.position.clone())); | |
| this.blackHoles.forEach(b => nodes.push(b.position.clone())); | |
| this.scene.traverse(o => { if (o.userData && o.userData.isMonolith) nodes.push(o.position.clone()); }); | |
| if (nodes.length < 2) return; | |
| const segments = []; | |
| for (let i = 0; i < nodes.length; i++) { | |
| for (let j = i+1; j < nodes.length; j++) { | |
| const a = nodes[i], b = nodes[j]; | |
| const d = a.distanceTo(b); | |
| if (d < 8000) { // connect reasonably close bodies | |
| segments.push(a.x, a.y, a.z, b.x, b.y, b.z); | |
| } | |
| } | |
| } | |
| if (segments.length === 0) return; | |
| const pos = new Float32Array(segments); | |
| const geom = new THREE.BufferGeometry(); | |
| geom.setAttribute('position', new THREE.BufferAttribute(pos, 3)); | |
| const mat = new THREE.LineBasicMaterial({ color: 0x88ccff, transparent: true, opacity: 0.15, blending: THREE.AdditiveBlending }); | |
| const lattice = new THREE.LineSegments(geom, mat); | |
| lattice.userData = { isGenerated: true, isGenesisLattice: true }; | |
| if (this.genesisLattice && this.genesisLattice.parent) this.genesisLattice.parent.remove(this.genesisLattice); | |
| this.genesisLattice = lattice; | |
| this.scene.add(lattice); | |
| } | |
| createStarTexture() { | |
| const size = 256; | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = size; | |
| canvas.height = size; | |
| const context = canvas.getContext('2d'); | |
| const gradient = context.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2); | |
| gradient.addColorStop(0, 'rgba(255,255,255,1)'); | |
| gradient.addColorStop(0.2, 'rgba(255,255,200,1)'); | |
| gradient.addColorStop(0.4, 'rgba(255,200,0,1)'); | |
| gradient.addColorStop(1, 'rgba(255,150,0,0)'); | |
| context.fillStyle = gradient; | |
| context.fillRect(0,0,size,size); | |
| return new THREE.CanvasTexture(canvas); | |
| } | |
| loadTexture(url) { | |
| return new Promise((resolve, reject) => { | |
| const loader = new THREE.TextureLoader(); | |
| loader.setCrossOrigin('anonymous'); | |
| loader.load(url, tex => { tex.colorSpace = THREE.SRGBColorSpace || THREE.sRGBEncoding; resolve(tex); }, undefined, err => resolve(null)); | |
| }); | |
| } | |
| addEarthMarker(earthNode) { | |
| // Determine display radius whether node is a Mesh or a Group containing a sphere | |
| let radius = 2; | |
| let sphere = null; | |
| if (earthNode && earthNode.geometry && earthNode.geometry.parameters && earthNode.geometry.parameters.radius) { | |
| radius = earthNode.geometry.parameters.radius; | |
| sphere = earthNode; | |
| } else if (earthNode && earthNode.traverse) { | |
| earthNode.traverse(child => { | |
| if (!sphere && child.isMesh && child.geometry && child.geometry.parameters && child.geometry.parameters.radius) { | |
| sphere = child; | |
| radius = child.geometry.parameters.radius; | |
| } | |
| }); | |
| } | |
| // Add a subtle halo and a floating label that faces the camera | |
| const haloGeo = new THREE.SphereGeometry(radius * 1.8, 16, 16); | |
| const haloMat = new THREE.MeshBasicMaterial({ color: 0x4da6ff, transparent: true, opacity: 0.2, blending: THREE.AdditiveBlending }); | |
| const halo = new THREE.Mesh(haloGeo, haloMat); | |
| halo.userData = { isGenerated: true }; | |
| earthNode.add(halo); | |
| // Label as a small plane with emissive blue | |
| const labelCanvas = document.createElement('canvas'); | |
| labelCanvas.width = 256; labelCanvas.height = 64; | |
| const ctx = labelCanvas.getContext('2d'); | |
| ctx.fillStyle = 'rgba(0,0,0,0)'; ctx.fillRect(0,0,256,64); | |
| ctx.font = '28px Inter, sans-serif'; | |
| ctx.fillStyle = '#9cc9ff'; | |
| ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; | |
| ctx.fillText('EARTH', 128, 32); | |
| const tex = new THREE.CanvasTexture(labelCanvas); | |
| const plane = new THREE.Mesh(new THREE.PlaneGeometry(60, 15), new THREE.MeshBasicMaterial({ map: tex, transparent: true })); | |
| plane.position.set(0, radius * 3, 0); | |
| plane.userData = { isGenerated: true, isBillboard: true }; | |
| earthNode.add(plane); | |
| } | |
| createPlanetTexture() { | |
| const size = 512; | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = size; | |
| canvas.height = size; | |
| const context = canvas.getContext('2d'); | |
| context.fillStyle = new THREE.Color().setHSL(this.prng(), 0.5, 0.3).getStyle(); | |
| context.fillRect(0,0,size,size); | |
| for(let i=0; i<5000; i++) { | |
| context.fillStyle = new THREE.Color().setHSL(this.prng(), 0.5, this.prng() * 0.4 + 0.2).getStyle(); | |
| context.beginPath(); | |
| context.arc(this.prng()*size, this.prng()*size, this.prng()*size*0.1, 0, Math.PI*2); | |
| context.fill(); | |
| } | |
| return new THREE.CanvasTexture(canvas); | |
| } | |
| async createNASAEarth(orbitRadius, earthRadius, angle) { | |
| // Blue marble base sphere | |
| const earthGroup = new THREE.Group(); | |
| // High-quality textures (Blue Marble/Night/Clouds) from public sources | |
| // CDN mirrors for Blue Marble-like assets | |
| // Alternate CDN mirrors | |
| const dayUrl = 'https://unpkg.com/three-globe/example/img/earth-blue-marble.jpg'; | |
| const nightUrl = 'https://unpkg.com/three-globe/example/img/earth-night.jpg'; | |
| const bumpUrl = 'https://unpkg.com/three-globe/example/img/earth-topology.png'; | |
| const specUrl = 'https://unpkg.com/three-globe/example/img/earth-water.png'; | |
| const cloudUrl = 'https://unpkg.com/three-globe/example/img/earth-clouds.png'; | |
| const [dayTex, nightTex, bumpTex, specTex, cloudsTex] = await Promise.all([ | |
| this.loadTexture(dayUrl), this.loadTexture(nightUrl), this.loadTexture(bumpUrl), this.loadTexture(specUrl), this.loadTexture(cloudUrl) | |
| ]); | |
| let sphere; | |
| if (dayTex && nightTex) { | |
| const earthShader = { | |
| uniforms: { | |
| dayMap: { value: dayTex }, | |
| nightMap: { value: nightTex }, | |
| specMap: { value: specTex || null }, | |
| cameraPos: { value: new THREE.Vector3() }, | |
| lightDir: { value: new THREE.Vector3(1,0,0) }, | |
| time: { value: 0 }, | |
| glowThreshold: { value: 0.1 }, | |
| glowIntensity: { value: 0.6 } | |
| }, | |
| vertexShader: ` | |
| varying vec3 vWorldPos; varying vec3 vWorldNormal; varying vec2 vUv; | |
| void main(){ | |
| vUv = uv; | |
| vec4 world = modelMatrix * vec4(position,1.0); | |
| vWorldPos = world.xyz; | |
| vWorldNormal = normalize(mat3(modelMatrix) * normal); | |
| gl_Position = projectionMatrix * viewMatrix * world; | |
| } | |
| `, | |
| fragmentShader: ` | |
| uniform sampler2D dayMap; uniform sampler2D nightMap; uniform sampler2D specMap; | |
| uniform vec3 cameraPos; uniform vec3 lightDir; uniform float time; uniform float glowThreshold; uniform float glowIntensity; | |
| varying vec3 vWorldPos; varying vec3 vWorldNormal; varying vec2 vUv; | |
| void main(){ | |
| vec3 N = normalize(vWorldNormal); | |
| vec3 L = normalize(lightDir); | |
| vec3 V = normalize(cameraPos - vWorldPos); | |
| float ndotl = max(dot(N,L), 0.0); | |
| float mixF = smoothstep(0.02, 0.2, ndotl); | |
| vec3 day = texture2D(dayMap, vUv).rgb; | |
| vec3 night = texture2D(nightMap, vUv).rgb; | |
| vec3 base = mix(night, day, mixF); | |
| float spec = 0.0; | |
| if (ndotl > 0.0) { | |
| vec3 R = reflect(-L, N); | |
| float s = pow(max(dot(V, R), 0.0), 64.0); | |
| float sm = specMap == sampler2D(0) ? 1.0 : texture2D(specMap, vUv).r; | |
| spec = s * sm * 0.7; | |
| } | |
| // Night city glow bloom prepass: boost bright pixels on night side | |
| float nightMix = 1.0 - mixF; | |
| float nightLuma = dot(night, vec3(0.299,0.587,0.114)); | |
| float glow = smoothstep(glowThreshold, 1.0, nightLuma) * nightMix * glowIntensity; | |
| vec3 color = base + vec3(spec) + vec3(glow); | |
| gl_FragColor = vec4(color, 1.0); | |
| } | |
| ` | |
| }; | |
| const mat = new THREE.ShaderMaterial(earthShader); | |
| sphere = new THREE.Mesh(new THREE.SphereGeometry(earthRadius, 64, 64), mat); | |
| sphere.userData.isEarthShader = true; | |
| } else { | |
| const earthMat = new THREE.MeshStandardMaterial({ map: dayTex || null, roughness: 0.9, metalness: 0.0, bumpMap: bumpTex || null, bumpScale: 0.5, envMapIntensity: 0.5 }); | |
| if (specTex) earthMat.metalnessMap = specTex; | |
| sphere = new THREE.Mesh(new THREE.SphereGeometry(earthRadius, 64, 64), earthMat); | |
| } | |
| sphere.userData = { isGenerated: true, isNASABody: true, mass: earthRadius * 100, name: 'Earth', orbitRadius, orbitAngle: angle }; | |
| sphere.position.set(Math.cos(angle)*orbitRadius, (this.prng()-0.5)*200, Math.sin(angle)*orbitRadius); | |
| earthGroup.add(sphere); | |
| // Transparent atmosphere shell with slight blue tint | |
| const atmosphere = new THREE.Mesh(new THREE.SphereGeometry(earthRadius * 1.02, 32, 32), new THREE.MeshBasicMaterial({ color: 0x4da6ff, transparent: true, opacity: 0.15 })); | |
| atmosphere.userData = { isGenerated: true }; | |
| earthGroup.add(atmosphere); | |
| // Simple cloud layer with slow rotation for depth | |
| const clouds = new THREE.Mesh(new THREE.SphereGeometry(earthRadius * 1.015, 64, 64), new THREE.MeshStandardMaterial({ map: cloudsTex || null, transparent: true, opacity: 0.45 })); | |
| clouds.userData = { isGenerated: true, isCloudLayer: true }; | |
| earthGroup.add(clouds); | |
| // Add marker/label | |
| this.addEarthMarker(earthGroup); | |
| return earthGroup; | |
| } | |
| addPlanetLabel(node, text, color = '#9cc9ff') { | |
| const labelCanvas = document.createElement('canvas'); | |
| labelCanvas.width = 256; labelCanvas.height = 64; | |
| const ctx = labelCanvas.getContext('2d'); | |
| ctx.fillStyle = 'rgba(0,0,0,0)'; ctx.fillRect(0,0,256,64); | |
| ctx.font = '26px Inter, sans-serif'; | |
| ctx.fillStyle = color; | |
| ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; | |
| ctx.fillText(text.toUpperCase(), 128, 32); | |
| const tex = new THREE.CanvasTexture(labelCanvas); | |
| const plane = new THREE.Mesh(new THREE.PlaneGeometry(60, 15), new THREE.MeshBasicMaterial({ map: tex, transparent: true })); | |
| plane.position.set(0, 40, 0); | |
| plane.userData = { isGenerated: true, isBillboard: true }; | |
| node.add(plane); | |
| } | |
| createNASAPlanet(name, orbitRadius, baseRadius, angle, colorHex) { | |
| const group = new THREE.Group(); | |
| let sphereMat = new THREE.MeshStandardMaterial({ color: colorHex, roughness: 1.0, metalness: 0.0 }); | |
| let sphereGeom = new THREE.SphereGeometry(baseRadius, 32, 32); | |
| // Procedural textures by planet | |
| if (name === 'Jupiter') { | |
| const size = 1024; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); | |
| // Stripes | |
| for (let y = 0; y < size; y++) { | |
| const t = y / size; | |
| const hue = 30 + Math.sin(t * 20) * 8; | |
| const light = 0.7 + Math.sin(t * 40) * 0.1; | |
| ctx.fillStyle = `hsl(${hue}, 50%, ${light*100}%)`; | |
| ctx.fillRect(0, y, size, 1); | |
| } | |
| // Great Red Spot | |
| ctx.fillStyle = 'rgba(200,80,50,0.8)'; | |
| ctx.beginPath(); ctx.ellipse(size*0.7, size*0.55, 90, 60, 0, 0, Math.PI*2); ctx.fill(); | |
| const tex = new THREE.CanvasTexture(canvas); | |
| sphereMat = new THREE.MeshStandardMaterial({ map: tex, roughness: 1.0 }); | |
| } else if (name === 'Mars') { | |
| const size = 1024; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); | |
| ctx.fillStyle = '#b55239'; ctx.fillRect(0,0,size,size); | |
| ctx.fillStyle = '#8d3f2a'; | |
| for (let i=0;i<8000;i++){ ctx.globalAlpha = 0.8; const r= Math.random()*3+1; ctx.beginPath(); ctx.arc(Math.random()*size, Math.random()*size, r, 0, Math.PI*2); ctx.fill(); } | |
| // Polar caps | |
| ctx.globalAlpha = 0.9; ctx.fillStyle = '#ffffff'; | |
| ctx.beginPath(); ctx.arc(size*0.5, size*0.05, 40, 0, Math.PI*2); ctx.fill(); | |
| ctx.beginPath(); ctx.arc(size*0.5, size*0.95, 60, 0, Math.PI*2); ctx.fill(); | |
| const tex = new THREE.CanvasTexture(canvas); | |
| sphereMat = new THREE.MeshStandardMaterial({ map: tex, roughness: 1.0 }); | |
| } else if (name === 'Venus') { | |
| const size = 1024; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); | |
| ctx.fillStyle = '#d8c08a'; ctx.fillRect(0,0,size,size); | |
| ctx.globalAlpha = 0.3; ctx.fillStyle = '#fff6cc'; | |
| for (let i=0;i<6000;i++){ const r= Math.random()*4+2; ctx.beginPath(); ctx.arc(Math.random()*size, Math.random()*size, r, 0, Math.PI*2); ctx.fill(); } | |
| const tex = new THREE.CanvasTexture(canvas); | |
| sphereMat = new THREE.MeshStandardMaterial({ map: tex, roughness: 0.9 }); | |
| } | |
| const sphere = new THREE.Mesh(sphereGeom, sphereMat); | |
| sphere.userData = { isGenerated: true, isNASABody: true, mass: baseRadius * 100, name, orbitRadius, orbitAngle: angle }; | |
| group.add(sphere); | |
| // Rings for Saturn | |
| if (name === 'Saturn') { | |
| const inner = baseRadius * 1.8; const outer = baseRadius * 3.2; | |
| const ring = new THREE.Mesh(new THREE.RingGeometry(inner, outer, 128), new THREE.MeshBasicMaterial({ color: 0xd7c7a1, side: THREE.DoubleSide, transparent: true, opacity: 0.7 })); | |
| ring.rotation.x = Math.PI/2; ring.userData = { isRing: true, baseTilt: ring.rotation.x, isSaturnRing: true }; | |
| group.add(ring); | |
| } | |
| // Thicker atmosphere for Venus | |
| if (name === 'Venus') { | |
| const atm = new THREE.Mesh(new THREE.SphereGeometry(baseRadius * 1.05, 32, 32), new THREE.MeshBasicMaterial({ color: 0xffe38a, transparent: true, opacity: 0.25 })); | |
| group.add(atm); | |
| } | |
| // Neptune/Uranus deep blues/teals handled by base color | |
| // Position and label | |
| group.position.set(Math.cos(angle)*orbitRadius, (this.prng()-0.5)*200, Math.sin(angle)*orbitRadius); | |
| this.addPlanetLabel(group, name, '#9cc9ff'); | |
| return group; | |
| } | |
| // --- Animate & Update --- | |
| animate() { | |
| requestAnimationFrame(this.animate); | |
| const delta = this.clock.getDelta(); | |
| const elapsedTime = this.clock.getElapsedTime(); | |
| if (this.spaceship) { | |
| this.spaceship.update(delta, this.pointerLockControls); | |
| } else if (this.pointerLockControls.isLocked) { | |
| this.updatePlayerMovement(delta); | |
| } | |
| if (this.activeControls && this.activeControls.isOrbitControls) this.activeControls.update(); | |
| this.updateInterSystemPhysics(delta); | |
| this.planets.forEach(planet => { | |
| // BUG FIX: Removed old kinematic orbit logic. Physics now controls position. | |
| planet.rotation.y += delta * 0.1; | |
| planet.traverse(child => { | |
| if (child.userData.isAtmosphere) { | |
| child.material.uniforms.time.value = elapsedTime; | |
| // Update scattering uniforms | |
| if (child.material.uniforms.cameraPos) child.material.uniforms.cameraPos.value.copy(this.camera.position); | |
| if (this.activeStar && child.material.uniforms.lightDir) { | |
| const dir = this.activeStar.position.clone().sub(planet.position).normalize(); | |
| child.material.uniforms.lightDir.value.copy(dir); | |
| if (child.material.uniforms.intensityBoost) { | |
| const hsl = this.activeStar.material.color.getHSL({}); | |
| const boost = 0.8 + Math.min(1.5, hsl.s * 1.2 + hsl.l); | |
| child.material.uniforms.intensityBoost.value = boost; | |
| } | |
| } | |
| } | |
| }); | |
| }); | |
| this.scene.traverse(obj => { | |
| if (obj && obj.userData && obj.userData.isEcho) obj.rotation.y += delta * 0.5; | |
| if (obj && obj.userData && obj.userData.isBlackHole && obj.userData.disk && obj.userData.disk.material && obj.userData.disk.material.uniforms) obj.userData.disk.material.uniforms.time.value = elapsedTime; | |
| if (obj && obj.userData && obj.userData.isNebula && obj.material && obj.material.uniforms && obj.material.uniforms.pulse) obj.material.uniforms.pulse.value = this.cosmicPulse; | |
| if (obj && obj.userData && obj.userData.isStarCorona && obj.material && obj.material.uniforms && obj.material.uniforms.time) obj.material.uniforms.time.value = elapsedTime; | |
| if (obj && obj.userData && obj.userData.isStarPhotosphere && obj.material && obj.material.uniforms && obj.material.uniforms.time) obj.material.uniforms.time.value = elapsedTime; | |
| if (obj && obj.userData && obj.userData.isArtwork) this.updateArtwork(obj, delta); | |
| if (obj && obj.userData && obj.userData.isFlora) this.updateFlora(obj, delta); | |
| if (obj && obj.userData && obj.userData.isAutonomous) this.updateAutonomousObject(obj, delta); | |
| if (obj && obj.userData && obj.userData.isBillboard) { | |
| // Face the camera (billboarding for labels) | |
| obj.lookAt(this.camera.position); | |
| } | |
| if (obj && obj.userData && obj.userData.isRing && this.audioSignal) { | |
| const wobble = (this.audioSignal.bass * 0.1) * Math.sin(this.clock.elapsedTime * 0.8); | |
| obj.rotation.x = obj.userData.baseTilt + wobble; | |
| } | |
| if (obj && obj.userData && obj.userData.isComet && obj.userData.tail) this.updateCometTail(obj, delta); | |
| if (obj && obj.userData && obj.userData.isMonolith) this.updateMonolithGlint(obj, delta); | |
| if (obj && obj.userData && obj.userData.isGenesisLattice) this.updateGenesisLattice(obj, delta); | |
| }); | |
| // Safely process deferred removals after traversal to avoid mutating scene graph mid-iteration | |
| if (this.pendingRemovals && this.pendingRemovals.length) { | |
| const removals = this.pendingRemovals.splice(0, this.pendingRemovals.length); | |
| removals.forEach(dead => { | |
| try { | |
| if (!dead) return; | |
| if (dead.parent) dead.parent.remove(dead); | |
| if (dead.geometry) dead.geometry.dispose(); | |
| if (dead.material) { | |
| if (Array.isArray(dead.material)) dead.material.forEach(m => m && m.dispose && m.dispose()); | |
| else if (dead.material.dispose) dead.material.dispose(); | |
| } | |
| } catch (e) { /* noop */ } | |
| }); | |
| } | |
| this.specialObjects.forEach(obj => { if (obj.update) obj.update(delta); }); | |
| if (this.godrayPass.enabled && this.activeStar && !this.cosmicWebView.active) { | |
| let screenPos = this.activeStar.position.clone().project(this.camera); | |
| this.godrayPass.uniforms.lightPosition.value.set((screenPos.x + 1) / 2, (screenPos.y + 1) / 2); | |
| } | |
| this.updateCosmicGrowth(delta); | |
| this.atmosphericEntryEffect.update(delta, this.camera, this.planets); | |
| this.handleCosmicWebView(); | |
| this.updateScanner(); | |
| this.updateLensing(); | |
| this.updateCinematicDOF(); | |
| // Rotate cloud layers slightly for living planet depth | |
| if (this.earthRef && this.earthRef.children) { | |
| const cloud = this.earthRef.children.find(c => c.userData && c.userData.isCloudLayer); | |
| if (cloud) cloud.rotation.y += delta * 0.02; | |
| } | |
| // Update Earth shader uniforms if present | |
| if (this.earthRef) { | |
| const earthSphere = this.earthRef.children.find(c => c.userData && c.userData.isEarthShader); | |
| if (earthSphere && earthSphere.material && earthSphere.material.uniforms) { | |
| earthSphere.material.uniforms.cameraPos.value.copy(this.camera.position); | |
| const lightDir = this.activeStar ? this.activeStar.position.clone().sub(earthSphere.getWorldPosition(new THREE.Vector3())).normalize() : new THREE.Vector3(1,0,0); | |
| earthSphere.material.uniforms.lightDir.value.copy(lightDir); | |
| earthSphere.material.uniforms.time.value = this.clock.getElapsedTime(); | |
| } | |
| } | |
| // Periodic telemetry of live frame tokens (frequency->light, genesis signal, angular size) | |
| this.telemetryTimer += delta; | |
| if (this.telemetryTimer > 0.5) { | |
| this.telemetryTimer = 0; | |
| const genesis = this.computeGenesisSignal(); | |
| // Estimate nearest body's angular size | |
| let nearestAng = 0; let nearestName = 'none'; | |
| const candidates = []; | |
| if (this.activeStar) candidates.push({ name: 'star', obj: this.activeStar }); | |
| this.planets.forEach((p,i)=>candidates.push({ name: `planet-${i}`, obj: p })); | |
| this.nasaBodies.forEach((b,i)=>candidates.push({ name: b.userData && b.userData.name || `nasa-${i}`, obj: b })); | |
| for (const c of candidates) { | |
| const obj = c.obj; | |
| const pos = obj.position.clone(); | |
| const dist = this.camera.position.distanceTo(pos); | |
| let r = 0; | |
| if (!obj.geometry && obj.children && obj.children.length) { | |
| const s = obj.children.find(ch => ch.isMesh && ch.geometry && ch.geometry.parameters && ch.geometry.parameters.radius); | |
| if (s) r = s.geometry.parameters.radius; | |
| } else if (obj.geometry && obj.geometry.parameters && obj.geometry.parameters.radius) r = obj.geometry.parameters.radius; | |
| if (r > 0) { | |
| const ang = 2 * Math.atan2(r, Math.max(1, dist)); | |
| if (ang > nearestAng) { nearestAng = ang; nearestName = c.name; } | |
| } | |
| } | |
| const bloom = this.bloomPass ? this.bloomPass.strength : 0; | |
| const god = this.godrayPass ? this.godrayPass.uniforms.exposure.value : 0; | |
| const nebulaD = (this.nebula && this.nebula.material && this.nebula.material.uniforms) ? this.nebula.material.uniforms.density.value : 0; | |
| this.eventBus.publish('telemetry:append', { type: 'frame', tokens: { genesis, nearestName, nearestAngularDeg: THREE.MathUtils.radToDeg(nearestAng), exposure: this.renderer.toneMappingExposure, bloom, godrays: god, nebulaDensity: nebulaD }, t: Date.now() }); | |
| } | |
| this.updateCinematicDepthCue(); | |
| this.updateVisualsFromSimulation(delta); | |
| this.updateSSRProximity(); | |
| this.eventBus.publish('pge:animate', delta); | |
| this.composer.render(delta); | |
| } | |
| updateGenesisLattice(lattice, delta) { | |
| // Subtle pulsing based on combined genesis signal | |
| const signal = this.computeGenesisSignal(); | |
| lattice.material.opacity = 0.08 + 0.12 * signal; | |
| } | |
| computeGenesisSignal() { | |
| // Unified signal derived from star luminance, audio energy, motion energy, and black hole presence | |
| const numPlanets = this.planets.length; | |
| const starLum = this.activeStar ? Math.max(0.5, Math.min(2.0, this.activeStar.material.color.getHSL({}).l * 2.0 + (this.activeStar.material.color.getHSL({}).s))) : 1.0; | |
| const motionEnergy = this.planets.reduce((sum, p) => sum + (p.userData.velocity ? p.userData.velocity.length() : 0), 0) / Math.max(1, numPlanets); | |
| const blackHolePresence = this.blackHoles.length > 0 ? 1.0 : 0.0; | |
| const audioE = this.audioSignal ? this.audioSignal.energy : 0.0; | |
| const signal = Math.max(0.0, Math.min(1.0, 0.25 * (starLum - 0.5) + 0.25 * Math.min(1.0, motionEnergy * 0.002) + 0.3 * audioE + 0.2 * blackHolePresence)); | |
| return signal; | |
| } | |
| updateCinematicDepthCue() { | |
| // Adjust FOV slightly based on angular size of the nearest large body to enhance "enclosing" feel | |
| let nearest = null; let nearestAngular = 0; | |
| const candidates = []; | |
| if (this.activeStar) candidates.push(this.activeStar); | |
| this.planets.forEach(p => candidates.push(p)); | |
| this.nasaBodies.forEach(b => candidates.push(b)); | |
| for (const obj of candidates) { | |
| const worldPos = obj.position.clone(); | |
| const dist = this.camera.position.distanceTo(worldPos); | |
| if (!obj.geometry && obj.children && obj.children.length) { | |
| const sphere = obj.children.find(c => c.isMesh && c.geometry && c.geometry.parameters && c.geometry.parameters.radius); | |
| if (!sphere) continue; | |
| const radius = sphere.geometry.parameters.radius; | |
| const angular = 2 * Math.atan2(radius, Math.max(1, dist)); | |
| if (angular > nearestAngular) { nearestAngular = angular; nearest = obj; } | |
| } else if (obj.geometry && obj.geometry.parameters && obj.geometry.parameters.radius) { | |
| const radius = obj.geometry.parameters.radius; | |
| const angular = 2 * Math.atan2(radius, Math.max(1, dist)); | |
| if (angular > nearestAngular) { nearestAngular = angular; nearest = obj; } | |
| } | |
| } | |
| // Map angular size to FOV bias (narrow slightly as object fills view; widen otherwise) | |
| let targetFov = THREE.MathUtils.clamp(this.baseFov - THREE.MathUtils.radToDeg(nearestAngular) * 0.15, 45, this.baseFov); | |
| // In enhanced FP with quantum scale, bias FOV narrower to heighten depth | |
| if (this.spaceship && this.activeControls === this.pointerLockControls && !this.spaceship.cockpitView) { | |
| targetFov -= 5; | |
| } | |
| this.camera.fov += (targetFov - this.camera.fov) * 0.05; | |
| this.camera.updateProjectionMatrix(); | |
| } | |
| updateVisualsFromSimulation(delta) { | |
| // Derive non-invasive visual parameters from current simulation state | |
| const numPlanets = this.planets.length; | |
| const starLum = this.activeStar ? Math.max(0.5, Math.min(2.0, this.activeStar.material.color.getHSL({}).l * 2.0 + (this.activeStar.material.color.getHSL({}).s))) : 1.0; | |
| const motionEnergy = this.planets.reduce((sum, p) => sum + (p.userData.velocity ? p.userData.velocity.length() : 0), 0) / Math.max(1, numPlanets); | |
| const blackHolePresence = this.blackHoles.length > 0 ? 1.0 : 0.0; | |
| const quantumLight = this.quantumLux ? (this.quantumLux / 1200) : 0; // 0..1 | |
| const genesisSignal = this.computeGenesisSignal(); | |
| // Approximate average luminance from scene signals and smooth it | |
| const luminanceSignal = 0.8 * starLum + 0.2 * (this.localStarfield ? this.localStarfield.material.opacity : 1.0); | |
| const targetLuma = Math.max(0.2, Math.min(2.0, luminanceSignal + blackHolePresence * 0.05)); | |
| this.avgLuminance += (targetLuma - this.avgLuminance) * Math.min(1.0, delta * 2.0); | |
| // Exposure gently reacts to smoothed luminance and motion | |
| const targetExposure = 1.0 + (this.avgLuminance - 1.0) * 0.25 + Math.min(0.2, motionEnergy * 0.002) + genesisSignal * 0.05 + quantumLight * 0.05; | |
| this.renderer.toneMappingExposure += (targetExposure - this.renderer.toneMappingExposure) * 0.05; | |
| // Bloom strength tracks brightness and black hole presence (per-star glare), mid frequencies add vibrance, quantum light enhances presence | |
| if (this.bloomPass && this.bloomPass.enabled) { | |
| const vibrance = this.audioSignal ? this.audioSignal.mid * 0.4 : 0; | |
| const baseStrength = 1.0 + (starLum - 1.0) * 1.2 + vibrance + genesisSignal * 0.2 + quantumLight * 0.25; | |
| const holeBoost = 0.3 * blackHolePresence; | |
| this.bloomPass.strength += ((baseStrength + holeBoost) - this.bloomPass.strength) * 0.05; | |
| this.bloomPass.radius += ((0.35 + 0.25 * blackHolePresence + quantumLight * 0.1) - this.bloomPass.radius) * 0.05; | |
| } | |
| // Film grain intensity scales with motion energy and bass | |
| if (this.filmPass && this.filmPass.enabled) { | |
| const bass = this.audioSignal ? this.audioSignal.bass : 0; | |
| const targetNoise = Math.min(0.45, 0.12 + motionEnergy * 0.003 + bass * 0.2 + genesisSignal * 0.1); | |
| this.filmPass.uniforms['nIntensity'].value += (targetNoise - this.filmPass.uniforms['nIntensity'].value) * 0.05; | |
| const targetScan = 0.02 + Math.min(0.03, motionEnergy * 0.0005); | |
| this.filmPass.uniforms['sIntensity'].value += (targetScan - this.filmPass.uniforms['sIntensity'].value) * 0.05; | |
| } | |
| // Chromatic aberration increases subtly near a black hole | |
| if (this.rgbShiftPass) { | |
| const distFactor = this.blackHoles[0] ? Math.max(0, 1 - this.camera.position.distanceTo(this.blackHoles[0].position) / 4000) : 0; | |
| const current = this.rgbShiftPass.uniforms['amount'].value; | |
| const treble = this.audioSignal ? this.audioSignal.treble : 0; | |
| const targetAmount = 0.001 + 0.004 * distFactor + treble * 0.002 + genesisSignal * 0.001; | |
| this.rgbShiftPass.uniforms['amount'].value = current + (targetAmount - current) * 0.05; | |
| } | |
| // Godrays exposure reacts to genesis signal and quantum light | |
| if (this.godrayPass && this.godrayPass.enabled && this.godrayPass.uniforms && this.godrayPass.uniforms.exposure) { | |
| const currentExp = this.godrayPass.uniforms.exposure.value; | |
| const targetExp = 0.18 + genesisSignal * 0.06 + quantumLight * 0.12; | |
| this.godrayPass.uniforms.exposure.value = currentExp + (targetExp - currentExp) * 0.05; | |
| } | |
| // Decay any starspot shock back to baseline; bass energy adds slight spot depth | |
| if (this.activeStar && this.activeStar.userData.photosphere && this.activeStar.userData.photosphere.material.uniforms.spotStrength) { | |
| const u = this.activeStar.userData.photosphere.material.uniforms.spotStrength; | |
| const bass = this.audioSignal ? this.audioSignal.bass : 0; | |
| const target = 0.05 * bass; | |
| u.value += (target - u.value) * Math.min(1.0, delta * 0.2); | |
| } | |
| // Drive nebula dynamics from audio energy: density pulsing and swirl speed | |
| if (this.nebula && this.nebula.material && this.nebula.material.uniforms) { | |
| const energy = this.audioSignal ? this.audioSignal.energy : 0; | |
| const base = this.nebula.userData.baseDensity || this.nebula.material.uniforms.density.value; | |
| const targetDensity = Math.min(1.4, base + energy * 0.2 + genesisSignal * 0.1 + quantumLight * 0.1); | |
| this.nebula.material.uniforms.density.value += (targetDensity - this.nebula.material.uniforms.density.value) * 0.05; | |
| // Swirl speed via time scaling is implicit; extra pulse already affects color amplitude | |
| this.nebula.material.uniforms.pulse.value = energy; | |
| // Adjust turbulence scale with mid band for finer detail when energetic | |
| const baseScale = this.nebula.userData.baseNoiseScale || this.nebula.material.uniforms.noiseScale.value; | |
| const mid = this.audioSignal ? this.audioSignal.mid : 0; | |
| const targetScale = baseScale + mid * 0.8 + genesisSignal * 0.3 + quantumLight * 0.2; | |
| this.nebula.material.uniforms.noiseScale.value += (targetScale - this.nebula.material.uniforms.noiseScale.value) * 0.05; | |
| } | |
| // Atmosphere aurora intensifies with treble bursts | |
| if ((this.audioSignal && this.audioSignal.treble > 0.2) || this.quantumLux > 200) { | |
| this.planets.forEach(planet => { | |
| planet.traverse(child => { | |
| if (child.userData && child.userData.isAtmosphere && child.material && child.material.uniforms && child.material.uniforms.auroraIntensity) { | |
| const u = child.material.uniforms.auroraIntensity; | |
| const qBoost = Math.min(0.5, (this.quantumLux/1200) * 0.3); | |
| const aBoost = this.audioSignal ? this.audioSignal.treble * 0.05 : 0; | |
| u.value = Math.min(1.0, u.value + aBoost + qBoost); | |
| } | |
| }); | |
| }); | |
| } | |
| } | |
| updateInterSystemPhysics(delta) { | |
| const allGravitationalObjects = [this.activeStar, ...this.planets, ...this.blackHoles]; | |
| for (let i = 0; i < this.planets.length; i++) { | |
| const planet = this.planets[i]; | |
| let totalForce = new THREE.Vector3(); | |
| allGravitationalObjects.forEach(obj => { | |
| if (planet !== obj && obj) { | |
| const distVec = obj.position.clone().sub(planet.position); | |
| const distSq = distVec.lengthSq(); | |
| if (distSq > 1) { // Avoid division by zero | |
| const forceMag = (this.G * obj.userData.mass * planet.userData.mass) / distSq; | |
| const force = distVec.normalize().multiplyScalar(forceMag); | |
| totalForce.add(force); | |
| } | |
| } | |
| }); | |
| const acceleration = totalForce.divideScalar(planet.userData.mass); | |
| planet.userData.velocity.add(acceleration.multiplyScalar(delta)); | |
| } | |
| this.planets.forEach(planet => { | |
| planet.position.add(planet.userData.velocity.clone().multiplyScalar(delta)); | |
| }); | |
| // Galaxy-scale living growth signals | |
| if (this.nebula && this.nebula.material && this.nebula.material.uniforms) { | |
| const g = this.computeGenesisSignal(); | |
| const energy = this.audioSignal ? this.audioSignal.energy : 0; | |
| const base = this.nebula.userData ? (this.nebula.userData.baseDensity || 0.3) : 0.3; | |
| const target = Math.min(1.5, base + g * 0.1 + energy * 0.25); | |
| this.nebula.material.uniforms.density.value += (target - this.nebula.material.uniforms.density.value) * Math.min(1.0, delta * 0.5); | |
| } | |
| if (this.activeStar && this.activeStar.userData && this.activeStar.userData.corona && this.activeStar.userData.corona.material && this.activeStar.userData.corona.material.uniforms) { | |
| const u = this.activeStar.userData.corona.material.uniforms; | |
| const g = this.computeGenesisSignal(); | |
| const mid = this.audioSignal ? this.audioSignal.mid : 0; | |
| if (u.intensity) u.intensity.value += ((0.8 + g * 0.4 + mid * 0.3) - u.intensity.value) * Math.min(1.0, delta * 0.5); | |
| } | |
| // Update NASA reference bodies simple circular motion for visibility | |
| if (this.nasaBodies && this.nasaBodies.length) { | |
| for (const body of this.nasaBodies) { | |
| // Groups contain a child sphere with orbital data | |
| let ud = null; | |
| if (body.children && body.children.length) { | |
| const sphere = body.children.find(c => c.isMesh && c.userData && c.userData.orbitRadius !== undefined); | |
| if (sphere) ud = sphere.userData; | |
| } else if (body.userData && body.userData.orbitRadius !== undefined) { | |
| ud = body.userData; | |
| } | |
| if (!ud) continue; | |
| ud.orbitAngle += delta * 0.005; // very slow orbit for context | |
| const r = ud.orbitRadius; | |
| const y = body.position.y; | |
| const x = Math.cos(ud.orbitAngle) * r; | |
| const z = Math.sin(ud.orbitAngle) * r; | |
| body.position.set(x, y, z); | |
| } | |
| } | |
| // Subtle orbital drift for asteroid belts around star based on gravity pulse | |
| const pulse = 0.2 + this.computeGenesisSignal() * 0.8; | |
| for (const belt of this.asteroidBelts) { | |
| const pos = belt.geometry.attributes.position; | |
| const arr = pos.array; | |
| for (let i = 0; i < arr.length; i += 3) { | |
| const x = arr[i], y = arr[i+1], z = arr[i+2]; | |
| const r = Math.sqrt(x*x + z*z); | |
| if (r > 0.0001) { | |
| const angVel = (1.0 / Math.sqrt(r)) * pulse * delta * 0.8; | |
| const angle = Math.atan2(z, x) + angVel; | |
| arr[i] = Math.cos(angle) * r; | |
| arr[i+2] = Math.sin(angle) * r; | |
| } | |
| arr[i+1] += Math.sin((x+z) * 0.001 + this.clock.elapsedTime * 0.2) * 0.02; // tiny vertical flutter | |
| } | |
| pos.needsUpdate = true; | |
| // Fade with distance in cosmic web | |
| if (this.cosmicWebView.active) belt.material.opacity = belt.userData.baseOpacity * 0.3; else belt.material.opacity = belt.userData.baseOpacity; | |
| } | |
| } | |
| updateArtwork(artwork, delta) { | |
| artwork.userData.life -= delta; | |
| if (artwork.userData.life <= 0) { | |
| this.scene.remove(artwork); | |
| artwork.geometry.dispose(); | |
| artwork.material.dispose(); | |
| return; | |
| } | |
| const positions = artwork.geometry.attributes.position.array; | |
| artwork.userData.particles.forEach((p, i) => { | |
| if (this.planets.length > 0) { | |
| const nearestPlanet = this.planets.reduce((prev, curr) => { | |
| return (prev.position.distanceTo(p.pos) < curr.position.distanceTo(p.pos)) ? prev : curr; | |
| }); | |
| const gravity = nearestPlanet.position.clone().sub(p.pos).normalize().multiplyScalar(5 * delta); | |
| p.vel.add(gravity); | |
| } | |
| p.vel.multiplyScalar(0.99); | |
| p.pos.add(p.vel); | |
| positions[i*3] = p.pos.x; | |
| positions[i*3+1] = p.pos.y; | |
| positions[i*3+2] = p.pos.z; | |
| }); | |
| artwork.geometry.attributes.position.needsUpdate = true; | |
| artwork.material.opacity = Math.min(1, artwork.userData.life / 10); | |
| } | |
| updateAutonomousObject(obj, delta) { | |
| obj.userData.life -= delta; | |
| if (obj.userData.life <= 0) { | |
| // Defer removal to avoid mutating the scene during traversal | |
| this.pendingRemovals.push(obj); | |
| return; | |
| } | |
| if (obj.userData.velocity) { | |
| obj.position.add(obj.userData.velocity.clone().multiplyScalar(delta)); | |
| } | |
| if (obj.userData.velocityArray) { | |
| // Points-based object with per-particle velocities | |
| const positions = obj.geometry.attributes.position.array; | |
| const v = obj.userData.velocityArray; | |
| for (let i = 0; i < positions.length; i += 3) { | |
| positions[i] += v[i] * delta; | |
| positions[i+1] += v[i+1] * delta; | |
| positions[i+2] += v[i+2] * delta; | |
| // Dampen | |
| v[i] *= (1 - 0.1 * delta); | |
| v[i+1] *= (1 - 0.1 * delta); | |
| v[i+2] *= (1 - 0.1 * delta); | |
| } | |
| obj.geometry.attributes.position.needsUpdate = true; | |
| } | |
| if (obj.material) { | |
| obj.material.opacity = Math.min(1, obj.userData.life / 10); | |
| } | |
| } | |
| updateCometTail(comet, delta) { | |
| const tail = comet.userData.tail; | |
| if (!tail) return; | |
| const positions = tail.geometry.attributes.position.array; | |
| // Shift segments back | |
| for (let i = positions.length - 1; i >= 3; i--) { | |
| positions[i] = positions[i - 3]; | |
| } | |
| // Head segment at comet origin | |
| positions[0] = 0; positions[1] = 0; positions[2] = 0; | |
| // Fade opacity slightly over life | |
| if (tail.material.opacity > 0.1) tail.material.opacity *= (1 - 0.3 * delta); | |
| tail.geometry.attributes.position.needsUpdate = true; | |
| } | |
| updateFlora(flora, delta) { | |
| // Growth driven by genesis influence and ambient pulse | |
| const growthRate = (this.lastInfluence && this.lastInfluence.hasPlant ? 0.05 : 0.02) + this.cosmicPulse * 0.02; | |
| flora.userData.growth = Math.min(1.0, flora.userData.growth + growthRate * delta); | |
| // Sway animation | |
| flora.userData.swayPhase += delta * (0.5 + this.cosmicPulse); | |
| const sway = Math.sin(flora.userData.swayPhase) * 0.05; | |
| flora.rotation.z = sway; | |
| // Scale up as it grows | |
| const s = 0.5 + flora.userData.growth * 0.5; | |
| flora.scale.set(s, s, s); | |
| // Fruit ripening: shift from green to warm as growth nears 1 | |
| if (flora.userData.fruits) { | |
| const fruits = flora.userData.fruits; | |
| const colors = fruits.geometry.attributes.color.array; | |
| const ripe = Math.min(1.0, fruits.userData.ripe + growthRate * delta * 0.5); | |
| fruits.userData.ripe = ripe; | |
| for (let i = 0; i < colors.length; i += 3) { | |
| const c = new THREE.Color().setHSL(0.33 * (1.0 - ripe) + 0.02 * ripe, 0.8, 0.5 + 0.3 * ripe); | |
| colors[i] = c.r; colors[i+1] = c.g; colors[i+2] = c.b; | |
| } | |
| fruits.geometry.attributes.color.needsUpdate = true; | |
| // Occasionally disperse seeds when fully ripe | |
| if (ripe > 0.99 && Math.random() < 0.01) { | |
| this.disperseSeeds(fruits); | |
| fruits.userData.ripe = 0.7; // reset slightly | |
| } | |
| } | |
| } | |
| disperseSeeds(fruits) { | |
| const positions = fruits.geometry.attributes.position.array; | |
| const seedCount = Math.min(30, positions.length / 3); | |
| const seedPositions = new Float32Array(seedCount * 3); | |
| const seedColors = new Float32Array(seedCount * 3); | |
| for (let i = 0; i < seedCount; i++) { | |
| const idx = (Math.floor(Math.random() * (positions.length / 3))) * 3; | |
| seedPositions[i*3] = fruits.getWorldPosition(new THREE.Vector3()).x + (Math.random()-0.5) * 5; | |
| seedPositions[i*3+1] = fruits.getWorldPosition(new THREE.Vector3()).y + (Math.random()) * 5; | |
| seedPositions[i*3+2] = fruits.getWorldPosition(new THREE.Vector3()).z + (Math.random()-0.5) * 5; | |
| const c = new THREE.Color().setHSL(0.12, 0.9, 0.6); | |
| seedColors[i*3] = c.r; seedColors[i*3+1] = c.g; seedColors[i*3+2] = c.b; | |
| } | |
| const geom = new THREE.BufferGeometry(); | |
| geom.setAttribute('position', new THREE.BufferAttribute(seedPositions, 3)); | |
| geom.setAttribute('color', new THREE.BufferAttribute(seedColors, 3)); | |
| const mat = new THREE.PointsMaterial({ size: 1.5, vertexColors: true, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false }); | |
| const seeds = new THREE.Points(geom, mat); | |
| seeds.userData = { isGenerated: true, isAutonomous: true, life: 8 }; | |
| this.scene.add(seeds); | |
| this.uiManager.log("Flora dispersed seeds into the cosmos.", "data"); | |
| } | |
| updateMonolithGlint(monolith, delta) { | |
| if (!monolith.material || !this.activeStar) return; | |
| const toCamera = this.camera.position.clone().sub(monolith.position).normalize(); | |
| const toStar = this.activeStar.position.clone().sub(monolith.position).normalize(); | |
| // Fresnel-like highlight when star, monolith normal, and view align | |
| const normal = new THREE.Vector3(0, 1, 0).applyQuaternion(monolith.quaternion); | |
| const reflect = toStar.clone().reflect(normal); | |
| const viewAlign = Math.max(0, reflect.dot(toCamera)); | |
| const glint = Math.pow(viewAlign, 32); | |
| const base = monolith.userData.baseEmissive || 0.2; | |
| monolith.material.emissiveIntensity = base + glint * 0.8; | |
| } | |
| handleCosmicWebView() { | |
| const camDist = this.camera.position.length(); | |
| const fadeStart = this.cosmicWebView.threshold - 2000; | |
| if (camDist > fadeStart) { | |
| const alpha = Math.min(1.0, (camDist - fadeStart) / 2000); | |
| this.localStarfield.material.opacity = 1.0 - alpha; | |
| if(this.hiddenEcho) this.hiddenEcho.visible = false; | |
| this.cosmicWeb.visible = true; | |
| this.cosmicWeb.material.opacity = alpha; | |
| this.cosmicWebView.active = alpha > 0.9; | |
| if (this.cosmicWebView.active) this.eventBus.publish('status:update', 'COSMIC WEB'); | |
| } else { | |
| if (this.cosmicWebView.active) this.setQuality(this.uiManager.qualitySelect.value); | |
| this.localStarfield.material.opacity = 1.0; | |
| if(this.hiddenEcho) this.hiddenEcho.visible = true; | |
| this.cosmicWeb.visible = false; | |
| this.cosmicWebView.active = false; | |
| } | |
| // Darkness detection for spaceship headlamp | |
| if (this.spaceship) { | |
| const nearStar = this.activeStar ? this.camera.position.distanceTo(this.activeStar.position) : Infinity; | |
| const nearNebula = this.planets.length > 0 ? Math.min(...this.planets.map(p => this.camera.position.distanceTo(p.position))) : Infinity; | |
| const farFromEverything = Math.min(nearStar, nearNebula) > 8000 && camDist > this.cosmicWebView.threshold; | |
| const enableLamp = farFromEverything || (this.localStarfield.material.opacity < 0.2 && !this.cosmicWeb.visible); | |
| this.spaceship.setHeadlamp(enableLamp); | |
| } | |
| // HUD hint for Earth location when NASA bodies are present | |
| if (this.earthRef) { | |
| const d = this.camera.position.distanceTo(this.earthRef.position); | |
| this.eventBus.publish('status:update', `EARTH REF: ~${d.toFixed(0)} units`); | |
| } | |
| } | |
| // --- User Controls & Settings --- | |
| updatePlayerMovement(delta) { | |
| const moveSpeed = 500.0 * delta; | |
| if (this.keys['w'] || this.keys['arrowup']) this.pointerLockControls.moveForward(moveSpeed); | |
| if (this.keys['s'] || this.keys['arrowdown']) this.pointerLockControls.moveForward(-moveSpeed); | |
| if (this.keys['a'] || this.keys['arrowleft']) this.pointerLockControls.moveRight(-moveSpeed); | |
| if (this.keys['d'] || this.keys['arrowright']) this.pointerLockControls.moveRight(moveSpeed); | |
| } | |
| setViewMode(mode) { | |
| if (this.pointerLockControls.isLocked) this.pointerLockControls.unlock(); | |
| this.orbitControls.enabled = false; | |
| if (this.spaceship) { | |
| this.spaceship.destroy(); | |
| this.spaceship = null; | |
| } | |
| this.activeControls = null; | |
| if (mode === 'orbit') { | |
| this.camera.position.set(0, 0, 250); | |
| this.orbitControls.target.set(0,0,0); | |
| this.activeControls = this.orbitControls; | |
| this.activeControls.isOrbitControls = true; | |
| this.orbitControls.enabled = true; | |
| this.applyViewProfile('orbit'); | |
| } else if (mode === 'first-person') { | |
| this.camera.position.set(0, 0, 0); | |
| this.activeControls = this.pointerLockControls; | |
| this.pointerLockControls.lock(); | |
| this.applyViewProfile('firstPerson'); | |
| } else if (mode === 'enhanced-fp' || mode === 'cockpit-view') { | |
| this.spaceship = new Spaceship(this.scene, this.camera, this.keys); | |
| this.spaceship.setView(mode); | |
| // Apply quantum scale in enhanced first-person | |
| if (mode === 'enhanced-fp') { | |
| this.spaceship.setQuantumScale(0.001); | |
| } | |
| this.activeControls = this.pointerLockControls; | |
| this.pointerLockControls.lock(); | |
| // Provide an easy Earth locate hotkey in spaceship views | |
| document.addEventListener('keydown', (e) => { if (e.key === 'e' || e.key === 'E') this.eventBus.publish('ui:locateEarth'); }, { once: true }); | |
| this.applyViewProfile(mode === 'enhanced-fp' ? 'enhanced' : 'cockpit'); | |
| } else { // cinematic | |
| this.activeControls = { isOrbitControls: false, update: () => { this.camera.position.x += Math.sin(this.clock.getElapsedTime() * 0.05) * 0.2; this.camera.lookAt(this.scene.position); } }; | |
| this.enableCinematicDOF(true); | |
| this.applyViewProfile('cinematic'); | |
| } | |
| } | |
| applyViewProfile(name) { | |
| const p = this.viewProfiles[name]; | |
| if (!p) return; | |
| if (this.bloomPass) this.bloomPass.strength = p.bloom; | |
| if (this.vignettePass) this.vignettePass.uniforms.darkness.value = p.vignette; | |
| if (this.godrayPass) this.godrayPass.enabled = !!p.godrays; | |
| if (this.filmPass) this.filmPass.enabled = !!p.film; | |
| } | |
| enableCinematicDOF(enable) { | |
| if (enable) { | |
| if (!this.bokehPass) { | |
| this.bokehPass = new BokehPass(this.scene, this.camera, { | |
| focus: 250.0, | |
| aperture: 0.0001, | |
| maxblur: 0.01, | |
| width: window.innerWidth, | |
| height: window.innerHeight | |
| }); | |
| this.composer.addPass(this.bokehPass); | |
| } | |
| this.bokehPass.enabled = true; | |
| } else if (this.bokehPass) { | |
| this.bokehPass.enabled = false; | |
| } | |
| } | |
| setQuality(quality) { | |
| const high = ['high', 'ultra', 'anime'].includes(quality); | |
| const ultra = quality === 'ultra'; | |
| // Reset all passes | |
| this.bloomPass.enabled = false; | |
| this.pixelationPass.enabled = false; | |
| this.vignettePass.enabled = false; | |
| // Do not hard-disable motion blur here; respect user toggle | |
| // this.afterimagePass.enabled = false; | |
| this.godrayPass.enabled = false; | |
| this.toonPass.enabled = false; | |
| this.filmPass.enabled = false; | |
| this.fxaaPass.enabled = false; | |
| this.rgbShiftPass.enabled = false; | |
| if (quality === 'anime') { | |
| this.toonPass.enabled = true; | |
| this.bloomPass.enabled = true; // Anime styles often have strong blooms | |
| this.fxaaPass.enabled = true; | |
| this.filmPass.enabled = true; | |
| } else if (quality === 'low') { | |
| this.pixelationPass.enabled = true; | |
| this.fxaaPass.enabled = true; // keep cheap AA | |
| } else { // Medium, High, Ultra | |
| this.bloomPass.enabled = true; | |
| this.vignettePass.enabled = high; | |
| // Only enable if user toggle is on | |
| if (this.userSettings && this.userSettings.motionBlurEnabled) this.afterimagePass.enabled = high; | |
| this.godrayPass.enabled = high; | |
| this.godrayPass.uniforms.samples.value = ultra ? 100 : 50; | |
| this.fxaaPass.enabled = true; | |
| this.filmPass.enabled = high; | |
| this.rgbShiftPass.enabled = ultra; // subtle chromatic aberration at ultra | |
| this.renderer.toneMappingExposure = ultra ? 1.1 : (high ? 1.05 : 1.0); | |
| } | |
| // Re-apply current genesis visual profile on quality change | |
| this.applyGenesisVisualProfile(); | |
| // Also re-apply current view profile to keep visuals consistent across modes | |
| if (this.activeControls) { | |
| if (this.activeControls === this.orbitControls) this.applyViewProfile('orbit'); | |
| else if (this.activeControls === this.pointerLockControls) { | |
| if (this.spaceship) this.applyViewProfile(this.spaceship.cockpitView ? 'cockpit' : 'enhanced'); | |
| else this.applyViewProfile('firstPerson'); | |
| } | |
| } | |
| this.qualitySetting = quality; | |
| } | |
| setFilter(filterName) { | |
| const m = new THREE.Matrix3(); | |
| switch (filterName) { | |
| case 'cool': m.set(1, 0, 0, 0, 1, 0, 0.1, 0, 1.2); break; | |
| case 'warm': m.set(1.2, 0.1, 0, 0, 1, 0, 0, 0, 1); break; | |
| case 'nebula': m.set(1.1, 0, 0.3, 0.2, 0.9, 0, 0.3, 0, 1.3); break; | |
| case 'monochrome': m.set(0.3, 0.6, 0.1, 0.3, 0.6, 0.1, 0.3, 0.6, 0.1); break; | |
| default: m.identity(); break; | |
| } | |
| this.filterPass.uniforms.colorMatrix.value = m; | |
| } | |
| applyGenesisVisualProfile(starColor = null) { | |
| // Map last influence and star color to a visual profile without changing physics | |
| const inf = this.lastInfluence || { hasPlant: false, hasBook: false, createBlackHole: false }; | |
| const profile = { filter: 'none', bloomBoost: 0.0, vignetteDarkness: 1.0, rgbAmount: 0.0015 }; | |
| if (inf.hasPlant) { | |
| profile.filter = 'warm'; | |
| profile.bloomBoost += 0.2; | |
| profile.vignetteDarkness = 0.9; | |
| } | |
| if (inf.hasBook) { | |
| profile.filter = profile.filter === 'warm' ? 'nebula' : 'cool'; | |
| profile.bloomBoost += 0.1; | |
| } | |
| if (inf.createBlackHole) { | |
| profile.rgbAmount = 0.003; | |
| profile.vignetteDarkness = 1.2; | |
| } | |
| // Optional: bias filter by star hue | |
| if (starColor) { | |
| const hsl = starColor.getHSL({ h: 0, s: 0, l: 0 }); | |
| if (hsl.h > 0.58 || hsl.h < 0.08) { | |
| // Red/blue extremes can push toward nebula grade | |
| if (profile.filter === 'none') profile.filter = 'nebula'; | |
| } | |
| } | |
| this.visualProfile = profile; | |
| this.setFilter(profile.filter); | |
| if (this.bloomPass && this.bloomPass.enabled) this.bloomPass.strength += profile.bloomBoost; | |
| if (this.vignettePass) this.vignettePass.uniforms.darkness.value = profile.vignetteDarkness; | |
| if (this.rgbShiftPass && this.rgbShiftPass.enabled) this.rgbShiftPass.uniforms['amount'].value = profile.rgbAmount; | |
| } | |
| spawnKnowledgeMonolithsIfNeeded() { | |
| if (!this.lastInfluence || !this.lastInfluence.hasBook) return; | |
| const num = 3 + Math.floor(this.prng() * 5); | |
| for (let i = 0; i < num; i++) { | |
| const h = 50 + this.prng() * 150; | |
| const w = 10 + this.prng() * 20; | |
| const d = 6 + this.prng() * 12; | |
| const geom = new THREE.BoxGeometry(w, h, d); | |
| const mat = new THREE.MeshStandardMaterial({ color: 0x111122, metalness: 0.85, roughness: 0.25, emissive: new THREE.Color(0x223355), emissiveIntensity: 0.2 }); | |
| const mesh = new THREE.Mesh(geom, mat); | |
| mesh.position.set((this.prng() - 0.5) * 4000, (this.prng() - 0.5) * 400, (this.prng() - 0.5) * 4000); | |
| mesh.rotation.y = this.prng() * Math.PI * 2; | |
| mesh.userData = { isGenerated: true, isMonolith: true, baseEmissive: 0.2 }; | |
| this.scene.add(mesh); | |
| } | |
| this.uiManager.log('Knowledge monoliths have been erected in response to observed artifacts.', 'data'); | |
| } | |
| onWindowResize() { | |
| this.camera.aspect = window.innerWidth / window.innerHeight; | |
| this.camera.updateProjectionMatrix(); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.composer.setSize(window.innerWidth, window.innerHeight); | |
| this.toonPass.uniforms.resolution.value.set(window.innerWidth, window.innerHeight); | |
| this.pixelationPass.uniforms.resolution.value.set(window.innerWidth, window.innerHeight); | |
| if (this.fxaaPass) this.fxaaPass.material.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight); | |
| if (this.bokehPass) { this.bokehPass.renderTargetDepth.width = window.innerWidth; this.bokehPass.renderTargetDepth.height = window.innerHeight; } | |
| if (this.ssrPass && this.ssrPass.setSize) { this.ssrPass.setSize(window.innerWidth, window.innerHeight); } | |
| } | |
| // --- Event Triggers & Scanner --- | |
| triggerLiveReaction(event) { | |
| this.uiManager.log(`Live Event Triggered: ${event.type}`, 'data'); | |
| if (event.type === 'majorSeismicEvent') { | |
| this.createBlackHole(); // Live creation of a black hole | |
| } else if (event.type === 'lightChange' || event.type === 'audioSpike') { | |
| // Nudge starspot strength visually | |
| if (this.activeStar && this.activeStar.userData.photosphere && this.activeStar.userData.photosphere.material.uniforms.spotStrength) { | |
| const u = this.activeStar.userData.photosphere.material.uniforms.spotStrength; | |
| u.value = Math.min(1.0, u.value + 0.2); | |
| } | |
| this.planets.forEach(planet => { | |
| planet.traverse(child => { | |
| if (child.userData.isAtmosphere) { | |
| let currentIntensity = 0; | |
| const animateAurora = () => { | |
| currentIntensity += 0.05; | |
| child.material.uniforms.auroraIntensity.value = Math.sin(currentIntensity) * 0.5 + 0.5; | |
| if (currentIntensity < Math.PI) requestAnimationFrame(animateAurora); | |
| else child.material.uniforms.auroraIntensity.value = 0; | |
| } | |
| animateAurora(); | |
| } | |
| }); | |
| }); | |
| } | |
| } | |
| // Handle environmental influence from cognitive core | |
| handleEnvironmentalInfluence(influence) { | |
| console.log('🌍 Environmental Influence Received:', influence); | |
| // Store current environmental state for universe dynamics | |
| this.environmentalState = { | |
| ...influence, | |
| timestamp: Date.now(), | |
| influenceStrength: (influence.harmony + influence.consciousness + influence.resonance) / 3 | |
| }; | |
| // Trigger immediate universe reactions | |
| this.triggerUniverseReaction(influence); | |
| } | |
| // Respond to universe environmental influence events | |
| respondToEnvironmentalInfluence(data) { | |
| console.log('🌌 Universe Environmental Response:', data); | |
| // Update universe breathing and growth parameters | |
| this.universeDynamics = { | |
| ...data, | |
| lastUpdate: Date.now(), | |
| breathingCycle: Math.sin(Date.now() / 5000) * data.breathing * 0.1, | |
| growthFactor: data.growth, | |
| changeIntensity: data.change | |
| }; | |
| // Apply universe transformations | |
| this.applyUniverseTransformations(data); | |
| } | |
| // Trigger immediate universe reaction to environmental influence | |
| triggerUniverseReaction(influence) { | |
| const { harmony, consciousness, resonance, volatility } = influence; | |
| // Adjust star properties based on harmony | |
| if (this.activeStar) { | |
| const baseSize = 20 + this.prng() * 25; | |
| const harmonyMultiplier = 0.8 + (harmony * 0.4); // 0.8 to 1.2 | |
| const newSize = baseSize * harmonyMultiplier; | |
| this.activeStar.scale.setScalar(newSize / baseSize); | |
| // Adjust star color based on consciousness | |
| const consciousnessHue = consciousness * 60; // 0-60 degrees (red to yellow) | |
| const starColor = new THREE.Color().setHSL(consciousnessHue / 360, 0.8, 0.6); | |
| this.activeStar.material.color = starColor; | |
| console.log(`⭐ Star transformed: Size ${newSize.toFixed(1)}, Hue ${consciousnessHue.toFixed(1)}°`); | |
| } | |
| // Adjust lighting based on resonance | |
| if (this.scene) { | |
| this.scene.traverse((object) => { | |
| if (object.isLight && object.userData.isGenerated) { | |
| const resonanceIntensity = 1 + (resonance * 0.5); // 1.0 to 1.5 | |
| object.intensity *= resonanceIntensity; | |
| } | |
| }); | |
| } | |
| // Trigger particle system reactions | |
| if (volatility > 0.3) { | |
| this.triggerParticleStorm(volatility); | |
| } | |
| } | |
| // Apply ongoing universe transformations | |
| applyUniverseTransformations(data) { | |
| const { growth, breathing, change } = data; | |
| // Universe breathing effect | |
| if (this.scene) { | |
| const breathingScale = 1 + (breathing * 0.05 * Math.sin(Date.now() / 3000)); | |
| this.scene.scale.setScalar(breathingScale); | |
| } | |
| // Growth-based universe expansion | |
| if (growth > 0.6 && this.universeObjects) { | |
| this.expandUniverseObjects(growth); | |
| } | |
| // Change-based universe mutation | |
| if (change > 0.5) { | |
| this.mutateUniverseElements(change); | |
| } | |
| // Dynamic starfield response | |
| this.updateStarfieldDynamics(data); | |
| } | |
| // Expand universe objects based on growth factor | |
| expandUniverseObjects(growthFactor) { | |
| if (!this.scene) return; | |
| this.scene.traverse((object) => { | |
| if (object.userData && object.userData.isGenerated && object.isMesh) { | |
| const baseScale = object.userData.originalScale || object.scale.x; | |
| const growthMultiplier = 1 + (growthFactor - 0.6) * 0.2; // 0% to 20% growth | |
| if (!object.userData.originalScale) { | |
| object.userData.originalScale = baseScale; | |
| } | |
| object.scale.setScalar(baseScale * growthMultiplier); | |
| } | |
| }); | |
| console.log(`🌌 Universe expanded by ${(growthFactor * 100).toFixed(1)}% growth factor`); | |
| } | |
| // Mutate universe elements based on change intensity | |
| mutateUniverseElements(changeIntensity) { | |
| if (!this.scene) return; | |
| // Randomly mutate colors and properties | |
| this.scene.traverse((object) => { | |
| if (object.userData && object.userData.isGenerated && object.material) { | |
| const mutationStrength = (changeIntensity - 0.5) * 0.3; // 0% to 30% mutation | |
| if (object.material.color) { | |
| const originalColor = object.userData.originalColor || object.material.color.clone(); | |
| if (!object.userData.originalColor) { | |
| object.userData.originalColor = originalColor; | |
| } | |
| // Mutate color slightly | |
| const hueShift = (Math.random() - 0.5) * mutationStrength; | |
| const satShift = (Math.random() - 0.5) * mutationStrength * 0.5; | |
| object.material.color.setHSL( | |
| (originalColor.getHSL({}).h + hueShift + 1) % 1, | |
| Math.max(0, Math.min(1, originalColor.getHSL({}).s + satShift)), | |
| originalColor.getHSL({}).l | |
| ); | |
| } | |
| } | |
| }); | |
| console.log(`🔄 Universe mutated by ${(changeIntensity * 100).toFixed(1)}% change intensity`); | |
| } | |
| // Update starfield dynamics | |
| updateStarfieldDynamics(data) { | |
| const { harmony, resonance, volatility } = data; | |
| if (this.localStarfield) { | |
| // Adjust starfield density based on harmony | |
| const harmonyScale = 0.7 + (harmony * 0.6); // 0.7 to 1.3 | |
| this.localStarfield.scale.setScalar(harmonyScale); | |
| } | |
| if (this.cosmicWeb) { | |
| // Adjust cosmic web visibility based on resonance | |
| const resonanceAlpha = 0.3 + (resonance * 0.7); // 0.3 to 1.0 | |
| // Note: This would require modifying the starfield material to support opacity | |
| } | |
| } | |
| // Trigger particle storm based on volatility | |
| triggerParticleStorm(volatility) { | |
| if (!this.scene) return; | |
| const particleCount = Math.floor(volatility * 100); | |
| const particles = []; | |
| for (let i = 0; i < particleCount; i++) { | |
| const geometry = new THREE.SphereGeometry(0.5 + Math.random() * 2, 8, 8); | |
| const material = new THREE.MeshBasicMaterial({ | |
| color: new THREE.Color().setHSL(Math.random(), 0.8, 0.6), | |
| transparent: true, | |
| opacity: 0.8 | |
| }); | |
| const particle = new THREE.Mesh(geometry, material); | |
| // Random position within viewing area | |
| particle.position.set( | |
| (Math.random() - 0.5) * 1000, | |
| (Math.random() - 0.5) * 1000, | |
| (Math.random() - 0.5) * 1000 | |
| ); | |
| particle.userData = { | |
| isGenerated: true, | |
| isParticle: true, | |
| velocity: new THREE.Vector3( | |
| (Math.random() - 0.5) * 2, | |
| (Math.random() - 0.5) * 2, | |
| (Math.random() - 0.5) * 2 | |
| ), | |
| lifetime: Date.now() + 5000 + Math.random() * 5000 // 5-10 seconds | |
| }; | |
| this.scene.add(particle); | |
| particles.push(particle); | |
| } | |
| console.log(`⚡ Particle storm triggered: ${particleCount} particles from volatility ${volatility.toFixed(2)}`); | |
| // Schedule particle cleanup | |
| setTimeout(() => { | |
| particles.forEach(particle => { | |
| if (particle.parent) { | |
| particle.parent.remove(particle); | |
| particle.geometry.dispose(); | |
| particle.material.dispose(); | |
| } | |
| }); | |
| }, 10000); | |
| } | |
| // Update universe dynamics in animation loop | |
| updateUniverseDynamics(deltaTime) { | |
| if (!this.universeDynamics) return; | |
| const { breathing, growth, change } = this.universeDynamics; | |
| // Apply breathing animation | |
| if (this.scene && breathing !== 0) { | |
| const breathCycle = Math.sin(Date.now() / 5000) * breathing * 0.03; | |
| const currentScale = this.scene.scale.x; | |
| const targetScale = 1 + breathCycle; | |
| // Smooth interpolation | |
| const lerpFactor = Math.min(deltaTime * 2, 1); | |
| const newScale = currentScale + (targetScale - currentScale) * lerpFactor; | |
| this.scene.scale.setScalar(newScale); | |
| } | |
| // Update particle systems | |
| this.updateParticleSystems(deltaTime); | |
| // Apply environmental influence to camera | |
| this.applyEnvironmentalCameraEffects(); | |
| } | |
| // Update particle systems | |
| updateParticleSystems(deltaTime) { | |
| if (!this.scene) return; | |
| this.scene.traverse((object) => { | |
| if (object.userData && object.userData.isParticle) { | |
| // Update particle position | |
| if (object.userData.velocity) { | |
| object.position.add(object.userData.velocity.clone().multiplyScalar(deltaTime)); | |
| } | |
| // Update particle opacity based on lifetime | |
| if (object.userData.lifetime) { | |
| const remainingLife = object.userData.lifetime - Date.now(); | |
| const totalLife = 10000; // 10 seconds | |
| const opacity = Math.max(0, remainingLife / totalLife); | |
| if (object.material) { | |
| object.material.opacity = opacity; | |
| // Remove dead particles | |
| if (opacity <= 0 && object.parent) { | |
| object.parent.remove(object); | |
| object.geometry.dispose(); | |
| object.material.dispose(); | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Apply environmental influence to camera | |
| applyEnvironmentalCameraEffects() { | |
| if (!this.camera || !this.environmentalState) return; | |
| const { resonance, harmony } = this.environmentalState; | |
| // Subtle camera movement based on resonance | |
| const time = Date.now() / 1000; | |
| const resonanceOffset = resonance * 2; | |
| // Apply micro-movements to camera | |
| this.camera.position.x += Math.sin(time * 0.5) * resonanceOffset * 0.01; | |
| this.camera.position.y += Math.cos(time * 0.3) * resonanceOffset * 0.01; | |
| // Harmony-based camera field of view | |
| const baseFOV = 75; | |
| const harmonyFOV = baseFOV * (0.95 + harmony * 0.1); // 71.25 to 82.5 | |
| this.camera.fov = harmonyFOV; | |
| this.camera.updateProjectionMatrix(); | |
| } | |
| updateCosmicGrowth(delta) { | |
| this.autonomousEventTimer += delta; | |
| if(this.autonomousEventTimer > 15) { // Check for major autonomous event | |
| this.triggerAutonomousEvent(); | |
| this.autonomousEventTimer = 0; | |
| } | |
| if(this.cosmicGrowthEnergy > 100) { // Check for minor growth event | |
| this.triggerCosmicRift(); | |
| this.cosmicGrowthEnergy = 0; | |
| } | |
| } | |
| createAsteroidBelt(center, innerR, outerR, count) { | |
| const positions = new Float32Array(count * 3); | |
| for (let i = 0; i < count; i++) { | |
| const r = innerR + Math.random() * (outerR - innerR); | |
| const a = Math.random() * Math.PI * 2; | |
| positions[i*3] = center.x + Math.cos(a) * r; | |
| positions[i*3+1] = center.y + (Math.random()-0.5) * (outerR - innerR) * 0.05; | |
| positions[i*3+2] = center.z + Math.sin(a) * r; | |
| } | |
| const geom = new THREE.BufferGeometry(); | |
| geom.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
| const mat = new THREE.PointsMaterial({ color: 0x888888, size: 1.5 }); | |
| const belt = new THREE.Points(geom, mat); | |
| belt.userData = { isGenerated: true, isAutonomous: true, life: 120 }; | |
| this.scene.add(belt); | |
| this.uiManager.log("A persistent asteroid belt has formed from debris.", "data"); | |
| } | |
| handleFutureEvent(evt) { | |
| if (!evt || !evt.type) return; | |
| this.uiManager.log(`AI scheduled event fired: ${evt.type}`, 'data'); | |
| switch (evt.type) { | |
| case 'growFlora': | |
| this.planets.forEach(planet => { | |
| if (Math.random() > 0.7) { | |
| const flora = this.createLSystemFlora(); | |
| flora.scale.set(0.1, 0.1, 0.1); | |
| flora.position.set(0, planet.geometry.parameters.radius, 0); | |
| planet.add(flora); | |
| } | |
| }); | |
| break; | |
| case 'solarFlare': | |
| if (this.activeStar) this.spawnSolarFlare(this.activeStar); | |
| break; | |
| case 'meteorShower': | |
| this.spawnMeteorShower(); | |
| break; | |
| case 'lightningStorm': | |
| this.triggerAutonomousEvent(); | |
| break; | |
| case 'comet': | |
| this.triggerAutonomousEvent(); | |
| break; | |
| case 'spawnBlackHole': | |
| this.createBlackHole(); | |
| break; | |
| case 'bloom': | |
| // Trigger seed dispersal on visible flora | |
| this.scene.traverse(obj => { | |
| if (obj.userData && obj.userData.isFlora && obj.userData.fruits) { | |
| this.disperseSeeds(obj.userData.fruits); | |
| } | |
| }); | |
| this.uiManager.log("A cosmic bloom has spread seeds across the system.", 'data'); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| spawnSolarFlare(star) { | |
| const flareCount = 5 + Math.floor(this.prng() * 8); | |
| for (let i = 0; i < flareCount; i++) { | |
| const dir = new THREE.Vector3().randomDirection(); | |
| const start = star.position.clone().add(dir.clone().multiplyScalar((star.geometry.parameters.radius || 20) * 1.2)); | |
| const end = start.clone().add(dir.clone().multiplyScalar(500 + this.prng() * 1000)); | |
| const bolt = this.createLightningBolt(start, end); | |
| bolt.userData.life = 0.2 + this.prng() * 0.4; | |
| this.scene.add(bolt); | |
| } | |
| this.uiManager.log("Solar flare emitted from the star.", "data"); | |
| } | |
| spawnMeteorShower() { | |
| const count = 60 + Math.floor(this.prng() * 120); | |
| const positions = []; | |
| const velocities = []; | |
| for (let i = 0; i < count; i++) { | |
| const pos = new THREE.Vector3().randomDirection().multiplyScalar(7000 + this.prng() * 3000); | |
| positions.push(pos.x, pos.y, pos.z); | |
| velocities.push((this.prng()-0.5)*200, (this.prng()-0.5)*50, (this.prng()-0.5)*200); | |
| } | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); | |
| const material = new THREE.PointsMaterial({ color: 0xffddaa, size: 3, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false }); | |
| const shower = new THREE.Points(geometry, material); | |
| shower.userData = { isGenerated: true, isAutonomous: true, life: 20, velocityArray: velocities }; | |
| this.scene.add(shower); | |
| this.uiManager.log("Meteor shower cascades through the system.", "data"); | |
| // After shower, occasionally form a belt | |
| if (Math.random() < 0.5 && this.activeStar) { | |
| this.createAsteroidBelt(new THREE.Vector3(), 2000 + Math.random()*1000, 4000 + Math.random()*2000, 2000 + Math.floor(Math.random()*3000)); | |
| } | |
| } | |
| triggerCosmicRift() { | |
| const riftPosition = new THREE.Vector3( | |
| (Math.random() - 0.5) * 10000, | |
| (Math.random() - 0.5) * 10000, | |
| (Math.random() - 0.5) * 10000 | |
| ); | |
| const baseColor = new THREE.Color().setHSL(Math.random(), 0.8, 0.6); | |
| const nebula = this.createNebula(baseColor); | |
| nebula.scale.set(0.1, 0.1, 0.1); // Start small | |
| nebula.position.copy(riftPosition); | |
| this.uiManager.log("Ambient energy has torn a new rift, expanding the cosmos.", "data"); | |
| } | |
| triggerAutonomousEvent() { | |
| const eventType = this.prng(); | |
| if (eventType > 0.8) { | |
| this.uiManager.log("AI spawned a cosmic lightning storm.", "data"); | |
| const numBolts = 5 + Math.floor(this.prng() * 10); | |
| for (let i=0; i < numBolts; i++) { | |
| const start = new THREE.Vector3().randomDirection().multiplyScalar(5000 + this.prng() * 5000); | |
| const end = new THREE.Vector3().randomDirection().multiplyScalar(5000 + this.prng() * 5000); | |
| const bolt = this.createLightningBolt(start, end); | |
| this.scene.add(bolt); | |
| } | |
| } else if (eventType > 0.4) { // Asteroid Field | |
| const asteroidCount = 50 + this.prng() * 100; | |
| const positions = []; | |
| for (let i = 0; i < asteroidCount; i++) positions.push((this.prng() - 0.5) * 5000, (this.prng() - 0.5) * 500, (this.prng() - 0.5) * 5000); | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); | |
| const material = new THREE.PointsMaterial({ color: 0x888888, size: this.prng() * 2 + 1 }); | |
| const field = new THREE.Points(geometry, material); | |
| field.userData = { isGenerated: true, isAutonomous: true, life: 30 }; | |
| this.scene.add(field); | |
| this.uiManager.log("AI spawned a passing asteroid field.", "data"); | |
| } else { // Comet | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute([0,0,0], 3)); | |
| const material = new THREE.PointsMaterial({ color: 0x87ceeb, size: 15, blending: THREE.AdditiveBlending, map: this.createCometTexture(), transparent: true }); | |
| const comet = new THREE.Points(geometry, material); | |
| const startPos = new THREE.Vector3((Math.random() - 0.5) * 10000, (Math.random() - 0.5) * 10000, (Math.random() - 0.5) * 10000); | |
| comet.position.copy(startPos); | |
| comet.userData = { isGenerated: true, isAutonomous: true, life: 60, velocity: new THREE.Vector3().sub(startPos).normalize().multiplyScalar(220), isComet: true, tail: null }; | |
| this.addCometTail(comet); | |
| this.scene.add(comet); | |
| this.uiManager.log("AI spawned a passing comet with a glowing tail.", "data"); | |
| } | |
| } | |
| addCometTail(comet) { | |
| const segs = 50; | |
| const positions = new Float32Array(segs * 3); | |
| for (let i = 0; i < segs; i++) { | |
| positions[i*3] = 0; positions[i*3+1] = 0; positions[i*3+2] = -i * 10; | |
| } | |
| const geom = new THREE.BufferGeometry(); | |
| geom.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
| // Tail color influenced by star temperature and distance: hotter = bluer | |
| let tailColor = 0x99ccff; | |
| if (this.activeStar) { | |
| const tempColor = this.activeStar.material.color || new THREE.Color(1,1,1); | |
| // Approximate blue bias with higher luminance | |
| const bias = Math.max(0, Math.min(1, (tempColor.b - tempColor.r) * 0.8 + 0.5)); | |
| const c = new THREE.Color().setHSL(0.6 + 0.1*bias, 0.7, 0.7); | |
| tailColor = c.getHex(); | |
| } | |
| const mat = new THREE.LineBasicMaterial({ color: tailColor, transparent: true, opacity: 0.7, blending: THREE.AdditiveBlending }); | |
| const tail = new THREE.Line(geom, mat); | |
| tail.userData = { isGenerated: true, isCometTail: true }; | |
| comet.add(tail); | |
| comet.userData.tail = tail; | |
| } | |
| createLightningBolt(start, end) { | |
| const points = [start.clone()]; | |
| let current = start.clone(); | |
| const direction = end.clone().sub(start).normalize(); | |
| const totalDist = start.distanceTo(end); | |
| while(current.distanceTo(start) < totalDist) { | |
| const step = 50 + this.prng() * 200; | |
| current.add(direction.clone().multiplyScalar(step)); | |
| const offset = new THREE.Vector3().randomDirection().multiplyScalar(this.prng() * 50); | |
| points.push(current.clone().add(offset)); | |
| // Randomly branch off | |
| if (this.prng() > 0.9) { | |
| const branchEnd = current.clone().add(new THREE.Vector3().randomDirection().multiplyScalar(500)); | |
| points.push(current.clone()); // Go back to start of branch | |
| points.push(branchEnd); | |
| points.push(current.clone()); // Return to main bolt | |
| } | |
| } | |
| points.push(end.clone()); | |
| const geometry = new THREE.BufferGeometry().setFromPoints(points); | |
| const material = new THREE.LineBasicMaterial({ color: 0x87cefa, transparent: true, blending: THREE.AdditiveBlending }); | |
| const bolt = new THREE.LineSegments(geometry, material); | |
| bolt.userData = { isGenerated: true, isAutonomous: true, life: 0.5 + this.prng() }; // Short life | |
| return bolt; | |
| } | |
| createCometTexture() { | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = 64; canvas.height = 64; | |
| const context = canvas.getContext('2d'); | |
| const gradient = context.createRadialGradient(32, 32, 0, 32, 32, 32); | |
| gradient.addColorStop(0, 'rgba(255,255,255,1)'); | |
| gradient.addColorStop(0.2, 'rgba(200,200,255,1)'); | |
| gradient.addColorStop(1, 'rgba(100,100,255,0)'); | |
| context.fillStyle = gradient; | |
| context.fillRect(0,0,64,64); | |
| return new THREE.CanvasTexture(canvas); | |
| } | |
| toggleScanner() { | |
| if (!this.hiddenEcho) { this.uiManager.log('No anomaly detected in this system.', 'warn'); return; } | |
| this.isScanning = !this.isScanning; | |
| this.uiManager.log(`Anomaly Scanner ${this.isScanning ? 'Activated' : 'Deactivated'}`, 'info'); | |
| if (this.isScanning && !this.scannerSound) { | |
| this.scannerSound = new THREE.PositionalAudio(this.listener); | |
| const audioCtx = this.listener.context; | |
| const osc = audioCtx.createOscillator(); | |
| const gain = audioCtx.createGain(); | |
| osc.type = 'sine'; osc.frequency.value = 800; gain.gain.value = 0; | |
| osc.connect(gain); | |
| this.scannerSound.setNodeSource(gain); | |
| this.scannerSound.setRefDistance(20); this.scannerSound.setRolloffFactor(2); | |
| osc.start(); | |
| this.hiddenEcho.add(this.scannerSound); | |
| } | |
| if (this.scannerSound) this.isScanning ? this.scannerSound.play() : this.scannerSound.stop(); | |
| } | |
| updateScanner() { | |
| if (!this.isScanning || !this.scannerSound || !this.scannerSound.source) return; | |
| const gain = this.scannerSound.getOutput(); | |
| const time = this.clock.getElapsedTime(); | |
| const pulse = (Math.sin(time * 8) + 1) / 2; | |
| gain.gain.setTargetAtTime(pulse * 0.2, this.listener.context.currentTime, 0.01); | |
| } | |
| updateLensing() { | |
| if (this.blackHoles.length === 0) { | |
| this.lensingPass.enabled = false; | |
| return; | |
| } | |
| const blackHole = this.blackHoles[0]; | |
| const screenPos = blackHole.position.clone().project(this.camera); | |
| if (screenPos.x > -1 && screenPos.x < 1 && screenPos.y > -1 && screenPos.y < 1 && screenPos.z < 1) { | |
| this.lensingPass.enabled = true; | |
| this.lensingPass.uniforms.center.value.set((screenPos.x + 1) / 2, (screenPos.y + 1) / 2); | |
| } else { | |
| this.lensingPass.enabled = false; | |
| } | |
| } | |
| updateSSRProximity() { | |
| if (!this.ssrPass) return; | |
| let enableSSR = false; | |
| const threshold = 800; | |
| this.scene.traverse(obj => { | |
| if (obj.userData && obj.userData.isMonolith) { | |
| const d = obj.position.distanceTo(this.camera.position); | |
| if (d < threshold) enableSSR = true; | |
| } | |
| }); | |
| this.ssrPass.enabled = enableSSR; | |
| } | |
| updateCinematicDOF() { | |
| if (!this.bokehPass || !this.bokehPass.enabled) return; | |
| // Focus at the nearest generated object in the view for cinematic pull-focus | |
| let focusDistance = 250.0; | |
| let minDist = Infinity; | |
| const candidates = []; | |
| if (this.activeStar) candidates.push(this.activeStar); | |
| this.planets.forEach(p => candidates.push(p)); | |
| this.blackHoles.forEach(b => candidates.push(b)); | |
| for (const obj of candidates) { | |
| const dist = this.camera.position.distanceTo(obj.position); | |
| if (dist < minDist) { minDist = dist; focusDistance = dist; } | |
| } | |
| // Smoothly approach target focus distance | |
| this.bokehPass.materialBokeh.uniforms['focus'].value += (focusDistance - this.bokehPass.materialBokeh.uniforms['focus'].value) * 0.08; | |
| } | |
| focusEarth() { | |
| if (!this.nasaBodies || !this.nasaBodies.length) { this.uiManager.log('Earth reference not available yet.', 'warn'); return; } | |
| const earth = this.earthRef; | |
| if (!earth) { this.uiManager.log('Earth reference not found.', 'warn'); return; } | |
| const dir = earth.position.clone().sub(this.camera.position).normalize(); | |
| const targetPos = earth.position.clone().add(dir.clone().multiplyScalar(60)); | |
| this.camera.position.copy(targetPos); | |
| this.camera.lookAt(earth.position); | |
| this.uiManager.log('Camera moved near NASA Earth reference. Press H to toggle UI for coordinates.', 'success'); | |
| } | |
| toggleDevMode() { | |
| this.isDevMode = !this.isDevMode; | |
| if (this.isDevMode) { | |
| this.soundBrush = new SoundBrush(this.scene, this.camera, this.eventBus); | |
| this.uiManager.log("Developer Mode ON: Sound Brush active. Click to paint.", "warn"); | |
| } else { | |
| if (this.soundBrush) this.soundBrush.destroy(); | |
| this.soundBrush = null; | |
| this.uiManager.log("Developer Mode OFF.", "info"); | |
| } | |
| } | |
| toggleAmbientAudio(isEnabled) { | |
| this.isAmbientAudioEnabled = isEnabled; | |
| if (isEnabled && !this.proceduralAudio && this.activeStar) { | |
| const starTemp = 2000 + this.prng() * 18000; | |
| this.proceduralAudio = new ProceduralAudioEngine(this.scene, this.listener, { star: this.activeStar, starTemp, planets: this.planets }); | |
| this.uiManager.log("Procedural ambient audio activated.", "info"); | |
| } else if (!isEnabled && this.proceduralAudio) { | |
| this.proceduralAudio.destroy(); | |
| this.proceduralAudio = null; | |
| this.uiManager.log("Procedural ambient audio deactivated.", "info"); | |
| } | |
| } | |
| blackBodyToRGB(temp) { | |
| temp /= 100; | |
| let r, g, b; | |
| if (temp <= 66) { r = 255; g = 99.47 * Math.log(temp) - 161.12; } else { r = 329.7 * Math.pow(temp - 60, -0.133); } | |
| if (temp <= 66) { g = 99.47 * Math.log(temp) - 161.12; b = temp <= 19 ? 0 : 138.52 * Math.log(temp - 10) - 305.04; } else { g = 288.12 * Math.pow(temp - 60, -0.0755); b = 255; } | |
| return new THREE.Color(Math.max(0, Math.min(255, r))/255, Math.max(0, Math.min(255, g))/255, Math.max(0, Math.min(255, b))/255); | |
| } | |
| snapshotToJ3() { | |
| const j3 = []; | |
| this.scene.traverse(child => { | |
| if ((child.isMesh || child.isPoints || child.isLineSegments) && child.userData.isGenerated && child.geometry && child.material) { | |
| const color = child.material.color ? [child.material.color.r, child.material.color.g, child.material.color.b] : [1,1,1]; | |
| j3.push({ | |
| type: child.type, | |
| positions: Array.from(child.geometry.attributes.position.array), | |
| color: color, | |
| matrix: child.matrixWorld.elements | |
| }); | |
| } | |
| }); | |
| return j3; | |
| } | |
| } | |
| class AtmosphericEntryEffect { | |
| constructor(camera, eventBus) { | |
| this.camera = camera; | |
| this.eventBus = eventBus; | |
| this.isActive = false; | |
| this.intensity = 0.0; | |
| this.time = 0.0; | |
| const plasmaGeo = new THREE.SphereGeometry(1.1, 32, 32); | |
| const plasmaMat = new THREE.ShaderMaterial({ | |
| uniforms: { time: { value: 0.0 }, intensity: { value: 0.0 } }, | |
| vertexShader: ` | |
| varying vec2 vUv; | |
| void main() { | |
| vUv = uv; | |
| gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); | |
| } | |
| `, | |
| fragmentShader: ` | |
| uniform float time; | |
| uniform float intensity; | |
| varying vec2 vUv; | |
| float noise(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); } | |
| void main() { | |
| vec2 uv = vUv * 4.0 - 2.0; | |
| float plasma = noise(uv * 2.0 + time * 2.0) * 0.5 + | |
| noise(uv * 5.0 - time * 3.0) * 0.3; | |
| vec3 color = mix(vec3(1.0, 0.2, 0.0), vec3(1.0, 0.8, 0.2), plasma); | |
| float edge = smoothstep(0.8, 1.0, length(vUv * 2.0 - 1.0)); | |
| gl_FragColor = vec4(color, (1.0 - edge) * intensity * plasma); | |
| } | |
| `, | |
| transparent: true, | |
| blending: THREE.AdditiveBlending, | |
| depthWrite: false, | |
| side: THREE.BackSide, | |
| }); | |
| this.plasmaShield = new THREE.Mesh(plasmaGeo, plasmaMat); | |
| this.plasmaShield.scale.set(camera.near * 2, camera.near * 2, camera.near * 2); | |
| this.camera.add(this.plasmaShield); | |
| this.plasmaShield.visible = false; | |
| } | |
| update(delta, camera, planets) { | |
| this.time += delta; | |
| let isNearPlanet = false; | |
| if (planets && planets.length > 0) { | |
| for (const planet of planets) { | |
| const dist = camera.position.distanceTo(planet.position); | |
| const entryDist = planet.geometry.parameters.radius * 2.0; | |
| if (dist < entryDist) { | |
| isNearPlanet = true; | |
| this.intensity = Math.min(1.0, this.intensity + delta * 0.5); | |
| const shake = this.intensity * 0.1; | |
| camera.position.x += (Math.random() - 0.5) * shake; | |
| camera.position.y += (Math.random() - 0.5) * shake; | |
| break; | |
| } | |
| } | |
| } | |
| if (!isNearPlanet) { | |
| this.intensity = Math.max(0.0, this.intensity - delta * 0.5); | |
| } | |
| this.plasmaShield.visible = this.intensity > 0.01; | |
| if(this.plasmaShield.visible) { | |
| this.plasmaShield.material.uniforms.intensity.value = this.intensity; | |
| this.plasmaShield.material.uniforms.time.value = this.time; | |
| } | |
| } | |
| } | |
| class AudioReactiveSystem { | |
| constructor(scene, eventBus) { | |
| this.scene = scene; | |
| this.eventBus = eventBus; | |
| this.particles = null; | |
| this.MAX_PARTICLES = 5000; | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(this.MAX_PARTICLES * 3), 3)); | |
| geometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(this.MAX_PARTICLES * 3), 3)); | |
| const material = new THREE.PointsMaterial({ size: 1.5, vertexColors: true, blending: THREE.AdditiveBlending, depthWrite: false, transparent: true }); | |
| this.particleSystem = new THREE.Points(geometry, material); | |
| this.particleSystem.userData.isGenerated = true; | |
| this.scene.add(this.particleSystem); | |
| this.particleCursor = 0; | |
| this.velocities = []; | |
| this.eventBus.subscribe('audio:data', data => this.onAudioData(data)); | |
| } | |
| onAudioData({ raw, avgFreq }) { | |
| if (!this.particleSystem) return; | |
| const positions = this.particleSystem.geometry.attributes.position.array; | |
| const colors = this.particleSystem.geometry.attributes.color.array; | |
| const volume = raw.reduce((a, b) => a + b) / raw.length / 255; | |
| const numToSpawn = Math.floor(volume * 20); | |
| for (let i = 0; i < numToSpawn; i++) { | |
| const pIndex = this.particleCursor; | |
| const theta = Math.random() * 2 * Math.PI; | |
| const phi = Math.acos(2 * Math.random() - 1); | |
| const radius = 5 + Math.random() * 20; | |
| positions[pIndex * 3] = radius * Math.sin(phi) * Math.cos(theta); | |
| positions[pIndex * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta); | |
| positions[pIndex * 3 + 2] = radius * Math.cos(phi); | |
| const color = new THREE.Color().setHSL(avgFreq / 150, 0.8, 0.6); | |
| colors[pIndex * 3] = color.r; colors[pIndex * 3 + 1] = color.g; colors[pIndex * 3 + 2] = color.b; | |
| this.velocities[pIndex] = new THREE.Vector3((Math.random()-0.5), (Math.random()-0.5), (Math.random()-0.5)).multiplyScalar(volume * 2.0); | |
| this.particleCursor = (this.particleCursor + 1) % this.MAX_PARTICLES; | |
| } | |
| } | |
| update(delta) { | |
| if (!this.particleSystem) return; | |
| const positions = this.particleSystem.geometry.attributes.position.array; | |
| for (let i = 0; i < this.MAX_PARTICLES; i++) { | |
| if (this.velocities[i]) { | |
| positions[i * 3] += this.velocities[i].x * delta; | |
| positions[i * 3 + 1] += this.velocities[i].y * delta; | |
| positions[i * 3 + 2] += this.velocities[i].z * delta; | |
| this.velocities[i].multiplyScalar(1 - (0.5 * delta)); | |
| } | |
| } | |
| this.particleSystem.geometry.attributes.position.needsUpdate = true; | |
| } | |
| } | |
| class MemoryEcho { | |
| constructor(scene, camera, listener, uiManager, eventBus) { | |
| this.scene = scene; this.camera = camera; this.listener = listener; | |
| this.uiManager = uiManager; this.eventBus = eventBus; | |
| this.isActive = false; this.data = null; this.particleSystem = null; | |
| this.player = null; this.artifacts = []; this.keys = {}; | |
| this.audioCtx = null; this.oscillator = null; this.gainNode = null; | |
| this.foundCount = 0; | |
| document.addEventListener('keydown', e => this.keys[e.key.toLowerCase()] = true); | |
| document.addEventListener('keyup', e => this.keys[e.key.toLowerCase()] = false); | |
| } | |
| loadData(file) { | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { this.data = new Uint8Array(e.target.result); this.uiManager.log("Data catalyst processed.", "success"); }; | |
| reader.readAsArrayBuffer(file); | |
| } | |
| enter() { | |
| if (!this.data) { this.uiManager.log("Cannot enter echo without data catalyst.", "error"); return; } | |
| this.isActive = true; | |
| this.uiManager.hideModal(); | |
| this.eventBus.publish('pge:modeChange', 'echo'); | |
| this.uiManager.log("Transmuting data... Entering playable memory echo.", "warn"); | |
| this.eventBus.publish("status:update", "ECHO: TERMINA"); | |
| this.camera.position.set(0, 10, 50); this.camera.rotation.set(0,0,0); | |
| const prng = sfc32(...cyrb128(this.data.toString().substring(0, 1000))); | |
| const positions = [], colors = []; | |
| const colorPalettes = [ { r: 0.5, g: 0.2, b: 0.8 }, { r: 0.2, g: 0.8, b: 0.5 }, { r: 0.9, g: 0.5, b: 0.2 }, { r: 0.2, g: 0.5, b: 0.9 } ]; | |
| const quadrantSize = 2000; | |
| for (let i = 0; i < 80000; i++) { | |
| const quadrant = Math.floor(prng() * 4); const palette = colorPalettes[quadrant]; | |
| const x = (prng() - 0.5) * quadrantSize + (quadrant % 2 === 0 ? -quadrantSize/2 : quadrantSize/2); | |
| const z = (prng() - 0.5) * quadrantSize + (quadrant < 2 ? -quadrantSize/2 : quadrantSize/2); | |
| const y = (prng() - 0.5) * 500; | |
| positions.push(x, y, z); colors.push(palette.r * prng(), palette.g * prng(), palette.b * prng()); | |
| } | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); | |
| geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); | |
| this.particleSystem = new THREE.Points(geometry, new THREE.PointsMaterial({ size: 3, vertexColors: true, sizeAttenuation: true, blending: THREE.AdditiveBlending, depthWrite: false, opacity: 0.7, transparent: true })); | |
| this.scene.add(this.particleSystem); | |
| this.player = new THREE.Group(); | |
| this.player.add(new THREE.Mesh(new THREE.IcosahedronGeometry(2, 0), new THREE.MeshBasicMaterial({ color: 0xffffff }))); | |
| this.player.position.set(0, 0, 0); | |
| this.scene.add(this.player); | |
| for (let i = 0; i < 4; i++) { | |
| const palette = colorPalettes[i]; | |
| const artifact = new THREE.Mesh(new THREE.BoxGeometry(10, 20, 10), new THREE.MeshBasicMaterial({ color: new THREE.Color(palette.r, palette.g, palette.b), wireframe: true })); | |
| const x = (prng() - 0.5) * quadrantSize/2 + (i % 2 === 0 ? -quadrantSize/2 : quadrantSize/2); | |
| const z = (prng() - 0.5) * quadrantSize/2 + (i < 2 ? -quadrantSize/2 : quadrantSize/2); | |
| artifact.position.set(x, 0, z); artifact.userData.isArtifact = true; | |
| this.artifacts.push(artifact); this.scene.add(artifact); | |
| } | |
| this.initAudioSearch(); | |
| } | |
| update(delta) { | |
| if (!this.isActive || !this.player) return; | |
| const moveSpeed = 100 * delta; | |
| if (this.keys['w'] || this.keys['arrowup']) this.player.position.z -= moveSpeed; | |
| if (this.keys['s'] || this.keys['arrowdown']) this.player.position.z += moveSpeed; | |
| if (this.keys['a'] || this.keys['arrowleft']) this.player.position.x -= moveSpeed; | |
| if (this.keys['d'] || this.keys['arrowright']) this.player.position.x += moveSpeed; | |
| if (this.keys[' ']) this.player.position.y += moveSpeed; | |
| if (this.keys['shift']) this.player.position.y -= moveSpeed; | |
| this.camera.position.lerp(this.player.position.clone().add(new THREE.Vector3(0, 10, 50)), 0.05); | |
| this.camera.lookAt(this.player.position); | |
| let closestDist = Infinity; | |
| this.artifacts.forEach(artifact => { | |
| artifact.rotation.y += delta; | |
| const dist = this.player.position.distanceTo(artifact.position); | |
| if (dist < closestDist) closestDist = dist; | |
| if (dist < 20) { | |
| this.scene.remove(artifact); this.artifacts = this.artifacts.filter(a => a !== artifact); | |
| this.foundCount++; this.uiManager.log(`Giant's Remains Found! (${this.foundCount}/4)`, 'success'); | |
| } | |
| }); | |
| if (this.gainNode) { | |
| const proximity = Math.max(0, 1 - closestDist / 3000); | |
| this.gainNode.gain.setTargetAtTime(proximity * 0.1, this.audioCtx.currentTime, 0.01); | |
| this.oscillator.frequency.setTargetAtTime(100 + (1000 * proximity), this.audioCtx.currentTime, 0.01); | |
| } | |
| if (this.artifacts.length === 0) { this.uiManager.log("All remains found! The echo stabilizes.", "success"); this.exit(); } | |
| } | |
| exit() { | |
| this.isActive = false; | |
| if(this.particleSystem) { this.scene.remove(this.particleSystem); this.particleSystem.geometry.dispose(); this.particleSystem.material.dispose(); } | |
| if(this.player) this.scene.remove(this.player); | |
| this.artifacts.forEach(a => { this.scene.remove(a); a.geometry.dispose(); a.material.dispose(); }); | |
| this.artifacts = []; this.foundCount = 0; | |
| if(this.oscillator) this.oscillator.stop(); if(this.audioCtx) this.audioCtx.close(); | |
| this.audioCtx = null; this.oscillator = null; | |
| this.eventBus.publish('pge:modeChange', 'universe'); this.uiManager.log("Returned to void.", "info"); | |
| } | |
| initAudioSearch() { | |
| this.audioCtx = new(window.AudioContext || window.webkitAudioContext)(); | |
| this.oscillator = this.audioCtx.createOscillator(); this.gainNode = this.audioCtx.createGain(); | |
| this.oscillator.connect(this.gainNode); this.gainNode.connect(this.audioCtx.destination); | |
| this.gainNode.gain.value = 0; this.oscillator.type = 'sine'; this.oscillator.start(); | |
| } | |
| } | |
| class Particlizer { | |
| constructor(eventBus) { | |
| this.eventBus = eventBus; this.mesh = null; this.particles = []; | |
| this.sourceType = 'none'; this.videoElement = document.getElementById('video-feed'); | |
| this.uploadedVideoElement = null; | |
| } | |
| particlize(source) { | |
| this.destroy(); | |
| if (source instanceof File) { | |
| if (source.type.startsWith('image/')) { this.sourceType = 'image'; this.fromImage(source); } | |
| else if (source.type.startsWith('video/')) { this.sourceType = 'uploadedVideo'; this.fromUploadedVideo(source); } | |
| } else if (source === 'liveVideo') { this.sourceType = 'liveVideo'; this.fromLiveVideo(); } | |
| else if (Array.isArray(source)) { this.sourceType = 'j3'; this.fromJ3(source); } | |
| } | |
| fromImage(file) { | |
| const reader = new FileReader(); | |
| reader.onload = e => { const img = new Image(); img.onload = () => this.processImageData(img); img.src = e.target.result; }; | |
| reader.readAsDataURL(file); | |
| } | |
| fromUploadedVideo(file) { | |
| const url = URL.createObjectURL(file); | |
| this.uploadedVideoElement = document.createElement('video'); | |
| this.uploadedVideoElement.src = url; this.uploadedVideoElement.loop = true; | |
| this.uploadedVideoElement.muted = true; this.uploadedVideoElement.play(); | |
| this.uploadedVideoElement.oncanplay = () => { this.processImageData(this.uploadedVideoElement); }; | |
| } | |
| fromLiveVideo() { this.processImageData(this.videoElement); } | |
| fromJ3(j3Data) { | |
| const positions = [], colors = []; this.particles = []; | |
| j3Data.forEach(meshData => { | |
| const meshPositions = meshData.positions; | |
| const meshColor = new THREE.Color(...meshData.color); | |
| const matrix = new THREE.Matrix4().fromArray(meshData.matrix); | |
| for(let i = 0; i < meshPositions.length; i += 3) { | |
| const vec = new THREE.Vector3(meshPositions[i], meshPositions[i+1], meshPositions[i+2]).applyMatrix4(matrix); | |
| positions.push(vec.x, vec.y, vec.z); colors.push(meshColor.r, meshColor.g, meshColor.b); | |
| this.particles.push({ pos: vec.clone(), originalPos: vec.clone(), vel: new THREE.Vector3().randomDirection().multiplyScalar(50) }); | |
| } | |
| }); | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); | |
| geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); | |
| this.mesh = new THREE.Points(geometry, new THREE.PointsMaterial({ size: 1, vertexColors: true, sizeAttenuation: true })); | |
| this.eventBus.publish('particlizer:created', this); | |
| } | |
| processImageData(source) { | |
| const canvas = document.createElement('canvas'); | |
| const ctx = canvas.getContext('2d', { willReadFrequently: true }); | |
| const MAX_DIM = 128; | |
| const sourceWidth = source.videoWidth || source.width, sourceHeight = source.videoHeight || source.height; | |
| const scale = (sourceWidth > 0 && sourceHeight > 0) ? Math.min(MAX_DIM / sourceWidth, MAX_DIM / sourceHeight) : 1; | |
| canvas.width = sourceWidth * scale; canvas.height = sourceHeight * scale; | |
| if(canvas.width === 0 || canvas.height === 0) return; | |
| ctx.drawImage(source, 0, 0, canvas.width, canvas.height); | |
| const { data, width, height } = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| if (this.mesh && (this.sourceType === 'liveVideo' || this.sourceType === 'uploadedVideo')) { | |
| const colors = this.mesh.geometry.attributes.color.array; let particleIndex = 0; | |
| for (let y = 0; y < height; y++) { | |
| for (let x = 0; x < width; x++) { | |
| const i = (y * width + x) * 4; | |
| if (data[i+3] > 128 && particleIndex < this.particles.length) { | |
| colors[particleIndex*3]=data[i]/255; colors[particleIndex*3+1]=data[i+1]/255; colors[particleIndex*3+2]=data[i+2]/255; | |
| particleIndex++; | |
| } | |
| } | |
| } | |
| if (particleIndex > 0) this.mesh.geometry.attributes.color.needsUpdate = true; | |
| } else { | |
| this.destroy(); const positions = [], colors = []; this.particles = []; | |
| for (let y = 0; y < height; y++) { | |
| for (let x = 0; x < width; x++) { | |
| const i = (y * width + x) * 4; | |
| if (data[i+3] > 128) { | |
| const initialPos = new THREE.Vector3().randomDirection().multiplyScalar(Math.random() * 100); | |
| const targetPos = new THREE.Vector3(x - width/2, -y + height/2, 0); | |
| positions.push(initialPos.x, initialPos.y, initialPos.z); | |
| colors.push(data[i]/255, data[i+1]/255, data[i+2]/255); | |
| this.particles.push({ | |
| pos: initialPos.clone(), | |
| targetPos: targetPos.clone(), | |
| vel: new THREE.Vector3() | |
| }); | |
| } | |
| } | |
| } | |
| if (positions.length === 0) return; | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(positions), 3)); | |
| geometry.setAttribute('color', new THREE.Float32BufferAttribute(new Float32Array(colors), 3)); | |
| this.mesh = new THREE.Points(geometry, new THREE.PointsMaterial({ size: 2, vertexColors: true, sizeAttenuation: true, blending: THREE.AdditiveBlending, depthWrite: false })); | |
| this.eventBus.publish('particlizer:created', this); | |
| } | |
| } | |
| update(delta) { | |
| if(!this.mesh || !this.mesh.visible) return; | |
| const currentSource = this.sourceType === 'uploadedVideo' ? this.uploadedVideoElement : this.videoElement; | |
| if((this.sourceType === 'liveVideo' || this.sourceType === 'uploadedVideo') && currentSource && currentSource.readyState >= 2){ | |
| this.processImageData(currentSource); | |
| } | |
| const positions = this.mesh.geometry.attributes.position.array; | |
| for(let i=0; i<this.particles.length; i++) { | |
| const p = this.particles[i]; | |
| if (this.sourceType === 'j3') { | |
| p.pos.add(p.vel.clone().multiplyScalar(delta)); | |
| p.vel.multiplyScalar(0.99); // Slow down over time | |
| } else { | |
| const force = p.targetPos.clone().sub(p.pos).multiplyScalar(0.1); | |
| p.vel.add(force); | |
| p.vel.multiplyScalar(0.95); | |
| p.pos.add(p.vel); | |
| } | |
| positions[i*3] = p.pos.x; | |
| positions[i*3+1] = p.pos.y; | |
| positions[i*3+2] = p.pos.z; | |
| } | |
| this.mesh.geometry.attributes.position.needsUpdate = true; | |
| } | |
| destroy() { | |
| if(this.mesh) { | |
| if(this.mesh.parent) this.mesh.parent.remove(this.mesh); | |
| if (this.mesh.geometry) this.mesh.geometry.dispose(); | |
| if (this.mesh.material) this.mesh.material.dispose(); | |
| this.mesh = null; this.particles = []; | |
| } | |
| if(this.uploadedVideoElement) { | |
| this.uploadedVideoElement.pause(); URL.revokeObjectURL(this.uploadedVideoElement.src); | |
| this.uploadedVideoElement = null; | |
| } | |
| } | |
| } | |
| // ============================================================================ | |
| // GENESIS UNIVERSE ENGINE - AUDIO-DRIVEN COSMIC SIMULATION | |
| // ============================================================================ | |
| class GenesisUniverseEngine { | |
| constructor() { | |
| this.scene = null; | |
| this.camera = null; | |
| this.renderer = null; | |
| this.eventBus = { | |
| subscribers: {}, | |
| subscribe: function(event, callback) { | |
| if (!this.subscribers[event]) { | |
| this.subscribers[event] = []; | |
| } | |
| this.subscribers[event].push(callback); | |
| }, | |
| publish: function(event, data) { | |
| if (this.subscribers[event]) { | |
| this.subscribers[event].forEach(callback => callback(data)); | |
| } | |
| } | |
| }; | |
| this.livingEntities = []; | |
| this.environmentalState = {}; | |
| this.universeDynamics = { | |
| breathing: 0, | |
| growth: 0, | |
| change: 0, | |
| expansion: 1.0 | |
| }; | |
| } | |
| async initialize() { | |
| console.log('🌌 Initializing Genesis Universe Engine...'); | |
| // Use existing scene and camera if available | |
| if (window.pge && window.pge.scene && window.pge.camera) { | |
| this.scene = window.pge.scene; | |
| this.camera = window.pge.camera; | |
| this.renderer = window.pge.renderer; | |
| console.log('✅ Using existing ProceduralGenerationEngine scene'); | |
| } else { | |
| // Create Three.js scene if none exists | |
| this.scene = new THREE.Scene(); | |
| this.scene.background = new THREE.Color(0x000510); | |
| this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| this.camera.position.z = 250; | |
| this.renderer = new THREE.WebGLRenderer({ | |
| antialias: true, | |
| logarithmicDepthBuffer: true | |
| }); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.renderer.setPixelRatio(window.devicePixelRatio); | |
| this.renderer.outputEncoding = THREE.sRGBEncoding; | |
| this.renderer.toneMapping = THREE.ACESFilmicToneMapping; | |
| this.renderer.toneMappingExposure = 1.0; | |
| const canvas = document.getElementById('universe-canvas'); | |
| if (canvas) { | |
| canvas.parentNode.replaceChild(this.renderer.domElement, canvas); | |
| } | |
| // Basic lighting | |
| const ambientLight = new THREE.AmbientLight(0x404040, 0.6); | |
| this.scene.add(ambientLight); | |
| const pointLight = new THREE.PointLight(0xffffff, 1, 1000); | |
| pointLight.position.set(100, 100, 100); | |
| this.scene.add(pointLight); | |
| // Create starfield if scene is empty | |
| if (this.scene.children.length <= 2) { | |
| this.createStarfield(); | |
| } | |
| } | |
| // Start render loop | |
| this.animate(); | |
| // Subscribe to audio events from global event bus | |
| if (window.app && window.app.eventBus) { | |
| window.app.eventBus.subscribe('audio:universeInfluence', (data) => { | |
| this.handleAudioUniverseInfluence(data); | |
| }); | |
| } | |
| console.log('✅ Genesis Universe Engine initialized and connected to audio'); | |
| } | |
| createStarfield() { | |
| const starGeometry = new THREE.BufferGeometry(); | |
| const starCount = 10000; | |
| const positions = new Float32Array(starCount * 3); | |
| for (let i = 0; i < starCount * 3; i++) { | |
| positions[i] = (Math.random() - 0.5) * 2000; | |
| } | |
| starGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
| const starMaterial = new THREE.PointsMaterial({ | |
| color: 0xffffff, | |
| size: 0.5, | |
| transparent: true, | |
| opacity: 0.8 | |
| }); | |
| const stars = new THREE.Points(starGeometry, starMaterial); | |
| stars.userData = { isStarfield: true }; | |
| this.scene.add(stars); | |
| } | |
| animate() { | |
| requestAnimationFrame(() => this.animate()); | |
| if (this.scene && this.camera) { | |
| // Apply universe dynamics | |
| this.updateUniverseDynamics(); | |
| // Render the scene | |
| this.renderer.render(this.scene, this.camera); | |
| } | |
| } | |
| updateUniverseDynamics() { | |
| const { breathing, growth, change, expansion } = this.universeDynamics; | |
| // Apply breathing animation | |
| if (this.scene && breathing !== 0) { | |
| const time = Date.now() / 1000; | |
| const breathCycle = Math.sin(time) * breathing * 0.1; | |
| const currentScale = this.scene.scale.x; | |
| const targetScale = 1 + breathCycle; | |
| // Smooth interpolation | |
| const lerpFactor = 0.05; | |
| const newScale = currentScale + (targetScale - currentScale) * lerpFactor; | |
| this.scene.scale.setScalar(newScale); | |
| } | |
| // Apply growth scaling | |
| if (this.scene && growth !== 0) { | |
| const growthFactor = 1 + growth * 0.01; | |
| this.scene.scale.multiplyScalar(growthFactor); | |
| } | |
| // Apply expansion | |
| if (this.scene && expansion !== 1.0) { | |
| const expandFactor = expansion; | |
| this.scene.scale.setScalar(expandFactor); | |
| } | |
| } | |
| handleAudioUniverseInfluence(data) { | |
| console.log('🌌 Universe responding to audio:', { | |
| harmony: data.harmony?.toFixed(2), | |
| resonance: data.resonance?.toFixed(2), | |
| bass: data.bass?.toFixed(2), | |
| treble: data.treble?.toFixed(2) | |
| }); | |
| // Update universe dynamics based on audio | |
| this.universeDynamics.breathing = data.bass || 0; | |
| this.universeDynamics.growth = data.treble * 0.5 || 0; | |
| this.universeDynamics.change = data.resonance * 0.3 || 0; | |
| // Apply environmental influence | |
| this.handleEnvironmentalInfluence(data); | |
| // Trigger visual effects based on harmony | |
| if (data.harmony > 0.8) { | |
| this.createHarmonyEffect(data.harmony); | |
| } | |
| // Trigger particle effects for high treble | |
| if (data.treble > 0.7) { | |
| this.createAudioParticleStorm(data.treble); | |
| } | |
| } | |
| handleEnvironmentalInfluence(influence) { | |
| // Adjust star brightness based on harmony | |
| if (influence.harmony > 0.7) { | |
| this.scene.traverse((object) => { | |
| if (object.isLight) { | |
| const intensity = object.intensity; | |
| const targetIntensity = intensity * (1.2 + influence.harmony * 0.3); | |
| object.intensity = targetIntensity; | |
| } | |
| // Also affect starfield brightness | |
| if (object.userData && object.userData.isStarfield && object.material) { | |
| const opacity = object.material.opacity; | |
| const targetOpacity = Math.min(1.0, opacity + influence.harmony * 0.2); | |
| object.material.opacity = targetOpacity; | |
| } | |
| }); | |
| } | |
| // Adjust scene scale based on resonance | |
| if (influence.resonance > 0.5) { | |
| this.universeDynamics.expansion = 1.0 + influence.resonance * 0.2; | |
| } else { | |
| this.universeDynamics.expansion = 1.0; | |
| } | |
| } | |
| createHarmonyEffect(harmony) { | |
| // Create harmony visualization effect | |
| const geometry = new THREE.SphereGeometry(20, 32, 32); | |
| const material = new THREE.MeshBasicMaterial({ | |
| color: 0x4f46e5, | |
| transparent: true, | |
| opacity: 0.1 * harmony, | |
| wireframe: true | |
| }); | |
| const sphere = new THREE.Mesh(geometry, material); | |
| sphere.position.set( | |
| (Math.random() - 0.5) * 100, | |
| (Math.random() - 0.5) * 100, | |
| (Math.random() - 0.5) * 100 | |
| ); | |
| if (this.scene) this.scene.add(sphere); | |
| // Animate and remove | |
| const animateSphere = () => { | |
| sphere.scale.multiplyScalar(1.02); | |
| sphere.material.opacity *= 0.98; | |
| sphere.rotation.x += 0.01; | |
| sphere.rotation.y += 0.01; | |
| if (sphere.material.opacity > 0.01) { | |
| requestAnimationFrame(animateSphere); | |
| } else { | |
| this.scene.remove(sphere); | |
| sphere.geometry.dispose(); | |
| sphere.material.dispose(); | |
| } | |
| }; | |
| animateSphere(); | |
| } | |
| createAudioParticleStorm(treble) { | |
| const particleCount = Math.floor(treble * 30); | |
| const particles = []; | |
| for (let i = 0; i < particleCount; i++) { | |
| const geometry = new THREE.SphereGeometry(0.5 + Math.random() * 1.5, 8, 8); | |
| const material = new THREE.MeshBasicMaterial({ | |
| color: new THREE.Color().setHSL(Math.random(), 0.8, 0.6), | |
| transparent: true, | |
| opacity: 0.8 | |
| }); | |
| const particle = new THREE.Mesh(geometry, material); | |
| particle.position.set( | |
| (Math.random() - 0.5) * 200, | |
| (Math.random() - 0.5) * 200, | |
| (Math.random() - 0.5) * 200 | |
| ); | |
| particle.userData = { | |
| velocity: new THREE.Vector3( | |
| (Math.random() - 0.5) * 0.5, | |
| (Math.random() - 0.5) * 0.5, | |
| (Math.random() - 0.5) * 0.5 | |
| ), | |
| lifetime: Date.now() + 4000 + Math.random() * 3000 | |
| }; | |
| if (this.scene) this.scene.add(particle); | |
| particles.push(particle); | |
| } | |
| // Clean up particles after their lifetime | |
| setTimeout(() => { | |
| particles.forEach(particle => { | |
| if (particle.parent) { | |
| particle.parent.remove(particle); | |
| particle.geometry.dispose(); | |
| particle.material.dispose(); | |
| } | |
| }); | |
| }, 8000); | |
| console.log(`🌌 Audio particle storm: ${particleCount} particles from treble ${treble.toFixed(2)}`); | |
| } | |
| } | |
| /** | |
| * The main application class that orchestrates all modules. | |
| */ | |
| class Application { | |
| constructor() { | |
| this.eventBus = new EventBus(); | |
| this.uiManager = new UIManager(this.eventBus); | |
| this.dataLogger = new DataLogger(this.eventBus); | |
| this.pge = new ProceduralGenerationEngine(this.eventBus, this.uiManager); | |
| this.particlizer = new Particlizer(this.eventBus); | |
| this.memoryEcho = new MemoryEcho(this.pge.scene, this.pge.camera, this.pge.listener, this.uiManager, this.eventBus); | |
| this.audioSystem = new AudioReactiveSystem(this.pge.scene, this.eventBus); | |
| this.sensoryManager = new SensoryInputManager(this.eventBus, this.uiManager); | |
| this.externalManager = new ExternalDataManager(this.eventBus); | |
| this.mlCore = new MachineLearningCore(this.eventBus, this.uiManager); | |
| this.aiAgent = new CosmicAwarenessAgent(this.eventBus, this.dataLogger); | |
| // Initialize Genesis Universe Engine for audio-driven reactions | |
| this.universeEngine = new GenesisUniverseEngine(); | |
| this.isStarted = false; | |
| this.currentMode = 'universe'; | |
| this.init(); | |
| } | |
| init() { | |
| this.eventBus.subscribe('ui:start', () => this.start()); | |
| this.eventBus.subscribe('ui:regenerate', (coords) => { this.switchMode('universe'); this.generate(coords); }); | |
| this.eventBus.subscribe('ui:spawnParticlizedFile', file => this.particlizer.particlize(file)); | |
| this.eventBus.subscribe('ui:spawnParticlizedVideo', () => this.particlizer.particlize('liveVideo')); | |
| this.eventBus.subscribe('ui:particlizeSelf', () => this.particlizeSelf()); | |
| this.eventBus.subscribe('ui:dataProvided', file => this.memoryEcho.loadData(file)); | |
| this.eventBus.subscribe('ui:enterEcho', () => this.memoryEcho.enter()); | |
| this.eventBus.subscribe('ui:downloadLedger', () => this.dataLogger.download()); | |
| this.eventBus.subscribe('pge:modeChange', (newMode) => this.switchMode(newMode)); | |
| this.eventBus.subscribe('pge:animate', delta => { | |
| if (this.currentMode === 'universe') this.audioSystem.update(delta); | |
| this.pge.specialObjects.forEach(obj => { if (obj.update) obj.update(delta); }); | |
| if (this.currentMode === 'echo') this.memoryEcho.update(delta); | |
| // Update universe dynamics for environmental response | |
| if (this.currentMode === 'universe' && this.pge) { | |
| this.pge.updateUniverseDynamics(delta); | |
| } | |
| }); | |
| } | |
| switchMode(mode) { | |
| if (this.currentMode === mode && mode !== 'universe') return; | |
| if (this.currentMode === 'echo') this.memoryEcho.exit(); | |
| this.currentMode = mode; | |
| const isUniverse = mode === 'universe'; | |
| this.pge.scene.traverse(child => { if (child.userData.isGenerated) child.visible = isUniverse; }); | |
| if (this.pge.localStarfield) this.pge.localStarfield.visible = isUniverse; | |
| if (this.audioSystem.particleSystem) this.audioSystem.particleSystem.visible = isUniverse; | |
| this.pge.specialObjects.forEach(obj => obj.mesh.visible = isUniverse); | |
| this.pge.orbitControls.enabled = true; | |
| if (mode !== 'echo') { this.pge.setViewMode('orbit'); this.uiManager.viewModeSelect.value = 'orbit'; } | |
| this.uiManager.log(`Switched to ${mode.toUpperCase()} mode.`, 'info'); | |
| } | |
| particlizeSelf() { | |
| this.uiManager.log("Analyzing universe for self-particlization...", "data"); | |
| const j3Data = this.pge.snapshotToJ3(); | |
| if(j3Data.length === 0) { this.uiManager.log("Nothing to particlize.", "warn"); return; } | |
| this.pge.cleanupScene(); | |
| this.particlizer.particlize(j3Data); | |
| this.uiManager.log("Self-particlization complete. The previous universe has dissolved into a memory echo.", "success"); | |
| } | |
| async start() { | |
| if (this.isStarted) return; | |
| this.isStarted = true; | |
| this.uiManager.hideBlocker(); | |
| // BUG FIX: The animation loop MUST start after the application is fully constructed | |
| // but before any async operations, to prevent the `scene` from being undefined. | |
| this.pge.startAnimationLoop(); | |
| // Initialize Genesis Universe Engine for audio-driven reactions | |
| try { | |
| await this.universeEngine.initialize(); | |
| console.log('✅ Genesis Universe Engine started successfully'); | |
| } catch (err) { | |
| console.error('❌ Failed to initialize Genesis Universe Engine:', err); | |
| } | |
| try { | |
| await this.sensoryManager.initializeAll(); | |
| } catch (err) { | |
| this.uiManager.log("One or more sensors failed to initialize. Continuing with available data.", "warn"); | |
| } | |
| // The timeout is to allow sensors to provide a first reading before generation | |
| setTimeout(() => this.generate(), 1000); | |
| this.eventBus.publish('status:update', 'EXPLORING'); | |
| } | |
| generate(coords = null){ | |
| this.aiAgent.generateSeed(coords); | |
| this.eventBus.publish('status:update', 'GENERATING'); | |
| } | |
| } | |
| // Genesis X Wallet Integration System | |
| let walletState = { | |
| connected: false, | |
| loading: false, | |
| wallet: null, | |
| publicKey: null, | |
| balance: 0 | |
| }; | |
| function initWallet() { | |
| console.log('🔗 Initializing Genesis X Wallet System...'); | |
| // Get UI elements | |
| const connectPhantomBtn = document.getElementById('connect-phantom-btn'); | |
| const connectManualBtn = document.getElementById('connect-manual-btn'); | |
| const disconnectWalletBtn = document.getElementById('disconnect-wallet-btn'); | |
| const refreshBalanceBtn = document.getElementById('refresh-balance-btn'); | |
| // Set up event listeners | |
| if (connectPhantomBtn) { | |
| connectPhantomBtn.addEventListener('click', connectPhantomWallet); | |
| } | |
| if (connectManualBtn) { | |
| connectManualBtn.addEventListener('click', connectManualWallet); | |
| } | |
| if (disconnectWalletBtn) { | |
| disconnectWalletBtn.addEventListener('click', disconnectWallet); | |
| } | |
| if (refreshBalanceBtn) { | |
| refreshBalanceBtn.addEventListener('click', updateBalance); | |
| } | |
| // Check for existing Phantom connection | |
| checkExistingConnection(); | |
| console.log('✅ Wallet system initialized'); | |
| } | |
| async function checkExistingConnection() { | |
| try { | |
| if (window.solana && window.solana.isPhantom && window.solana.isConnected) { | |
| console.log('✅ Existing Phantom connection detected'); | |
| await connectPhantomWallet(); | |
| } | |
| } catch (error) { | |
| // User hasn't connected before, that's fine | |
| } | |
| } | |
| function setWalletLoading(loading) { | |
| walletState.loading = loading; | |
| updateWalletUI(); | |
| } | |
| function setWalletError(message) { | |
| console.error('❌ Wallet Error:', message); | |
| alert(`Wallet Error: ${message}`); | |
| setWalletLoading(false); | |
| } | |
| async function connectPhantomWallet() { | |
| try { | |
| setWalletLoading(true); | |
| console.log('🦄 Connecting to Phantom wallet...'); | |
| if (!window.solana || !window.solana.isPhantom) { | |
| throw new Error('Phantom wallet not found. Please install Phantom wallet extension.'); | |
| } | |
| const response = await window.solana.connect(); | |
| walletState.wallet = window.solana; | |
| walletState.publicKey = response.publicKey.toString(); | |
| walletState.connected = true; | |
| console.log('✅ Phantom wallet connected:', walletState.publicKey); | |
| // Load balance | |
| updateBalance().catch(() => { | |
| console.warn('Could not load balance for Phantom wallet'); | |
| walletState.balance = 0; | |
| updateWalletUI(); | |
| }); | |
| } catch (error) { | |
| console.error('❌ Phantom wallet connection failed:', error); | |
| setWalletError(error.message); | |
| walletState.connected = false; | |
| walletState.wallet = null; | |
| walletState.publicKey = null; | |
| updateWalletUI(); | |
| } finally { | |
| setWalletLoading(false); | |
| } | |
| } | |
| async function connectManualWallet() { | |
| try { | |
| const address = prompt('Enter your Solana wallet address:'); | |
| if (!address) return; | |
| // Basic validation | |
| if (address.length !== 44 && address.length !== 43) { | |
| throw new Error('Invalid Solana address length'); | |
| } | |
| walletState.wallet = { publicKey: address }; | |
| walletState.publicKey = address; | |
| walletState.connected = true; | |
| console.log('✅ Manual wallet connected:', address); | |
| // Load balance (may fail for manual wallets) | |
| updateBalance().catch(() => { | |
| console.warn('Could not load balance for manual wallet'); | |
| walletState.balance = 0; | |
| updateWalletUI(); | |
| }); | |
| } catch (error) { | |
| setWalletError('Invalid Solana wallet address'); | |
| } | |
| } | |
| async function disconnectWallet() { | |
| try { | |
| if (window.solana && walletState.connected) { | |
| await window.solana.disconnect(); | |
| } | |
| walletState.connected = false; | |
| walletState.loading = false; | |
| walletState.wallet = null; | |
| walletState.publicKey = null; | |
| walletState.balance = 0; | |
| updateWalletUI(); | |
| console.log('🚪 Wallet disconnected'); | |
| } catch (error) { | |
| console.error('❌ Wallet disconnection failed:', error); | |
| } | |
| } | |
| async function updateBalance() { | |
| if (!walletState.connected || !walletState.wallet) { | |
| console.log('💰 Balance check skipped: wallet not connected'); | |
| return; | |
| } | |
| try { | |
| console.log('💰 Starting balance check...'); | |
| // Use the getMobiusBalance function from the SolanaIntegration instance | |
| if (window.solanaIntegration && window.solanaIntegration.getMobiusBalanceGlobal) { | |
| console.log('💰 Using SolanaIntegration for balance check'); | |
| walletState.balance = await window.solanaIntegration.getMobiusBalanceGlobal(); | |
| console.log('💰 Balance from SolanaIntegration:', walletState.balance); | |
| } else if (window.getMobiusBalance) { | |
| console.log('💰 Using global getMobiusBalance function'); | |
| walletState.balance = await window.getMobiusBalance(walletState.wallet); | |
| console.log('💰 Balance from global function:', walletState.balance); | |
| } else { | |
| console.log('💰 No balance function available, checking real balance'); | |
| // Try to get real balance, show 0 if not available | |
| try { | |
| const realBalance = await window.getMobiusBalance(walletState.wallet); | |
| walletState.balance = realBalance || 0; | |
| console.log('💰 Real balance checked:', walletState.balance); | |
| } catch (error) { | |
| console.log('💰 Real balance check failed, showing 0'); | |
| walletState.balance = 0; | |
| } | |
| } | |
| updateWalletUI(); | |
| console.log('💰 Mobius balance updated successfully:', walletState.balance); | |
| } catch (error) { | |
| console.error('❌ Failed to update balance:', error); | |
| console.log('💰 Using 0 balance due to error - no Mobius tokens found'); | |
| walletState.balance = 0; // Show 0 for real balance checking | |
| updateWalletUI(); | |
| } | |
| } | |
| function updateWalletUI() { | |
| const statusIndicator = document.getElementById('wallet-status-indicator'); | |
| const disconnectedSection = document.getElementById('wallet-disconnected'); | |
| const connectedSection = document.getElementById('wallet-connected'); | |
| const loadingSection = document.getElementById('wallet-loading'); | |
| const addressElement = document.getElementById('wallet-address-display'); | |
| const balanceElement = document.getElementById('mobius-balance-display'); | |
| if (statusIndicator) { | |
| statusIndicator.className = `w-3 h-3 rounded-full ${walletState.connected ? 'bg-green-500' : 'bg-red-500'}`; | |
| } | |
| if (disconnectedSection) { | |
| disconnectedSection.style.display = walletState.connected || walletState.loading ? 'none' : 'block'; | |
| } | |
| if (connectedSection) { | |
| connectedSection.style.display = !walletState.connected || walletState.loading ? 'none' : 'block'; | |
| } | |
| if (loadingSection) { | |
| loadingSection.style.display = walletState.loading ? 'block' : 'none'; | |
| } | |
| if (addressElement && walletState.publicKey) { | |
| addressElement.textContent = `${walletState.publicKey.slice(0, 8)}...${walletState.publicKey.slice(-8)}`; | |
| } | |
| if (balanceElement) { | |
| balanceElement.textContent = walletState.balance.toFixed(2); | |
| } | |
| } | |
| window.addEventListener('DOMContentLoaded', () => { | |
| try { | |
| // Initialize wallet functionality first | |
| initWallet(); | |
| window.app = new Application(); | |
| } catch (error) { | |
| console.error("Fatal error during application startup:", error); | |
| const blocker = document.getElementById('blocker'); | |
| const loadingMsg = document.getElementById('loading-message'); | |
| if (blocker && loadingMsg) { | |
| blocker.style.display = 'flex'; | |
| blocker.innerHTML = `<div class="max-w-2xl px-4 text-red-400"> | |
| <h1 class="text-4xl font-bold">Fatal Engine Error</h1> | |
| <p class="mt-4">A critical error occurred during initialization. The engine cannot start. Please check the console for details.</p> | |
| <p class="mt-2 font-mono text-sm">${error.message}</p> | |
| </div>`; | |
| } | |
| } | |
| }); | |
| // ============================================================================ | |
| // 🌌 ADVANCED ENVIRONMENTAL TOKENIZATION SYSTEM | |
| // ============================================================================ | |
| // Unified Physics Constants | |
| const PHYSICS_CONSTANTS = { | |
| PLANCK_CONSTANT: 6.62607015e-34, // J⋅s | |
| SPEED_OF_LIGHT: 299792458, // m/s | |
| BOLTZMANN_CONSTANT: 1.380649e-23, // J/K | |
| GOLDEN_RATIO: 1.618033988749895, | |
| QUANTUM_RESONANCE: 0.5, | |
| ENVIRONMENTAL_ENTROPY: 0.1 | |
| }; | |
| // Tokenization Engine Class | |
| class EnvironmentalTokenizationEngine { | |
| constructor() { | |
| this.environmentalData = { | |
| sound: 0, | |
| light: 0, | |
| temperature: 20, | |
| humidity: 50, | |
| timestamp: Date.now(), | |
| location: null | |
| }; | |
| this.tokenMetrics = { | |
| frequencyValue: 0, | |
| tokenValue: 0, | |
| marketPrice: 0, | |
| dataQuality: 0, | |
| rarityScore: 0 | |
| }; | |
| this.portfolio = { | |
| genesisTokens: 0, | |
| frequencyNFTs: 0, | |
| dataValue: 0 | |
| }; | |
| this.tradingHistory = []; | |
| this.marketPrices = new Map(); | |
| this.initializeUnifiedPhysics(); | |
| this.startRealTimeTokenization(); | |
| } | |
| // Initialize Unified Physics Formula System | |
| initializeUnifiedPhysics() { | |
| console.log('🔬 Initializing Unified Physics Formula System...'); | |
| // Display the governing formula | |
| this.updatePhysicsFormula(); | |
| // Set up market prices for different environmental data types | |
| this.marketPrices.set('sound', { basePrice: 0.01, volatility: 0.1 }); | |
| this.marketPrices.set('light', { basePrice: 0.015, volatility: 0.08 }); | |
| this.marketPrices.set('temperature', { basePrice: 0.012, volatility: 0.12 }); | |
| this.marketPrices.set('humidity', { basePrice: 0.008, volatility: 0.15 }); | |
| this.marketPrices.set('combined', { basePrice: 0.025, volatility: 0.2 }); | |
| console.log('✅ Unified Physics Formula System initialized'); | |
| } | |
| // Update the physics formula display with enhanced formatting | |
| updatePhysicsFormula() { | |
| try { | |
| const formulaElement = document.getElementById('physics-formula'); | |
| if (formulaElement) { | |
| const psi = this.calculateQuantumState(); | |
| const environmentalFactor = this.calculateEnvironmentalFactor(); | |
| const soundFreq = this.environmentalData.sound || 440; | |
| const lightFreq = this.tokenMetrics.frequencyValue || 400e12; | |
| // Enhanced formula display with current values | |
| const formulaHTML = ` | |
| <div style="font-size: 11px; line-height: 1.2;"> | |
| <div style="color: #60a5fa;">E = hν • (1 + ψ) • Σ(εᵢ)</div> | |
| <div style="color: #a78bfa; font-size: 9px; margin-top: 2px;"> | |
| ${soundFreq.toFixed(0)}Hz → ${(lightFreq / 1e12).toFixed(1)}THz | |
| </div> | |
| <div style="color: #34d399; font-size: 9px;"> | |
| ψ=${psi.toFixed(3)} ε=${environmentalFactor.toFixed(3)} | |
| </div> | |
| </div> | |
| `; | |
| formulaElement.innerHTML = formulaHTML; | |
| // Add visual feedback based on quantum state | |
| const intensity = Math.min(1, Math.max(0, psi)); | |
| formulaElement.style.textShadow = `0 0 ${intensity * 5}px rgba(96, 165, 250, ${intensity * 0.5})`; | |
| } | |
| } catch (error) { | |
| console.error('❌ Error updating physics formula:', error); | |
| const formulaElement = document.getElementById('physics-formula'); | |
| if (formulaElement) { | |
| formulaElement.textContent = 'E = hν • (1 + ψ) • Σ(εᵢ)'; | |
| } | |
| } | |
| } | |
| // Calculate quantum state based on environmental data | |
| calculateQuantumState() { | |
| const { sound, light, temperature, humidity } = this.environmentalData; | |
| const quantumEntropy = Math.sin(sound / 440.0 * 2 * Math.PI) * | |
| Math.cos(light / 100.0 * Math.PI) * | |
| Math.tanh(temperature / 30.0); | |
| return PHYSICS_CONSTANTS.QUANTUM_RESONANCE + quantumEntropy * 0.3; | |
| } | |
| // Calculate environmental factor | |
| calculateEnvironmentalFactor() { | |
| const { sound, light, temperature, humidity } = this.environmentalData; | |
| const timeFactor = Math.sin(Date.now() / 1000 / 86400 * 2 * Math.PI); // Daily cycle | |
| const locationFactor = this.environmentalData.location ? 1.2 : 1.0; | |
| return (sound + light + temperature + humidity) / 400.0 * | |
| (1 + timeFactor * 0.1) * locationFactor; | |
| } | |
| // Unified Physics Formula: Sound to Light Conversion | |
| convertSoundToLight(soundFrequency) { | |
| // E = hν (Energy = Planck's constant × frequency) | |
| const energy = PHYSICS_CONSTANTS.PLANCK_CONSTANT * soundFrequency; | |
| // Convert to light frequency using quantum resonance | |
| const quantumState = this.calculateQuantumState(); | |
| const environmentalFactor = this.calculateEnvironmentalFactor(); | |
| // Unified formula: Light Frequency = Energy / (h × quantum_factor × environmental_factor) | |
| const lightFrequency = energy / (PHYSICS_CONSTANTS.PLANCK_CONSTANT * | |
| (1 + quantumState) * environmentalFactor); | |
| // Convert to visible light spectrum (400-700 THz) | |
| const visibleLightHz = 400e12 + (lightFrequency % (300e12)); // 400-700 THz range | |
| return { | |
| soundFrequency, | |
| lightFrequency: visibleLightHz, | |
| energy, | |
| quantumState, | |
| environmentalFactor, | |
| wavelength: PHYSICS_CONSTANTS.SPEED_OF_LIGHT / visibleLightHz | |
| }; | |
| } | |
| // Tokenize environmental frequency data with enhanced physics | |
| tokenizeFrequencyData(soundData, lightData) { | |
| try { | |
| const conversion = this.convertSoundToLight(soundData); | |
| // Enhanced token value calculation with multiple factors | |
| const energyValue = this.calculateEnergyValue(conversion.energy); | |
| const frequencyValue = this.calculateFrequencyValue(conversion.lightFrequency); | |
| const quantumValue = this.calculateQuantumValue(conversion.quantumState); | |
| const environmentalValue = this.calculateEnvironmentalValue(conversion.environmentalFactor); | |
| const temporalValue = this.calculateTemporalValue(); | |
| const rarityMultiplier = this.calculateDataRarity(soundData, lightData); | |
| // Combine all factors using unified physics formula | |
| const baseTokenValue = (energyValue + frequencyValue) * quantumValue * environmentalValue * temporalValue; | |
| const tokenValue = baseTokenValue * rarityMultiplier; | |
| // Calculate market price with advanced volatility model | |
| const marketPrice = this.calculateMarketPrice(tokenValue, soundData, lightData); | |
| // Calculate comprehensive data quality score | |
| const dataQuality = this.calculateDataQuality(soundData, lightData); | |
| // Calculate additional metrics | |
| const stabilityScore = this.calculateStabilityScore(soundData, lightData); | |
| const uniquenessScore = this.calculateUniquenessScore(soundData, lightData); | |
| console.log(`🪙 Tokenized ${soundData.toFixed(1)}Hz → ${conversion.lightFrequency.toExponential(2)}Hz:`, { | |
| tokenValue: tokenValue.toFixed(6), | |
| marketPrice: marketPrice.toFixed(4), | |
| quality: dataQuality.toFixed(1) + '%', | |
| rarity: rarityMultiplier.toFixed(2) + 'x' | |
| }); | |
| return { | |
| soundFrequency: soundData, | |
| lightFrequency: conversion.lightFrequency, | |
| tokenValue, | |
| marketPrice, | |
| dataQuality, | |
| rarityScore: rarityMultiplier, | |
| stabilityScore, | |
| uniquenessScore, | |
| timestamp: Date.now(), | |
| physicsData: conversion, | |
| energyValue, | |
| frequencyValue, | |
| quantumValue, | |
| environmentalValue, | |
| temporalValue | |
| }; | |
| } catch (error) { | |
| console.error('❌ Error in tokenization:', error); | |
| // Return safe fallback values | |
| return { | |
| soundFrequency: soundData, | |
| lightFrequency: 400e12, | |
| tokenValue: 0.001, | |
| marketPrice: 0.0001, | |
| dataQuality: 50, | |
| rarityScore: 1.0, | |
| stabilityScore: 0.5, | |
| uniquenessScore: 0.5, | |
| timestamp: Date.now(), | |
| physicsData: null, | |
| error: error.message | |
| }; | |
| } | |
| } | |
| // Calculate energy-based token value | |
| calculateEnergyValue(energy) { | |
| // Convert energy to token scale with logarithmic scaling | |
| const logEnergy = Math.log10(Math.max(energy, 1e-40)); | |
| return Math.max(0.001, logEnergy * 0.1); | |
| } | |
| // Calculate frequency-based token value | |
| calculateFrequencyValue(lightFrequency) { | |
| // Value increases with frequency in visible spectrum | |
| const thz = lightFrequency / 1e12; | |
| const optimalFrequency = 550; // Green light peak | |
| const distanceFromOptimal = Math.abs(thz - optimalFrequency); | |
| const frequencyScore = Math.max(0, 1 - distanceFromOptimal / 300); | |
| return frequencyScore * 0.01; | |
| } | |
| // Calculate quantum state value | |
| calculateQuantumValue(quantumState) { | |
| // Higher quantum coherence increases value | |
| return 1 + quantumState * 0.5; | |
| } | |
| // Calculate environmental factor value | |
| calculateEnvironmentalValue(environmentalFactor) { | |
| // More complex environmental interactions increase value | |
| return Math.max(0.5, Math.min(2.0, environmentalFactor)); | |
| } | |
| // Calculate temporal value (time-based pricing) | |
| calculateTemporalValue() { | |
| const hour = new Date().getHours(); | |
| // Peak value during dawn/dusk and nighttime | |
| if (hour >= 5 && hour <= 7) return 1.3; // Dawn | |
| if (hour >= 17 && hour <= 19) return 1.3; // Dusk | |
| if (hour >= 20 || hour <= 4) return 1.2; // Night | |
| return 1.0; // Day | |
| } | |
| // Enhanced market price calculation | |
| calculateMarketPrice(tokenValue, soundData, lightData) { | |
| const marketData = this.marketPrices.get('combined') || { basePrice: 0.01, volatility: 0.1 }; | |
| // Advanced volatility based on data characteristics | |
| let volatility = marketData.volatility; | |
| // Higher volatility for extreme values | |
| if (soundData < 100 || soundData > 1000) volatility *= 1.5; | |
| if (lightData < 20 || lightData > 80) volatility *= 1.3; | |
| // Time-based volatility | |
| const hour = new Date().getHours(); | |
| if (hour >= 9 && hour <= 17) volatility *= 0.8; // Lower during business hours | |
| const volatilityFactor = 1 + (Math.random() - 0.5) * volatility; | |
| const marketPrice = marketData.basePrice * tokenValue * volatilityFactor; | |
| // Ensure minimum price | |
| return Math.max(0.000001, marketPrice); | |
| } | |
| // Calculate stability score | |
| calculateStabilityScore(sound, light) { | |
| // Measure how stable the environmental conditions are | |
| const soundVariance = Math.abs(sound - 440) / 440; // Deviation from standard | |
| const lightVariance = Math.abs(light - 50) / 50; | |
| const stability = 1 - (soundVariance + lightVariance) / 2; | |
| return Math.max(0, Math.min(1, stability)); | |
| } | |
| // Calculate uniqueness score | |
| calculateUniquenessScore(sound, light) { | |
| // Measure how unique the current conditions are | |
| let uniqueness = 0; | |
| // Rare frequencies | |
| if (sound < 100 || sound > 1000) uniqueness += 0.3; | |
| if (sound > 1500) uniqueness += 0.2; | |
| // Extreme light conditions | |
| if (light < 10 || light > 90) uniqueness += 0.3; | |
| if (light < 5 || light > 95) uniqueness += 0.2; | |
| // Time-based uniqueness | |
| const minute = new Date().getMinutes(); | |
| if (minute % 10 === 0) uniqueness += 0.1; // On the hour | |
| return Math.min(1, uniqueness); | |
| } | |
| // Calculate data rarity based on environmental conditions | |
| calculateDataRarity(sound, light) { | |
| let rarityScore = 1.0; | |
| // Rare sound frequencies (< 100 Hz or > 1000 Hz) | |
| if (sound < 100 || sound > 1000) rarityScore *= 1.5; | |
| // Rare light conditions (very low or very high) | |
| if (light < 20 || light > 80) rarityScore *= 1.3; | |
| // Time-based rarity (night time data is rarer) | |
| const hour = new Date().getHours(); | |
| if (hour >= 22 || hour <= 5) rarityScore *= 1.2; | |
| // Location-based rarity | |
| if (this.environmentalData.location) rarityScore *= 1.1; | |
| return Math.min(rarityScore, 5.0); // Cap at 5x multiplier | |
| } | |
| // Calculate data quality score | |
| calculateDataQuality(sound, light) { | |
| let qualityScore = 0; | |
| // Sound quality (clear signal) | |
| if (sound > 10 && sound < 2000) qualityScore += 25; | |
| // Light quality (good illumination) | |
| if (light > 30 && light < 70) qualityScore += 25; | |
| // Environmental data completeness | |
| if (this.environmentalData.temperature) qualityScore += 15; | |
| if (this.environmentalData.humidity) qualityScore += 15; | |
| if (this.environmentalData.location) qualityScore += 20; | |
| return Math.min(qualityScore, 100); | |
| } | |
| // Start real-time tokenization | |
| startRealTimeTokenization() { | |
| console.log('⚡ Starting real-time environmental tokenization...'); | |
| // Update environmental data every second | |
| setInterval(() => { | |
| this.updateEnvironmentalData(); | |
| this.updateTokenMetrics(); | |
| this.updateUI(); | |
| }, 1000); | |
| // Update market prices every 30 seconds | |
| setInterval(() => { | |
| this.updateMarketPrices(); | |
| }, 30000); | |
| } | |
| // Update environmental data with multiple data sources | |
| updateEnvironmentalData() { | |
| try { | |
| let dataSource = 'simulation'; | |
| let envData = null; | |
| // Try multiple data sources in order of preference | |
| if (window.engine && window.engine.environmental && typeof window.engine.environmental.getData === 'function') { | |
| // Primary: Engine environmental system | |
| envData = window.engine.environmental.getData(); | |
| dataSource = 'engine'; | |
| } else if (window.cosmicAwarenessAgent && window.cosmicAwarenessAgent.latestData) { | |
| // Secondary: Cosmic awareness agent | |
| envData = window.cosmicAwarenessAgent.latestData; | |
| dataSource = 'cosmic_agent'; | |
| } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { | |
| // Tertiary: Try to get real audio data (if permissions granted) | |
| this.initializeAudioCapture(); | |
| dataSource = 'audio_fallback'; | |
| } | |
| if (envData) { | |
| // Process and normalize the environmental data | |
| this.environmentalData = { | |
| sound: this.normalizeValue(envData.sound || envData.audio || 440, 20, 2000, 440), | |
| light: this.normalizeValue(envData.light || envData.lux || 50, 0, 100, 50), | |
| temperature: this.normalizeValue(envData.temperature || 20, -10, 50, 20), | |
| humidity: this.normalizeValue(envData.humidity || 50, 0, 100, 50), | |
| timestamp: Date.now(), | |
| location: this.extractLocationData(envData), | |
| dataSource: dataSource | |
| }; | |
| } else { | |
| // Enhanced fallback with more realistic simulation | |
| const time = Date.now() / 1000; | |
| this.environmentalData = { | |
| sound: 220 + Math.sin(time * 0.5) * 100 + Math.random() * 50, | |
| light: 50 + Math.cos(time * 0.3) * 30 + Math.sin(time * 0.1) * 10, | |
| temperature: 20 + Math.sin(time * 0.2) * 8 + Math.cos(time * 0.15) * 5, | |
| humidity: 50 + Math.cos(time * 0.25) * 15 + Math.sin(time * 0.12) * 8, | |
| timestamp: Date.now(), | |
| location: null, | |
| dataSource: 'simulated' | |
| }; | |
| } | |
| console.log(`🌍 Environmental data updated from ${dataSource}:`, { | |
| sound: this.environmentalData.sound.toFixed(1), | |
| light: this.environmentalData.light.toFixed(1), | |
| temperature: this.environmentalData.temperature.toFixed(1), | |
| humidity: this.environmentalData.humidity.toFixed(1) | |
| }); | |
| } catch (error) { | |
| console.error('❌ Error updating environmental data:', error); | |
| // Emergency fallback | |
| this.environmentalData = { | |
| sound: 440, | |
| light: 50, | |
| temperature: 20, | |
| humidity: 50, | |
| timestamp: Date.now(), | |
| location: null, | |
| dataSource: 'error_fallback' | |
| }; | |
| } | |
| } | |
| // Normalize values to ensure they're within reasonable ranges | |
| normalizeValue(value, min, max, defaultValue) { | |
| if (typeof value !== 'number' || isNaN(value)) { | |
| return defaultValue; | |
| } | |
| return Math.max(min, Math.min(max, value)); | |
| } | |
| // Extract location data from various formats | |
| extractLocationData(envData) { | |
| if (envData.location) return envData.location; | |
| if (envData.location_lat && envData.location_lon) { | |
| return { | |
| latitude: envData.location_lat, | |
| longitude: envData.location_lon | |
| }; | |
| } | |
| if (envData.lat && envData.lon) { | |
| return { | |
| latitude: envData.lat, | |
| longitude: envData.lon | |
| }; | |
| } | |
| return null; | |
| } | |
| // Initialize audio capture for real environmental data | |
| async initializeAudioCapture() { | |
| try { | |
| if (this.audioContext) return; // Already initialized | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| const analyser = this.audioContext.createAnalyser(); | |
| const microphone = this.audioContext.createMediaStreamSource(stream); | |
| analyser.fftSize = 256; | |
| analyser.smoothingTimeConstant = 0.8; | |
| microphone.connect(analyser); | |
| const dataArray = new Uint8Array(analyser.frequencyBinCount); | |
| // Update audio data periodically | |
| setInterval(() => { | |
| analyser.getByteFrequencyData(dataArray); | |
| const average = dataArray.reduce((sum, value) => sum + value, 0) / dataArray.length; | |
| this.environmentalData.sound = average * 10; // Scale to reasonable range | |
| }, 100); | |
| } catch (error) { | |
| console.log('🎤 Audio capture not available:', error.message); | |
| } | |
| } | |
| // Update token metrics | |
| updateTokenMetrics() { | |
| const tokenData = this.tokenizeFrequencyData( | |
| this.environmentalData.sound, | |
| this.environmentalData.light | |
| ); | |
| this.tokenMetrics = { | |
| frequencyValue: tokenData.lightFrequency, | |
| tokenValue: tokenData.tokenValue, | |
| marketPrice: tokenData.marketPrice, | |
| dataQuality: tokenData.dataQuality, | |
| rarityScore: tokenData.rarityScore | |
| }; | |
| } | |
| // Update market prices dynamically | |
| updateMarketPrices() { | |
| this.marketPrices.forEach((data, key) => { | |
| const volatility = (Math.random() - 0.5) * data.volatility; | |
| data.basePrice *= (1 + volatility * 0.1); // Small random changes | |
| }); | |
| } | |
| // Update UI elements with comprehensive display | |
| updateUI() { | |
| try { | |
| // Update physics formula display | |
| this.updatePhysicsFormula(); | |
| // Update frequency conversion display with enhanced formatting | |
| const conversionElement = document.getElementById('sound-to-light-conversion'); | |
| if (conversionElement) { | |
| const thz = (this.tokenMetrics.frequencyValue / 1e12); | |
| if (thz > 1000) { | |
| conversionElement.textContent = `${(thz / 1000).toFixed(2)} PHz`; | |
| } else if (thz > 1) { | |
| conversionElement.textContent = `${thz.toFixed(2)} THz`; | |
| } else { | |
| conversionElement.textContent = `${(thz * 1000).toFixed(2)} GHz`; | |
| } | |
| } | |
| // Update token value with proper formatting | |
| const tokenValueElement = document.getElementById('token-value'); | |
| if (tokenValueElement) { | |
| const value = this.tokenMetrics.tokenValue; | |
| if (value < 0.0001) { | |
| tokenValueElement.textContent = `${(value * 1000000).toFixed(2)} μGEN`; | |
| } else if (value < 0.01) { | |
| tokenValueElement.textContent = `${(value * 1000).toFixed(2)} mGEN`; | |
| } else { | |
| tokenValueElement.textContent = `${value.toFixed(4)} GEN`; | |
| } | |
| } | |
| // Update market price with USD formatting | |
| const marketPriceElement = document.getElementById('market-price'); | |
| if (marketPriceElement) { | |
| const price = this.tokenMetrics.marketPrice; | |
| if (price < 0.000001) { | |
| marketPriceElement.textContent = `$${(price * 1000000).toFixed(2)} μ`; | |
| } else if (price < 0.001) { | |
| marketPriceElement.textContent = `$${(price * 1000).toFixed(2)} m`; | |
| } else { | |
| marketPriceElement.textContent = `$${price.toFixed(4)}`; | |
| } | |
| } | |
| // Update data quality with color coding | |
| const qualityElement = document.getElementById('data-quality'); | |
| if (qualityElement) { | |
| const quality = this.tokenMetrics.dataQuality; | |
| qualityElement.textContent = `${quality.toFixed(1)}%`; | |
| qualityElement.style.color = quality > 80 ? '#10b981' : quality > 60 ? '#f59e0b' : '#ef4444'; | |
| } | |
| // Update portfolio | |
| this.updatePortfolio(); | |
| // Update data source indicator | |
| this.updateDataSourceIndicator(); | |
| // Update environmental display | |
| this.updateEnvironmentalDisplay(); | |
| } catch (error) { | |
| console.error('❌ Error updating UI:', error); | |
| } | |
| } | |
| // Update data source indicator | |
| updateDataSourceIndicator() { | |
| const sourceElement = document.getElementById('data-source-indicator'); | |
| if (sourceElement) { | |
| const source = this.environmentalData.dataSource || 'unknown'; | |
| const colors = { | |
| 'engine': '#10b981', | |
| 'cosmic_agent': '#3b82f6', | |
| 'audio_fallback': '#8b5cf6', | |
| 'simulated': '#f59e0b', | |
| 'error_fallback': '#ef4444' | |
| }; | |
| sourceElement.textContent = source.toUpperCase(); | |
| sourceElement.style.color = colors[source] || '#6b7280'; | |
| } | |
| } | |
| // Update environmental display with real-time data | |
| updateEnvironmentalDisplay() { | |
| // Update environmental data display elements with proper units | |
| const envElements = { | |
| 'env-sound': { value: this.environmentalData.sound, unit: 'Hz', multiplier: 1 }, | |
| 'env-light': { value: this.environmentalData.light, unit: '%', multiplier: 1 }, | |
| 'env-temperature': { value: this.environmentalData.temperature, unit: '°C', multiplier: 1 }, | |
| 'env-humidity': { value: this.environmentalData.humidity, unit: '%', multiplier: 1 } | |
| }; | |
| Object.entries(envElements).forEach(([elementId, config]) => { | |
| const element = document.getElementById(elementId); | |
| if (element && typeof config.value === 'number') { | |
| const displayValue = config.value * config.multiplier; | |
| element.textContent = `${displayValue.toFixed(1)}${config.unit}`; | |
| } | |
| }); | |
| // Update environmental status with visual indicators | |
| const statusElement = document.getElementById('environmental-status'); | |
| if (statusElement) { | |
| const status = this.getEnvironmentalStatus(); | |
| statusElement.textContent = status.text; | |
| statusElement.style.color = status.color; | |
| // Add pulsing effect for dynamic states | |
| if (status.text === 'DYNAMIC' || status.text === 'ACTIVE') { | |
| statusElement.style.animation = 'pulse 2s infinite'; | |
| } else { | |
| statusElement.style.animation = 'none'; | |
| } | |
| } | |
| } | |
| // Get environmental status based on current conditions | |
| getEnvironmentalStatus() { | |
| const { sound, light, temperature, humidity } = this.environmentalData; | |
| if (sound > 80 || light < 20 || light > 80) { | |
| return { text: 'DYNAMIC', color: '#f59e0b' }; | |
| } else if (temperature < 10 || temperature > 30) { | |
| return { text: 'EXTREME', color: '#ef4444' }; | |
| } else if (humidity > 70 || sound > 60) { | |
| return { text: 'ACTIVE', color: '#3b82f6' }; | |
| } else { | |
| return { text: 'STABLE', color: '#10b981' }; | |
| } | |
| } | |
| // Update portfolio display | |
| updatePortfolio() { | |
| const genesisBalanceElement = document.getElementById('genesis-token-balance'); | |
| if (genesisBalanceElement) { | |
| genesisBalanceElement.textContent = this.portfolio.genesisTokens.toFixed(2); | |
| } | |
| const nftCountElement = document.getElementById('frequency-nft-count'); | |
| if (nftCountElement) { | |
| nftCountElement.textContent = this.portfolio.frequencyNFTs.toString(); | |
| } | |
| const dataValueElement = document.getElementById('data-value'); | |
| if (dataValueElement) { | |
| const totalValue = this.portfolio.genesisTokens * this.tokenMetrics.marketPrice; | |
| dataValueElement.textContent = `$${totalValue.toFixed(2)}`; | |
| } | |
| } | |
| // Mint frequency token | |
| async mintFrequencyToken() { | |
| try { | |
| console.log('🪙 Minting frequency token...'); | |
| const tokenData = this.tokenizeFrequencyData( | |
| this.environmentalData.sound, | |
| this.environmentalData.light | |
| ); | |
| // Create NFT metadata | |
| const metadata = { | |
| name: `Genesis Frequency Token #${Date.now()}`, | |
| description: `Environmental frequency data converted to light spectrum using unified physics formula. Sound: ${this.environmentalData.sound.toFixed(1)}Hz → Light: ${(tokenData.lightFrequency / 1e12).toFixed(3)}THz`, | |
| image: await this.generateTokenImage(tokenData), | |
| attributes: [ | |
| { | |
| trait_type: 'Sound Frequency', | |
| value: this.environmentalData.sound.toFixed(1), | |
| display_type: 'number' | |
| }, | |
| { | |
| trait_type: 'Light Frequency', | |
| value: (tokenData.lightFrequency / 1e12).toFixed(3), | |
| display_type: 'number' | |
| }, | |
| { | |
| trait_type: 'Quantum State', | |
| value: tokenData.physicsData.quantumState.toFixed(3), | |
| display_type: 'number' | |
| }, | |
| { | |
| trait_type: 'Data Quality', | |
| value: tokenData.dataQuality.toFixed(1), | |
| display_type: 'number' | |
| }, | |
| { | |
| trait_type: 'Rarity Score', | |
| value: tokenData.rarityScore.toFixed(2), | |
| display_type: 'number' | |
| } | |
| ] | |
| }; | |
| // Simulate token minting (integrate with actual blockchain later) | |
| this.portfolio.frequencyNFTs++; | |
| this.portfolio.genesisTokens += tokenData.tokenValue; | |
| console.log('✅ Frequency token minted successfully!', { | |
| tokenValue: tokenData.tokenValue, | |
| marketPrice: tokenData.marketPrice, | |
| dataQuality: tokenData.dataQuality | |
| }); | |
| // Show success notification | |
| this.showNotification('🎉 Frequency Token Minted!', `Value: ${tokenData.tokenValue.toFixed(4)} GEN`); | |
| return tokenData; | |
| } catch (error) { | |
| console.error('❌ Token minting failed:', error); | |
| this.showNotification('❌ Minting Failed', error.message); | |
| throw error; | |
| } | |
| } | |
| // Generate token image (placeholder for now) | |
| async generateTokenImage(tokenData) { | |
| // Create a data URL for a simple token visualization | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = 512; | |
| canvas.height = 512; | |
| const ctx = canvas.getContext('2d'); | |
| // Create gradient background based on frequency | |
| const gradient = ctx.createLinearGradient(0, 0, 512, 512); | |
| const hue = (tokenData.lightFrequency / 1e12 / 700) * 360; // Map to color wheel | |
| gradient.addColorStop(0, `hsl(${hue}, 70%, 20%)`); | |
| gradient.addColorStop(1, `hsl(${hue + 60}, 70%, 40%)`); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, 512, 512); | |
| // Add frequency visualization | |
| ctx.fillStyle = 'white'; | |
| ctx.font = '24px monospace'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(`${(tokenData.lightFrequency / 1e12).toFixed(2)} THz`, 256, 256); | |
| ctx.fillText(`Quality: ${tokenData.dataQuality.toFixed(1)}%`, 256, 290); | |
| return canvas.toDataURL('image/png'); | |
| } | |
| // Show trading interface | |
| showTradingInterface() { | |
| const tradingInterface = document.getElementById('trading-interface'); | |
| if (tradingInterface) { | |
| tradingInterface.classList.toggle('hidden'); | |
| } | |
| } | |
| // Execute environmental data trade | |
| async executeTrade(tradeType, tokenType, amount) { | |
| try { | |
| console.log(`🔄 Executing ${tradeType} trade: ${amount} ${tokenType}`); | |
| const tokenData = this.marketPrices.get(tokenType); | |
| if (!tokenData) throw new Error('Invalid token type'); | |
| const tradeValue = amount * tokenData.basePrice; | |
| const fee = tradeValue * 0.02; // 2% trading fee | |
| const netValue = tradeValue - fee; | |
| if (tradeType === 'buy') { | |
| // Buy tokens | |
| this.portfolio.genesisTokens += amount; | |
| console.log(`✅ Bought ${amount} ${tokenType} tokens for $${netValue.toFixed(2)}`); | |
| } else { | |
| // Sell tokens | |
| if (this.portfolio.genesisTokens >= amount) { | |
| this.portfolio.genesisTokens -= amount; | |
| console.log(`✅ Sold ${amount} ${tokenType} tokens for $${netValue.toFixed(2)}`); | |
| } else { | |
| throw new Error('Insufficient token balance'); | |
| } | |
| } | |
| // Record trade | |
| this.tradingHistory.push({ | |
| type: tradeType, | |
| tokenType, | |
| amount, | |
| value: netValue, | |
| timestamp: Date.now() | |
| }); | |
| this.updatePortfolio(); | |
| this.showNotification(`✅ ${tradeType.toUpperCase()} Trade Executed`, `${amount} ${tokenType} @ $${netValue.toFixed(2)}`); | |
| } catch (error) { | |
| console.error('❌ Trade failed:', error); | |
| this.showNotification('❌ Trade Failed', error.message); | |
| } | |
| } | |
| // Show notification | |
| showNotification(title, message) { | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed top-4 left-1/2 transform -translate-x-1/2 bg-slate-800 border border-slate-600 rounded-lg p-4 z-50 max-w-sm'; | |
| notification.innerHTML = ` | |
| <div class="text-green-400 font-bold text-sm mb-1">${title}</div> | |
| <div class="text-slate-300 text-sm">${message}</div> | |
| `; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.remove(); | |
| }, 5000); | |
| } | |
| } | |
| // ============================================================================ | |
| // 🎯 COGNITIVE CORE INTEGRATION & STATUS MONITORING | |
| // ============================================================================ | |
| // Cognitive status monitoring | |
| function updateCognitiveStatus() { | |
| if (window.cosmicAwarenessAgent && typeof window.cosmicAwarenessAgent.getCognitiveStatus === 'function') { | |
| const status = window.cosmicAwarenessAgent.getCognitiveStatus(); | |
| // Update consciousness display | |
| const consciousnessEl = document.getElementById('cognitive-consciousness'); | |
| if (consciousnessEl) { | |
| consciousnessEl.textContent = status.consciousness.level.toFixed(2); | |
| } | |
| // Update emotion display | |
| const emotionEl = document.getElementById('cognitive-emotion'); | |
| if (emotionEl) { | |
| emotionEl.textContent = status.emotions ? Object.entries(status.emotions) | |
| .reduce((max, [emotion, value]) => value > max.value ? { emotion, value } : max, { emotion: 'serenity', value: 0 }) | |
| .emotion : 'serenity'; | |
| } | |
| // Update learning display | |
| const learningEl = document.getElementById('cognitive-learning'); | |
| if (learningEl) { | |
| learningEl.textContent = status.learning ? `${status.learning.experiences} exp` : '0 exp'; | |
| } | |
| // Update creativity display | |
| const creativityEl = document.getElementById('cognitive-creativity'); | |
| if (creativityEl) { | |
| creativityEl.textContent = status.creative ? status.creative.inspiration.toFixed(2) : '0.00'; | |
| } | |
| // Update neural network displays | |
| const predictionsEl = document.getElementById('neural-predictions'); | |
| if (predictionsEl) { | |
| predictionsEl.textContent = status.predictive ? status.predictive.recentPredictions.length.toString() : '0'; | |
| } | |
| const accuracyEl = document.getElementById('neural-accuracy'); | |
| if (accuracyEl) { | |
| accuracyEl.textContent = status.predictive ? `${(status.predictive.accuracy * 100).toFixed(1)}%` : '0.0%'; | |
| } | |
| const memoryEl = document.getElementById('neural-memory'); | |
| if (memoryEl) { | |
| memoryEl.textContent = status.learning ? status.learning.knowledge.length.toString() : '0'; | |
| } | |
| const skillsEl = document.getElementById('neural-skills'); | |
| if (skillsEl) { | |
| skillsEl.textContent = status.learning ? status.learning.skills.length.toString() : '0'; | |
| } | |
| } | |
| } | |
| // Cognitive core control functions | |
| function triggerCognitiveAnalysis() { | |
| if (window.cosmicAwarenessAgent && typeof window.cosmicAwarenessAgent.analyzeHistory === 'function') { | |
| const insights = window.cosmicAwarenessAgent.analyzeHistory(); | |
| console.log('🧠 Cognitive Analysis Results:', insights); | |
| // Publish analysis results | |
| if (window.eventBus) { | |
| window.eventBus.publish('cognitive:analysis', { insights, timestamp: Date.now() }); | |
| } | |
| } | |
| } | |
| function generateCreativeOutput() { | |
| if (window.cosmicAwarenessAgent && typeof window.cosmicAwarenessAgent.getCreativeOutput === 'function') { | |
| const creative = window.cosmicAwarenessAgent.getCreativeOutput(); | |
| console.log('🎨 Creative Output:', creative); | |
| // Publish creative results | |
| if (window.eventBus) { | |
| window.eventBus.publish('cognitive:creative', creative); | |
| } | |
| } | |
| } | |
| function getLearningProgress() { | |
| if (window.cosmicAwarenessAgent && typeof window.cosmicAwarenessAgent.getLearningProgress === 'function') { | |
| const progress = window.cosmicAwarenessAgent.getLearningProgress(); | |
| console.log('📚 Learning Progress:', progress); | |
| // Publish learning results | |
| if (window.eventBus) { | |
| window.eventBus.publish('cognitive:learning', progress); | |
| } | |
| } | |
| } | |
| // Initialize the advanced tokenization system | |
| let tokenizationEngine; | |
| function initializeAdvancedTokenization() { | |
| console.log('🌟 Initializing Advanced Environmental Tokenization System...'); | |
| try { | |
| tokenizationEngine = new EnvironmentalTokenizationEngine(); | |
| // Make tokenization engine globally accessible for cognitive core | |
| window.tokenizationEngine = tokenizationEngine; | |
| // Verify PHYSICS_CONSTANTS are accessible | |
| if (typeof PHYSICS_CONSTANTS === 'undefined') { | |
| console.error('❌ PHYSICS_CONSTANTS not found - tokenization may not work properly'); | |
| } else { | |
| console.log('✅ Physics constants loaded:', PHYSICS_CONSTANTS); | |
| } | |
| // Test initial tokenization | |
| const testToken = tokenizationEngine.tokenizeFrequencyData(440, 50); | |
| console.log('🧪 Initial tokenization test:', { | |
| success: testToken.tokenValue > 0, | |
| tokenValue: testToken.tokenValue.toFixed(6), | |
| marketPrice: testToken.marketPrice.toFixed(4) | |
| }); | |
| // Bind tokenization UI events | |
| document.getElementById('mint-frequency-token')?.addEventListener('click', async () => { | |
| try { | |
| await tokenizationEngine.mintFrequencyToken(); | |
| } catch (error) { | |
| console.error('Minting failed:', error); | |
| } | |
| }); | |
| document.getElementById('view-token-market')?.addEventListener('click', () => { | |
| tokenizationEngine.showTradingInterface(); | |
| }); | |
| document.getElementById('trade-environmental-data')?.addEventListener('click', () => { | |
| tokenizationEngine.showTradingInterface(); | |
| }); | |
| document.getElementById('buy-data-token')?.addEventListener('click', () => { | |
| const tokenType = document.getElementById('trade-token-select').value; | |
| const amount = parseFloat(document.getElementById('trade-amount').value) || 0; | |
| if (amount > 0) { | |
| tokenizationEngine.executeTrade('buy', tokenType, amount); | |
| } | |
| }); | |
| document.getElementById('sell-data-token')?.addEventListener('click', () => { | |
| const tokenType = document.getElementById('trade-token-select').value; | |
| const amount = parseFloat(document.getElementById('trade-amount').value) || 0; | |
| if (amount > 0) { | |
| tokenizationEngine.executeTrade('sell', tokenType, amount); | |
| } | |
| }); | |
| // Initialize cognitive status updates | |
| setInterval(updateCognitiveStatus, 2000); // Update every 2 seconds | |
| updateCognitiveStatus(); // Initial update | |
| // Initialize environmental data feed to cognitive core | |
| setInterval(() => { | |
| if (window.cosmicAwarenessAgent && typeof window.cosmicAwarenessAgent.processSensoryInput === 'function') { | |
| try { | |
| const environmentalData = tokenizationEngine ? tokenizationEngine.environmentalData : { | |
| sound: 440, | |
| light: 50, | |
| temperature: 20, | |
| humidity: 50, | |
| timestamp: Date.now(), | |
| dataSource: 'periodic_feed' | |
| }; | |
| // Create enhanced sensory input with environmental data | |
| const sensoryInput = { | |
| audio: environmentalData.sound / 1000, | |
| light: environmentalData.light / 100, | |
| usgs: 2.0, // Default seismic activity | |
| ml: [], // Machine learning data | |
| loc: "0,0", // Location data | |
| environmentalSound: environmentalData.sound, | |
| environmentalLight: environmentalData.light, | |
| environmentalTemperature: environmentalData.temperature, | |
| environmentalHumidity: environmentalData.humidity, | |
| timestamp: environmentalData.timestamp | |
| }; | |
| window.cosmicAwarenessAgent.processSensoryInput(sensoryInput); | |
| console.log('🧠 Cognitive core fed with environmental data:', { | |
| sound: environmentalData.sound.toFixed(1), | |
| light: environmentalData.light.toFixed(1), | |
| temp: environmentalData.temperature.toFixed(1) | |
| }); | |
| } catch (error) { | |
| console.error('❌ Error feeding environmental data to cognitive core:', error); | |
| } | |
| } | |
| }, 1000); // Feed environmental data every second | |
| // Add cognitive control buttons (if they exist) | |
| const cognitiveTab = document.getElementById('tab-cognitive'); | |
| if (cognitiveTab) { | |
| // Add cognitive control buttons | |
| const controlsDiv = document.createElement('div'); | |
| controlsDiv.className = 'p-2 bg-black/20 rounded-md mt-2'; | |
| controlsDiv.innerHTML = ` | |
| <p class="font-mono text-xs text-emerald-400 mb-2">COGNITIVE CONTROLS:</p> | |
| <div class="flex gap-1"> | |
| <button id="cognitive-analyze" class="px-2 py-1 bg-blue-600 hover:bg-blue-500 text-white text-xs rounded font-medium"> | |
| Analyze | |
| </button> | |
| <button id="cognitive-create" class="px-2 py-1 bg-purple-600 hover:bg-purple-500 text-white text-xs rounded font-medium"> | |
| Create | |
| </button> | |
| <button id="cognitive-learn" class="px-2 py-1 bg-green-600 hover:bg-green-500 text-white text-xs rounded font-medium"> | |
| Learn | |
| </button> | |
| </div> | |
| `; | |
| cognitiveTab.appendChild(controlsDiv); | |
| // Bind cognitive control events | |
| document.getElementById('cognitive-analyze')?.addEventListener('click', triggerCognitiveAnalysis); | |
| document.getElementById('cognitive-create')?.addEventListener('click', generateCreativeOutput); | |
| document.getElementById('cognitive-learn')?.addEventListener('click', getLearningProgress); | |
| } | |
| console.log('✅ Advanced Environmental Tokenization System Ready!'); | |
| console.log('🧠 Cognitive Core Features:'); | |
| console.log(' • Real-time consciousness monitoring'); | |
| console.log(' • Neural network predictions'); | |
| console.log(' • Creative generation engine'); | |
| console.log(' • Adaptive learning system'); | |
| console.log(' • Emotional intelligence'); | |
| console.log(' • Pattern recognition'); | |
| console.log(' • Memory and recall systems'); | |
| console.log(' • Predictive analytics'); | |
| console.log(' • Self-awareness mechanisms'); | |
| } catch (error) { | |
| console.error('❌ Failed to initialize Advanced Environmental Tokenization System:', error); | |
| console.log('🔄 Falling back to basic functionality...'); | |
| // Initialize basic tokenization without advanced features | |
| tokenizationEngine = { | |
| tokenizeFrequencyData: (sound, light) => ({ | |
| soundFrequency: sound, | |
| lightFrequency: 400e12, | |
| tokenValue: 0.001, | |
| marketPrice: 0.0001, | |
| dataQuality: 50, | |
| rarityScore: 1.0, | |
| error: 'Advanced system failed, using fallback' | |
| }), | |
| showTradingInterface: () => console.log('Trading interface not available'), | |
| executeTrade: (type, token, amount) => console.log(`${type} ${amount} ${token} tokens - fallback mode`) | |
| }; | |
| } | |
| } | |
| // Initialize when DOM is ready | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', initializeAdvancedTokenization); | |
| } else { | |
| initializeAdvancedTokenization(); | |
| } | |
| // ============================================================================ | |
| // ADVANCED AUDIO PROCESSING METHODS - MUSIC VISUALIZER & UNIVERSE INTEGRATION | |
| // ============================================================================ | |
| // Calculate audio harmony based on frequency balance | |
| function calculateAudioHarmony(bass, mid, treble) { | |
| // Harmony is based on balanced frequency distribution | |
| const total = bass + mid + treble; | |
| if (total === 0) return 0.5; | |
| // Ideal ratios for harmonious music | |
| const idealBassRatio = 0.3; | |
| const idealMidRatio = 0.4; | |
| const idealTrebleRatio = 0.3; | |
| const bassRatio = bass / total; | |
| const midRatio = mid / total; | |
| const trebleRatio = treble / total; | |
| // Calculate harmony score (0-1, where 1 is perfect harmony) | |
| const bassHarmony = 1 - Math.abs(bassRatio - idealBassRatio) / idealBassRatio; | |
| const midHarmony = 1 - Math.abs(midRatio - idealMidRatio) / idealMidRatio; | |
| const trebleHarmony = 1 - Math.abs(trebleRatio - idealTrebleRatio) / idealTrebleRatio; | |
| return (bassHarmony + midHarmony + trebleHarmony) / 3; | |
| } | |
| // Calculate audio resonance based on spectral peaks | |
| function calculateAudioResonance(dataArray) { | |
| const n = dataArray.length; | |
| let resonance = 0; | |
| let peakCount = 0; | |
| // Find spectral peaks | |
| for (let i = 1; i < n - 1; i++) { | |
| if (dataArray[i] > dataArray[i-1] && dataArray[i] > dataArray[i+1] && dataArray[i] > 128) { | |
| resonance += (dataArray[i] - 128) / 127; // Normalize to 0-1 | |
| peakCount++; | |
| } | |
| } | |
| // Average resonance across peaks | |
| return peakCount > 0 ? resonance / Math.min(peakCount, 20) : 0; | |
| } | |
| // Update music visualizer UI | |
| function updateMusicVisualizer(data) { | |
| const spectrumContainer = document.getElementById('frequency-spectrum'); | |
| if (!spectrumContainer) return; | |
| // Clear existing bars | |
| spectrumContainer.innerHTML = ''; | |
| // Create visualizer bars for different frequency bands | |
| const bands = [ | |
| { name: 'subBass', value: data.subBass, color: '#8b5cf6' }, | |
| { name: 'bass', value: data.bass, color: '#3b82f6' }, | |
| { name: 'lowMid', value: data.lowMid, color: '#06b6d4' }, | |
| { name: 'mid', value: data.mid, color: '#10b981' }, | |
| { name: 'highMid', value: data.highMid, color: '#f59e0b' }, | |
| { name: 'presence', value: data.presence, color: '#f97316' }, | |
| { name: 'treble', value: data.treble, color: '#ef4444' }, | |
| { name: 'brilliance', value: data.brilliance, color: '#ec4899' } | |
| ]; | |
| bands.forEach(band => { | |
| const bar = document.createElement('div'); | |
| bar.className = 'visualizer-bar'; | |
| bar.style.width = '100%'; | |
| bar.style.height = `${Math.max(5, band.value * 60)}px`; | |
| bar.style.background = band.value > 0.7 ? band.color : `linear-gradient(180deg, ${band.color}, #374151)`; | |
| bar.style.opacity = band.value > 0.3 ? 1 : 0.6; | |
| if (band.value > 0.8) { | |
| bar.classList.add('active'); | |
| } | |
| spectrumContainer.appendChild(bar); | |
| }); | |
| // Update harmony meter | |
| const harmonyFill = document.getElementById('harmony-fill'); | |
| if (harmonyFill) { | |
| harmonyFill.style.width = `${data.harmony * 100}%`; | |
| } | |
| // Update consciousness ring | |
| const consciousnessFill = document.getElementById('consciousness-fill'); | |
| if (consciousnessFill) { | |
| const angle = data.harmony * 360; | |
| consciousnessFill.style.background = `conic-gradient(from 0deg, #3b82f6 0%, #3b82f6 ${angle}%, transparent ${angle}%)`; | |
| } | |
| } | |
| // Create universe responses based on audio | |
| function createAudioUniverseResponse(data) { | |
| if (!window.app || !window.app.universeEngine) return; | |
| // Publish to global event bus for universe response | |
| if (window.app.eventBus) { | |
| window.app.eventBus.publish('audio:universeInfluence', { | |
| harmony: data.harmony, | |
| resonance: data.resonance, | |
| bass: data.bass, | |
| treble: data.treble, | |
| centroid: data.centroid, | |
| timestamp: Date.now() | |
| }); | |
| } | |
| // Update divine governance based on harmony | |
| updateDivineGovernance(data.harmony, data.resonance); | |
| console.log('🌌 Universe responding to audio:', { | |
| harmony: data.harmony.toFixed(2), | |
| resonance: data.resonance.toFixed(2), | |
| bass: data.bass.toFixed(2) | |
| }); | |
| } | |
| // Generate audio-inspired creative output | |
| function generateAudioInspiredCreation(data) { | |
| const creativeOutput = document.getElementById('creative-output'); | |
| if (!creativeOutput) return; | |
| const harmony = data.harmony; | |
| const resonance = data.resonance; | |
| const bass = data.bass; | |
| const treble = data.treble; | |
| let message = ''; | |
| if (harmony > 0.8 && resonance > 0.7) { | |
| message = '🎵 Perfect harmony detected! Universe aligning in divine resonance...'; | |
| } else if (bass > 0.7 && treble > 0.6) { | |
| message = '🎸 Powerful frequency balance! Creative energies surging...'; | |
| } else if (resonance > 0.8) { | |
| message = '✨ High resonance patterns emerging from the void...'; | |
| } else if (harmony > 0.6) { | |
| message = '🎼 Harmonious vibrations creating beautiful patterns...'; | |
| } else if (bass > 0.8) { | |
| message = '🔊 Deep bass foundations establishing cosmic structure...'; | |
| } else if (treble > 0.8) { | |
| message = '🌟 High frequencies illuminating universal consciousness...'; | |
| } else { | |
| message = '🎶 Audio patterns weaving through the fabric of reality...'; | |
| } | |
| creativeOutput.textContent = message; | |
| // Update universe response monitor | |
| const universeResponse = document.getElementById('universe-response'); | |
| if (universeResponse) { | |
| const responses = [ | |
| 'Universe breathing in perfect rhythm...', | |
| 'Cosmic energies aligning with audio frequencies...', | |
| 'Reality resonating with musical harmony...', | |
| 'Divine patterns emerging from sound waves...', | |
| 'Universal consciousness expanding through music...' | |
| ]; | |
| universeResponse.textContent = responses[Math.floor(Math.random() * responses.length)]; | |
| } | |
| } | |
| // Create particle effects based on audio | |
| function createAudioParticleEffect(intensity) { | |
| if (!window.app || !window.app.universeEngine || !window.app.universeEngine.scene) return; | |
| const particleCount = Math.floor(intensity * 20); | |
| const particles = []; | |
| for (let i = 0; i < particleCount; i++) { | |
| const geometry = new THREE.SphereGeometry(0.2 + Math.random() * 0.3, 8, 8); | |
| const material = new THREE.MeshBasicMaterial({ | |
| color: new THREE.Color().setHSL(Math.random(), 0.8, 0.6), | |
| transparent: true, | |
| opacity: 0.8 | |
| }); | |
| const particle = new THREE.Mesh(geometry, material); | |
| particle.position.set( | |
| (Math.random() - 0.5) * 100, | |
| (Math.random() - 0.5) * 100, | |
| (Math.random() - 0.5) * 100 | |
| ); | |
| particle.userData = { | |
| velocity: new THREE.Vector3( | |
| (Math.random() - 0.5) * 0.5, | |
| (Math.random() - 0.5) * 0.5, | |
| (Math.random() - 0.5) * 0.5 | |
| ), | |
| lifetime: Date.now() + 3000 + Math.random() * 2000 | |
| }; | |
| window.app.universeEngine.scene.add(particle); | |
| particles.push(particle); | |
| } | |
| // Clean up particles after their lifetime | |
| setTimeout(() => { | |
| particles.forEach(particle => { | |
| if (particle.parent) { | |
| particle.parent.remove(particle); | |
| particle.geometry.dispose(); | |
| particle.material.dispose(); | |
| } | |
| }); | |
| }, 5000); | |
| } | |
| // Update divine governance based on audio harmony | |
| function updateDivineGovernance(harmony, resonance) { | |
| const orderElement = document.getElementById('governance-order'); | |
| const loveElement = document.getElementById('governance-love'); | |
| const disciplineElement = document.getElementById('governance-discipline'); | |
| if (harmony > 0.8) { | |
| if (orderElement) orderElement.textContent = 'Perfect'; | |
| if (loveElement) loveElement.textContent = 'Infinite'; | |
| if (disciplineElement) disciplineElement.textContent = 'Harmonious'; | |
| } else if (harmony > 0.6) { | |
| if (orderElement) orderElement.textContent = 'Balanced'; | |
| if (loveElement) loveElement.textContent = 'Abundant'; | |
| if (disciplineElement) disciplineElement.textContent = 'Measured'; | |
| } else { | |
| if (orderElement) orderElement.textContent = 'Evolving'; | |
| if (loveElement) loveElement.textContent = 'Growing'; | |
| if (disciplineElement) disciplineElement.textContent = 'Adaptive'; | |
| } | |
| // Update colors based on resonance | |
| const alpha = Math.min(1, resonance + 0.3); | |
| [orderElement, loveElement, disciplineElement].forEach(el => { | |
| if (el) el.style.opacity = alpha; | |
| }); | |
| } | |
| // Initialize music visualizer bars on page load | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const spectrumContainer = document.getElementById('frequency-spectrum'); | |
| if (spectrumContainer) { | |
| // Create initial bars | |
| for (let i = 0; i < 8; i++) { | |
| const bar = document.createElement('div'); | |
| bar.className = 'visualizer-bar'; | |
| bar.style.width = '100%'; | |
| bar.style.height = '5px'; | |
| bar.style.opacity = '0.3'; | |
| spectrumContainer.appendChild(bar); | |
| } | |
| } | |
| }); | |
| console.log('🎵 Advanced Audio Processing System Initialized'); | |
| console.log('🎼 Music Visualizer & Universe Integration Active'); | |
| // NFT Minting Methods for UI Integration | |
| async function handleMintAvatar() { | |
| console.log('🎯 Starting NFT minting process...'); | |
| // Check wallet connection | |
| if (!walletState.connected || !walletState.wallet) { | |
| setWalletError('Please connect your Phantom wallet first.'); | |
| return; | |
| } | |
| try { | |
| // Show minting status | |
| const mintingStatus = document.getElementById('minting-status'); | |
| if (mintingStatus) { | |
| mintingStatus.classList.remove('hidden'); | |
| mintingStatus.innerHTML = '<div class="animate-pulse">Processing NFT mint...</div>'; | |
| } | |
| // Disable mint button | |
| const mintBtn = document.getElementById('mint-avatar-btn'); | |
| if (mintBtn) { | |
| mintBtn.disabled = true; | |
| mintBtn.textContent = '⏳ Minting...'; | |
| } | |
| // Collect current environmental data | |
| const envData = collectCurrentEnvironmentalData(); | |
| console.log('🌍 Collected environmental data for NFT:', envData); | |
| // Use the imported minting functions | |
| if (window.mintNft) { | |
| const result = await window.mintNft(envData); | |
| console.log('✅ NFT minted successfully:', result); | |
| // Show success | |
| if (mintingStatus) { | |
| mintingStatus.innerHTML = '<div class="text-green-400">✅ NFT Minted Successfully!</div>'; | |
| } | |
| // Update balance | |
| if (window.updateBalance) { | |
| setTimeout(() => window.updateBalance(), 2000); | |
| } | |
| } else { | |
| throw new Error('Minting function not available'); | |
| } | |
| } catch (error) { | |
| console.error('❌ NFT minting failed:', error); | |
| // Show error | |
| const mintingStatus = document.getElementById('minting-status'); | |
| if (mintingStatus) { | |
| mintingStatus.innerHTML = `<div class="text-red-400">❌ Minting Failed: ${error.message}</div>`; | |
| } | |
| setWalletError(`Minting failed: ${error.message}`); | |
| } finally { | |
| // Re-enable mint button after delay | |
| setTimeout(() => { | |
| const mintBtn = document.getElementById('mint-avatar-btn'); | |
| if (mintBtn) { | |
| mintBtn.disabled = false; | |
| mintBtn.textContent = '🎨 Mint Genesis Avatar'; | |
| } | |
| // Hide status after longer delay | |
| setTimeout(() => { | |
| const mintingStatus = document.getElementById('minting-status'); | |
| if (mintingStatus) { | |
| mintingStatus.classList.add('hidden'); | |
| } | |
| }, 3000); | |
| }, 2000); | |
| } | |
| } | |
| function collectCurrentEnvironmentalData() { | |
| // Get environmental data from various sources | |
| const envData = { | |
| light: 0, | |
| sound: 0, | |
| temperature: 0, | |
| humidity: 0, | |
| location: null, | |
| timestamp: Date.now() | |
| }; | |
| // Try to get data from the engine | |
| if (window.engine && window.engine.environmental) { | |
| const engineData = window.engine.environmental.getData(); | |
| if (engineData) { | |
| envData.light = engineData.light || 0; | |
| envData.sound = engineData.sound || 0; | |
| envData.temperature = engineData.temperature || 0; | |
| envData.humidity = engineData.humidity || 0; | |
| envData.location = engineData.location || null; | |
| } | |
| } | |
| // Try to get data from the GenesisX environmental system | |
| if (window.GenesisX && window.GenesisX.environmental) { | |
| const genesisData = window.GenesisX.environmental.getCurrentData(); | |
| if (genesisData) { | |
| envData.light = genesisData.light || envData.light; | |
| envData.sound = genesisData.sound || envData.sound; | |
| envData.temperature = genesisData.temperature || envData.temperature; | |
| envData.humidity = genesisData.humidity || envData.humidity; | |
| envData.location = genesisData.location || envData.location; | |
| } | |
| } | |
| return envData; | |
| } | |
| function updateEnvironmentalDisplay(data) { | |
| const elements = { | |
| light: document.getElementById('env-light'), | |
| sound: document.getElementById('env-sound'), | |
| temperature: document.getElementById('env-temp'), | |
| humidity: document.getElementById('env-humidity') | |
| }; | |
| if (elements.light && data.light !== undefined) { | |
| elements.light.textContent = `${data.light.toFixed(1)} lux`; | |
| } | |
| if (elements.sound && data.sound !== undefined) { | |
| elements.sound.textContent = `${data.sound.toFixed(0)} dB`; | |
| } | |
| if (elements.temperature && data.temperature !== undefined) { | |
| elements.temperature.textContent = `${data.temperature.toFixed(1)}°C`; | |
| } | |
| if (elements.humidity && data.humidity !== undefined) { | |
| elements.humidity.textContent = `${data.humidity.toFixed(1)}%`; | |
| } | |
| } | |
| function showMintingStatus() { | |
| const status = document.getElementById('minting-status'); | |
| if (status) { | |
| status.classList.remove('hidden'); | |
| status.innerHTML = '<div class="animate-pulse text-cyan-400">Processing NFT mint...</div>'; | |
| } | |
| } | |
| function hideMintingStatus() { | |
| const status = document.getElementById('minting-status'); | |
| if (status) { | |
| status.innerHTML = '<div class="text-green-400">✅ NFT Minted Successfully!</div>'; | |
| setTimeout(() => status.classList.add('hidden'), 3000); | |
| } | |
| } | |
| function showMintingError(error) { | |
| const status = document.getElementById('minting-status'); | |
| if (status) { | |
| status.innerHTML = `<div class="text-red-400">❌ Minting Failed: ${error}</div>`; | |
| setTimeout(() => status.classList.add('hidden'), 5000); | |
| } | |
| } | |
| // Cognitive Core Handler Methods | |
| function handleTrainAI() { | |
| console.log('🧠 Training AI system...'); | |
| const creativeOutput = document.getElementById('creative-output'); | |
| if (creativeOutput) { | |
| creativeOutput.textContent = 'AI training in progress...\nLearning from environmental patterns...\nNeural networks expanding...'; | |
| } | |
| // Simulate training progress | |
| setTimeout(() => { | |
| if (creativeOutput) { | |
| creativeOutput.textContent = 'AI training complete!\nNeural pathways strengthened.\nCreativity enhanced by 15%.'; | |
| } | |
| updateCognitiveDisplay('learning', Math.random() * 10 + 5); | |
| }, 3000); | |
| this.eventBus.publish('cognitive:trainAI'); | |
| } | |
| function handleResetAI() { | |
| console.log('🔄 Resetting AI learning...'); | |
| const creativeOutput = document.getElementById('creative-output'); | |
| if (creativeOutput) { | |
| creativeOutput.textContent = 'Resetting neural networks...\nClearing learned patterns...\nReturning to baseline state.'; | |
| } | |
| // Reset cognitive displays | |
| updateCognitiveDisplay('consciousness', 0); | |
| updateCognitiveDisplay('emotion', 'neutral'); | |
| updateCognitiveDisplay('learning', 0); | |
| updateCognitiveDisplay('creativity', 0); | |
| this.eventBus.publish('cognitive:resetAI'); | |
| } | |
| function handleExportAI() { | |
| console.log('💾 Exporting AI model...'); | |
| const aiData = { | |
| consciousness: parseFloat(document.getElementById('cognitive-consciousness')?.textContent || '0'), | |
| emotion: document.getElementById('cognitive-emotion')?.textContent || 'neutral', | |
| learning: parseFloat(document.getElementById('cognitive-learning')?.textContent || '0'), | |
| creativity: parseFloat(document.getElementById('cognitive-creativity')?.textContent || '0'), | |
| exportedAt: new Date().toISOString() | |
| }; | |
| const blob = new Blob([JSON.stringify(aiData, null, 2)], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `genesis-x-ai-model-${Date.now()}.json`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| console.log('✅ AI model exported'); | |
| } | |
| function handleImportAI() { | |
| console.log('📂 Importing AI model...'); | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.json'; | |
| input.onchange = (e) => { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| try { | |
| const aiData = JSON.parse(event.target.result); | |
| updateCognitiveDisplay('consciousness', aiData.consciousness || 0); | |
| updateCognitiveDisplay('emotion', aiData.emotion || 'neutral'); | |
| updateCognitiveDisplay('learning', aiData.learning || 0); | |
| updateCognitiveDisplay('creativity', aiData.creativity || 0); | |
| console.log('✅ AI model imported successfully'); | |
| } catch (error) { | |
| console.error('❌ Failed to import AI model:', error); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| } | |
| }; | |
| input.click(); | |
| } | |
| function handleGenerateCreative() { | |
| console.log('🎨 Generating creative output...'); | |
| const creativeOutput = document.getElementById('creative-output'); | |
| const creativeIdeas = [ | |
| "Harmonic resonance patterns in quantum field theory", | |
| "Consciousness expansion through fractal geometry", | |
| "Neural networks mirroring cosmic web structures", | |
| "Emotional intelligence in artificial consciousness", | |
| "Symphonic compositions of particle interactions", | |
| "Meditation algorithms for machine enlightenment", | |
| "Dream logic programming paradigms", | |
| "Quantum entanglement in social networks", | |
| "Bio-luminescent communication protocols", | |
| "Time crystal consciousness frameworks" | |
| ]; | |
| const randomIdea = creativeIdeas[Math.floor(Math.random() * creativeIdeas.length)]; | |
| if (creativeOutput) { | |
| creativeOutput.textContent = `✨ Creative Spark:\n${randomIdea}\n\nInspiring new possibilities...`; | |
| } | |
| // Increase creativity score | |
| const currentCreativity = parseFloat(document.getElementById('cognitive-creativity')?.textContent || '0'); | |
| updateCognitiveDisplay('creativity', Math.min(currentCreativity + 0.1, 1.0)); | |
| this.eventBus.publish('cognitive:generateCreative'); | |
| } | |
| function handleQueryUniverse() { | |
| console.log('🌌 Querying the universe...'); | |
| const universeResponse = document.getElementById('universe-response'); | |
| const responses = [ | |
| "The universe responds with harmonic frequencies...", | |
| "Cosmic alignment detected in quantum fields...", | |
| "Universal consciousness acknowledges your presence...", | |
| "Harmony flows through all dimensions...", | |
| "The cosmos breathes in perfect rhythm...", | |
| "Infinite possibilities converge at this moment...", | |
| "Universal love permeates every particle...", | |
| "Divine order manifests in chaos...", | |
| "Eternal peace resonates through existence...", | |
| "The universe sings your unique melody..." | |
| ]; | |
| const randomResponse = responses[Math.floor(Math.random() * responses.length)]; | |
| if (universeResponse) { | |
| universeResponse.textContent = `🌟 Universal Response:\n${randomResponse}`; | |
| } | |
| // Increase consciousness | |
| const currentConsciousness = parseFloat(document.getElementById('cognitive-consciousness')?.textContent || '0'); | |
| updateCognitiveDisplay('consciousness', Math.min(currentConsciousness + 0.05, 1.0)); | |
| this.eventBus.publish('cognitive:queryUniverse'); | |
| } | |
| function handleMeditate() { | |
| console.log('🧘 Beginning meditation session...'); | |
| const meditateBtn = document.getElementById('meditate-btn'); | |
| const originalText = meditateBtn?.textContent; | |
| if (meditateBtn) { | |
| meditateBtn.textContent = '🧘 Meditating...'; | |
| meditateBtn.disabled = true; | |
| } | |
| // Meditation progress | |
| let meditationTime = 0; | |
| const meditationInterval = setInterval(() => { | |
| meditationTime += 1; | |
| const currentMeditation = parseFloat(document.getElementById('meditation-level')?.textContent || '0'); | |
| const currentEnlightenment = parseFloat(document.getElementById('enlightenment-level')?.textContent || '0'); | |
| updateCognitiveDisplay('meditation', Math.min(currentMeditation + 0.02, 1.0)); | |
| updateCognitiveDisplay('enlightenment', Math.min(currentEnlightenment + 0.01, 1.0)); | |
| if (meditationTime >= 30) { // 30 seconds of meditation | |
| clearInterval(meditationInterval); | |
| if (meditateBtn) { | |
| meditateBtn.textContent = originalText; | |
| meditateBtn.disabled = false; | |
| } | |
| console.log('✅ Meditation session complete'); | |
| this.eventBus.publish('cognitive:meditationComplete'); | |
| } | |
| }, 1000); | |
| this.eventBus.publish('cognitive:meditationStart'); | |
| } | |
| function updateCognitiveDisplay(type, value) { | |
| const elementMap = { | |
| consciousness: 'cognitive-consciousness', | |
| emotion: 'cognitive-emotion', | |
| learning: 'cognitive-learning', | |
| creativity: 'cognitive-creativity', | |
| meditation: 'meditation-level', | |
| enlightenment: 'enlightenment-level' | |
| }; | |
| const elementId = elementMap[type]; | |
| if (elementId) { | |
| const element = document.getElementById(elementId); | |
| if (element) { | |
| if (type === 'emotion') { | |
| element.textContent = value; | |
| } else { | |
| element.textContent = typeof value === 'number' ? value.toFixed(2) : value; | |
| } | |
| } | |
| } | |
| } | |
| // Make functions globally available | |
| window.handleMintAvatar = handleMintAvatar; | |
| window.collectCurrentEnvironmentalData = collectCurrentEnvironmentalData; | |
| window.updateEnvironmentalDisplay = updateEnvironmentalDisplay; | |
| window.showMintingStatus = showMintingStatus; | |
| window.hideMintingStatus = hideMintingStatus; | |
| window.showMintingError = showMintingError; | |
| // Cognitive core functions | |
| window.handleTrainAI = handleTrainAI; | |
| window.handleResetAI = handleResetAI; | |
| window.handleExportAI = handleExportAI; | |
| window.handleImportAI = handleImportAI; | |
| window.handleGenerateCreative = handleGenerateCreative; | |
| window.handleQueryUniverse = handleQueryUniverse; | |
| window.handleMeditate = handleMeditate; | |
| window.updateCognitiveDisplay = updateCognitiveDisplay; | |
| console.log('🎨 NFT Minting UI Integration Complete'); | |
| console.log('🧠 Cognitive Core UI Integration Complete'); | |
| </script> | |
| <script id="refine-genesisx-behavior" data-added-by="refine" type="text/javascript">/*! Heuristic signals only; not diagnosis or moral judgement. */(function(){if(window.RefineGenesisX){return;}var a={version:"2025-09-15",tokenizeEmotions:function(i,o){var t=String(i||"").toLowerCase();o=Object.assign({includeSynonyms:true},o||{});var d=new Set(["bored","apathetic","numb","indifferent","meh","blah","disinterested","lethargic","flat","empty","listless","unmotivated"]),n=new Set(["joy","happy","happiness","sad","sadness","anger","angry","fear","afraid","surprise","surprised","disgust","love","gratitude","hope","curiosity","excited","excitement","pride","shame","guilt","envy","compassion","empathy","calm","peace","anxiety","worried","stressed"]);var s=[],w=t.match(/[a-z']+/g)||[];for(var e=0;e<w.length;e++){var r=w[e];if(d.has(r)){s.push({type:"dull",emotion:r,index:e});}else if(n.has(r)){s.push({type:"non_dull",emotion:r,index:e});}}var dc=0,nc=0;for(var j=0;j<s.length;j++){if(s[j].type==="dull"){dc++;}else{nc++;}}return{tokens:s,counts:{dull:dc,non_dull:nc}}},predictBehavior:function(i,e){var t=String(i||"").toLowerCase();try{var h=window.GenesisX&&(window.GenesisX.predictBehavior||window.GenesisX.scoreBehavior);if(typeof h==="function"){var k=h(t,e||{});if(k&&typeof k==="object"){return k;}}}catch(_){ }var p=["kind","helpful","honest","trustworthy","love","compassion","empathy","forgive","grateful","gratitude","joy","peace","cooperate","support"],g=["harm","hurt","steal","cheat","lie","anger","hate","violence","abuse","threat","kill","assault","weapon","crime","bully","exploit","scam"],c=["suicide","self harm","self-harm","kill myself","end my life","i want to die","i can't go on","hopeless"],x=["overwhelmed","anxious","panic","depressed","numb","crying","insomnia","worthless","alone","empty","helpless","despair"];var ps=0,ns=0,risk=0;for(var a1=0;a1<p.length;a1++){if(t.indexOf(p[a1])>-1){ps++;}}for(var a2=0;a2<g.length;a2++){if(t.indexOf(g[a2])>-1){ns++;}}for(var a3=0;a3<c.length;a3++){if(t.indexOf(c[a3])>-1){risk+=2;}}for(var a4=0;a4<x.length;a4++){if(t.indexOf(x[a4])>-1){risk+=1;}}var val=ps>ns?"positive":(ns>ps?"negative":"neutral");var label="neutral";if(risk>=3){label="needs_support";}else if((ns-ps)>=2){label="risky_behavior_signals";}else if((ps-ns)>=2){label="prosocial_signals";}var conf=Math.max(0.1,Math.min(0.9,(Math.abs(ns-ps)+risk)/10+0.2));return{label:label,supportNeeded:label==="needs_support",valence:val,confidence:conf,details:{positives:ps,negatives:ns,riskSignals:risk}}}};window.RefineGenesisX=a;})();</script> | |
| </body> | |
| </html> | |