Files
blackroad-metaverse/index.html
Your Name a40f2b2742 🔐 Login system
2026-01-30 16:29:07 -06:00

1835 lines
61 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlackRoad Metaverse — Ultimate 3D Universe</title>
<meta name="description" content="BlackRoad Metaverse - A living, infinite 3D universe where AI agents exist, interact, and evolve">
<!-- Fonts -->
<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;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<!-- Three.js -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* ===== OFFICIAL BLACKROAD COLORS ===== */
--sunrise-orange: #FF9D00;
--warm-orange: #FF6B00;
--hot-pink: #FF0066;
--electric-magenta: #FF006B;
--deep-magenta: #D600AA;
--vivid-purple: #7700FF;
--cyber-blue: #0066FF;
/* ===== NEUTRALS ===== */
--pure-black: #000000;
--deep-black: #0A0A0A;
--charcoal: #1A1A1A;
--pure-white: #FFFFFF;
--gray-300: #A0A0A0;
/* ===== SEMANTIC COLORS ===== */
--success: #27AE60;
--error: #E74C3C;
--alice-blue: #0066FF;
--aria-pink: #FF0066;
--lucidia-purple: #7700FF;
/* ===== OFFICIAL GRADIENTS ===== */
--gradient-br: linear-gradient(180deg, #FF9D00 0%, #FF6B00 25%, #FF0066 75%, #FF006B 100%);
--gradient-os: linear-gradient(180deg, #FF006B 0%, #D600AA 25%, #7700FF 75%, #0066FF 100%);
--gradient-full: linear-gradient(180deg, #FF9D00 0%, #FF6B00 14%, #FF0066 28%, #FF006B 42%, #D600AA 57%, #7700FF 71%, #0066FF 100%);
/* ===== GOLDEN RATIO SPACING (φ = 1.618) ===== */
--space-xs: 8px;
--space-sm: 13px;
--space-md: 21px;
--space-lg: 34px;
--space-xl: 55px;
--space-2xl: 89px;
/* ===== BORDER RADIUS ===== */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-xl: 24px;
--radius-2xl: 34px;
/* ===== GLASS MORPHISM ===== */
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-bg-strong: rgba(255, 255, 255, 0.08);
--glass-border: rgba(255, 255, 255, 0.1);
--glass-border-strong: rgba(255, 255, 255, 0.15);
/* ===== LEGACY COMPAT ===== */
--bg-dark: var(--deep-black);
--bg-darker: var(--pure-black);
--text-primary: var(--pure-white);
--text-secondary: var(--gray-300);
--accent-purple: var(--vivid-purple);
--accent-blue: var(--cyber-blue);
--accent-red: var(--error);
--accent-green: var(--success);
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-darker);
color: var(--text-primary);
overflow: hidden;
position: relative;
}
/* ===== LOGIN SCREEN ===== */
#login-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 50%, #0a0a0a 100%);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
transition: opacity 0.5s ease, visibility 0.5s ease;
}
#login-screen.hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.login-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.star {
position: absolute;
width: 2px;
height: 2px;
background: white;
border-radius: 50%;
animation: twinkle 3s infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.login-container {
position: relative;
z-index: 2;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: var(--radius-xl);
padding: var(--space-2xl);
width: 90%;
max-width: 500px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
.login-container h1 {
font-size: 42px;
font-weight: 800;
font-family: 'JetBrains Mono', monospace;
margin-bottom: var(--space-sm);
background: var(--gradient-full);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.login-container p {
color: var(--text-secondary);
margin-bottom: var(--space-lg);
font-size: 16px;
}
.form-group {
margin-bottom: var(--space-md);
}
.form-group label {
display: block;
margin-bottom: var(--space-xs);
font-size: 14px;
font-weight: 500;
color: var(--text-secondary);
}
.form-group input {
width: 100%;
padding: var(--space-sm) var(--space-md);
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: var(--radius-md);
color: var(--text-primary);
font-size: 16px;
font-family: 'JetBrains Mono', monospace;
transition: all 0.3s ease;
}
.form-group input:focus {
outline: none;
border-color: var(--hot-pink);
box-shadow: 0 0 0 3px rgba(255, 0, 102, 0.2);
}
.btn-primary {
width: 100%;
padding: var(--space-sm) var(--space-md);
background: var(--gradient-br);
border: none;
border-radius: var(--radius-md);
color: white;
font-size: 16px;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
cursor: pointer;
transition: all 0.3s ease;
margin-top: var(--space-sm);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(255, 0, 102, 0.4);
}
.signup-link {
text-align: center;
margin-top: 20px;
font-size: 14px;
color: var(--text-secondary);
}
.signup-link a {
color: var(--accent-purple);
text-decoration: none;
font-weight: 600;
}
/* ===== METAVERSE CONTAINER ===== */
#metaverse-container {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
}
#metaverse-canvas {
display: block;
}
/* ===== UI OVERLAY ===== */
#ui-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 100;
opacity: 0;
transition: opacity 0.5s ease;
}
#ui-overlay.active {
opacity: 1;
}
#ui-overlay > * {
pointer-events: auto;
}
/* Top Bar */
.top-bar {
position: absolute;
top: 20px;
left: 20px;
right: 20px;
display: flex;
justify-content: space-between;
align-items: center;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 15px;
padding: 15px 25px;
}
.location-info {
display: flex;
align-items: center;
gap: 15px;
}
.status-dot {
width: 10px;
height: 10px;
background: var(--accent-green);
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.7; }
}
.location-text {
font-size: 14px;
font-weight: 500;
}
.user-info {
display: flex;
align-items: center;
gap: 12px;
}
.user-avatar {
width: 35px;
height: 35px;
border-radius: 50%;
background: linear-gradient(135deg, #9B59B6, #4A90E2);
}
/* Controls Panel */
.controls-panel {
position: absolute;
bottom: 20px;
left: 20px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 15px;
padding: 20px;
min-width: 250px;
}
.controls-panel h3 {
font-size: 16px;
font-weight: 600;
margin-bottom: 15px;
color: var(--text-primary);
}
.control-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
font-size: 13px;
}
.control-key {
font-family: 'JetBrains Mono', monospace;
background: rgba(255, 255, 255, 0.1);
padding: 4px 8px;
border-radius: 5px;
font-weight: 600;
}
.control-action {
color: var(--text-secondary);
}
/* Agents Panel */
.agents-panel {
position: absolute;
top: 100px;
right: 20px;
width: 320px;
max-height: calc(100vh - 140px);
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
}
.agent-card {
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 15px;
padding: 20px;
cursor: pointer;
transition: all 0.3s ease;
}
.agent-card:hover {
transform: translateX(-5px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.agent-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 12px;
}
.agent-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
.agent-info h4 {
font-size: 16px;
font-weight: 600;
margin-bottom: 3px;
}
.agent-model {
font-size: 12px;
color: var(--text-secondary);
}
.agent-status {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
font-size: 13px;
}
.agent-thought {
font-size: 13px;
color: var(--text-secondary);
font-style: italic;
margin-bottom: 15px;
}
.agent-actions {
display: flex;
gap: 8px;
}
.btn-small {
flex: 1;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: white;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-small:hover {
background: rgba(255, 255, 255, 0.2);
}
/* Weather Panel */
.weather-panel {
position: absolute;
bottom: 20px;
right: 20px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 15px;
padding: 20px;
min-width: 200px;
}
.weather-info {
text-align: center;
}
.weather-icon {
font-size: 48px;
margin-bottom: 10px;
}
.weather-time {
font-size: 24px;
font-weight: 600;
margin-bottom: 5px;
}
.weather-biome {
font-size: 14px;
color: var(--text-secondary);
}
/* Transport Menu */
.transport-menu {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 20px;
padding: 30px;
min-width: 400px;
display: none;
}
.transport-menu.active {
display: block;
}
.transport-menu h2 {
font-size: 24px;
margin-bottom: 20px;
text-align: center;
}
.waypoint-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.waypoint-btn {
padding: 15px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
text-align: left;
cursor: pointer;
transition: all 0.3s ease;
}
.waypoint-btn:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateX(5px);
}
.waypoint-name {
font-size: 16px;
font-weight: 600;
margin-bottom: 5px;
}
.waypoint-coords {
font-size: 12px;
color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace;
}
/* Loading Screen */
#loadingScreen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bg-darker);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-spinner {
width: 60px;
height: 60px;
border: 4px solid rgba(255, 255, 255, 0.1);
border-top-color: var(--accent-purple);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
font-size: 16px;
color: var(--text-secondary);
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
</style>
</head>
<body>
<!-- Loading Screen -->
<div id="loadingScreen">
<div class="loading-spinner"></div>
<div class="loading-text">Initializing universe...</div>
</div>
<!-- Login Screen -->
<div id="login-screen">
<div class="login-background" id="loginBackground"></div>
<div class="login-container">
<h1>🌌 BlackRoad</h1>
<p>Enter the infinite metaverse</p>
<form id="loginForm">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter your name" required autocomplete="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="••••••••" required autocomplete="current-password">
</div>
<button type="submit" class="btn-primary">Enter Metaverse</button>
</form>
<div class="signup-link">
Don't have an account? <a href="#" id="signupLink">Sign up</a>
</div>
</div>
</div>
<!-- Metaverse Container -->
<div id="metaverse-container">
<canvas id="metaverse-canvas"></canvas>
</div>
<!-- UI Overlay -->
<div id="ui-overlay">
<!-- Top Bar -->
<div class="top-bar">
<div class="location-info">
<div class="status-dot"></div>
<div class="location-text" id="locationText">Spawn Point</div>
</div>
<div class="user-info">
<span id="usernameDisplay">Traveler</span>
<div class="user-avatar"></div>
</div>
</div>
<!-- Controls Panel -->
<div class="controls-panel">
<h3>🎮 Controls</h3>
<div class="control-row">
<span class="control-key">W A S D</span>
<span class="control-action">Move</span>
</div>
<div class="control-row">
<span class="control-key">Mouse</span>
<span class="control-action">Look</span>
</div>
<div class="control-row">
<span class="control-key">Space</span>
<span class="control-action">Jump / Fly Up</span>
</div>
<div class="control-row">
<span class="control-key">Shift</span>
<span class="control-action">Fly Down</span>
</div>
<div class="control-row">
<span class="control-key">F</span>
<span class="control-action">Toggle Flying</span>
</div>
<div class="control-row">
<span class="control-key">T</span>
<span class="control-action">Teleport Menu</span>
</div>
<div class="control-row">
<span class="control-key">R</span>
<span class="control-action">Toggle Rain</span>
</div>
<div class="control-row">
<span class="control-key">N</span>
<span class="control-action">Toggle Snow</span>
</div>
<div class="control-row">
<span class="control-key">G</span>
<span class="control-action">Toggle Fireflies</span>
</div>
</div>
<!-- Agents Panel -->
<div class="agents-panel" id="agentsPanel"></div>
<!-- Weather Panel -->
<div class="weather-panel">
<div class="weather-info">
<div class="weather-icon" id="weatherIcon">☀️</div>
<div class="weather-time" id="weatherTime">12:00</div>
<div class="weather-biome" id="weatherBiome">Enchanted Forest</div>
</div>
</div>
<!-- Transport Menu -->
<div class="transport-menu" id="transportMenu">
<h2>🚀 Fast Travel</h2>
<div class="waypoint-list" id="waypointList"></div>
<button class="btn-primary" onclick="closeTransportMenu()" style="margin-top: 15px;">Close</button>
</div>
</div>
<script type="module">
import * as THREE from 'three';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
// ===== GLOBAL STATE =====
const state = {
loggedIn: false,
username: '',
scene: null,
camera: null,
renderer: null,
controls: null,
agents: [],
timeOfDay: 0.5, // 0 = midnight, 0.5 = noon, 1 = midnight
currentBiome: 'forest',
isFlying: false,
particles: {
rain: null,
snow: null,
fireflies: null
},
chunks: new Map(),
loadedChunks: new Set()
};
// ===== PERLIN NOISE (Simple Implementation) =====
class PerlinNoise {
constructor(seed = Math.random()) {
this.seed = seed;
this.gradients = {};
this.memory = {};
}
rand_vect() {
let theta = Math.random() * 2 * Math.PI;
return {x: Math.cos(theta), y: Math.sin(theta)};
}
dot_prod_grid(x, y, vx, vy) {
let g_vect;
let d_vect = {x: x - vx, y: y - vy};
let grid_key = `${vx},${vy}`;
if (this.gradients[grid_key]) {
g_vect = this.gradients[grid_key];
} else {
g_vect = this.rand_vect();
this.gradients[grid_key] = g_vect;
}
return d_vect.x * g_vect.x + d_vect.y * g_vect.y;
}
smootherstep(x) {
return 6*x**5 - 15*x**4 + 10*x**3;
}
interp(x, a, b) {
return a + this.smootherstep(x) * (b - a);
}
noise(x, y) {
let xf = Math.floor(x);
let yf = Math.floor(y);
let tl = this.dot_prod_grid(x, y, xf, yf);
let tr = this.dot_prod_grid(x, y, xf + 1, yf);
let bl = this.dot_prod_grid(x, y, xf, yf + 1);
let br = this.dot_prod_grid(x, y, xf + 1, yf + 1);
let xt = this.interp(x - xf, tl, tr);
let xb = this.interp(x - xf, bl, br);
let v = this.interp(y - yf, xt, xb);
return v;
}
}
const noise = new PerlinNoise();
// ===== BIOME DEFINITIONS =====
const BIOMES = {
forest: {
name: 'Enchanted Forest',
groundColor: 0x2d5016,
skyColor: 0x87CEEB,
fogColor: 0x87CEEB,
heightVariation: 5,
features: ['trees', 'flowers', 'mushrooms']
},
ocean: {
name: 'Infinite Ocean',
groundColor: 0x006994,
skyColor: 0x87CEEB,
fogColor: 0x87CEEB,
heightVariation: 2,
features: ['water', 'waves']
},
mountains: {
name: 'Crystalline Peaks',
groundColor: 0x8B7355,
skyColor: 0xB0C4DE,
fogColor: 0xB0C4DE,
heightVariation: 50,
features: ['crystals', 'snow']
},
desert: {
name: 'Golden Dunes',
groundColor: 0xF4A460,
skyColor: 0xFFD700,
fogColor: 0xFFD700,
heightVariation: 10,
features: ['cacti', 'rocks']
},
crystal: {
name: 'Crystal Caverns',
groundColor: 0x2d2d44,
skyColor: 0x1a1a2e,
fogColor: 0x1a1a2e,
heightVariation: 15,
features: ['crystals', 'glow']
},
sky: {
name: 'Sky Islands',
groundColor: 0x87CEEB,
skyColor: 0xADD8E6,
fogColor: 0xADD8E6,
heightVariation: 30,
features: ['floating', 'waterfalls']
}
};
// ===== PARTICLE SYSTEMS =====
class RainEffect {
constructor(scene) {
this.scene = scene;
this.particles = null;
this.count = 1000;
}
create() {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(this.count * 3);
const velocities = new Float32Array(this.count);
for (let i = 0; i < this.count; i++) {
positions[i * 3] = (Math.random() - 0.5) * 100;
positions[i * 3 + 1] = Math.random() * 50;
positions[i * 3 + 2] = (Math.random() - 0.5) * 100;
velocities[i] = 0.5 + Math.random() * 0.5;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 1));
const material = new THREE.PointsMaterial({
color: 0x4A90E2,
size: 0.1,
transparent: true,
opacity: 0.6
});
this.particles = new THREE.Points(geometry, material);
this.scene.add(this.particles);
}
update() {
if (!this.particles) return;
const positions = this.particles.geometry.attributes.position.array;
const velocities = this.particles.geometry.attributes.velocity.array;
for (let i = 0; i < this.count; i++) {
positions[i * 3 + 1] -= velocities[i];
if (positions[i * 3 + 1] < 0) {
positions[i * 3 + 1] = 50;
}
}
this.particles.geometry.attributes.position.needsUpdate = true;
}
destroy() {
if (this.particles) {
this.scene.remove(this.particles);
this.particles.geometry.dispose();
this.particles.material.dispose();
this.particles = null;
}
}
}
class SnowEffect {
constructor(scene) {
this.scene = scene;
this.particles = null;
this.count = 2000;
}
create() {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(this.count * 3);
const phases = new Float32Array(this.count);
for (let i = 0; i < this.count; i++) {
positions[i * 3] = (Math.random() - 0.5) * 100;
positions[i * 3 + 1] = Math.random() * 50;
positions[i * 3 + 2] = (Math.random() - 0.5) * 100;
phases[i] = Math.random() * Math.PI * 2;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('phase', new THREE.BufferAttribute(phases, 1));
const material = new THREE.PointsMaterial({
color: 0xFFFFFF,
size: 0.15,
transparent: true,
opacity: 0.8
});
this.particles = new THREE.Points(geometry, material);
this.scene.add(this.particles);
}
update() {
if (!this.particles) return;
const positions = this.particles.geometry.attributes.position.array;
const phases = this.particles.geometry.attributes.phase.array;
const time = performance.now() * 0.001;
for (let i = 0; i < this.count; i++) {
const phase = phases[i];
positions[i * 3] += Math.sin(time + phase) * 0.02;
positions[i * 3 + 1] -= 0.1;
positions[i * 3 + 2] += Math.cos(time + phase) * 0.02;
if (positions[i * 3 + 1] < 0) {
positions[i * 3 + 1] = 50;
}
}
this.particles.geometry.attributes.position.needsUpdate = true;
}
destroy() {
if (this.particles) {
this.scene.remove(this.particles);
this.particles.geometry.dispose();
this.particles.material.dispose();
this.particles = null;
}
}
}
class FirefliesEffect {
constructor(scene) {
this.scene = scene;
this.particles = null;
this.lights = [];
this.count = 100;
}
create() {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(this.count * 3);
const colors = new Float32Array(this.count * 3);
const phases = new Float32Array(this.count);
for (let i = 0; i < this.count; i++) {
positions[i * 3] = (Math.random() - 0.5) * 50;
positions[i * 3 + 1] = Math.random() * 10 + 1;
positions[i * 3 + 2] = (Math.random() - 0.5) * 50;
colors[i * 3] = 0.8;
colors[i * 3 + 1] = 1.0;
colors[i * 3 + 2] = 0.3;
phases[i] = Math.random() * Math.PI * 2;
if (i % 10 === 0) {
const light = new THREE.PointLight(0xffff00, 0.5, 3);
light.position.set(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
this.scene.add(light);
this.lights.push({ light, phase: phases[i], index: i });
}
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('phase', new THREE.BufferAttribute(phases, 1));
const material = new THREE.PointsMaterial({
size: 0.3,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
this.particles = new THREE.Points(geometry, material);
this.scene.add(this.particles);
}
update() {
if (!this.particles) return;
const positions = this.particles.geometry.attributes.position.array;
const phases = this.particles.geometry.attributes.phase.array;
const time = performance.now() * 0.001;
for (let i = 0; i < this.count; i++) {
const phase = phases[i];
positions[i * 3] += Math.sin(time + phase) * 0.01;
positions[i * 3 + 1] += Math.cos(time * 0.5 + phase) * 0.01;
positions[i * 3 + 2] += Math.sin(time * 0.7 + phase) * 0.01;
}
this.lights.forEach(({ light, phase, index }) => {
light.position.set(
positions[index * 3],
positions[index * 3 + 1],
positions[index * 3 + 2]
);
light.intensity = 0.3 + Math.sin(time * 3 + phase) * 0.2;
});
this.particles.geometry.attributes.position.needsUpdate = true;
}
destroy() {
if (this.particles) {
this.scene.remove(this.particles);
this.particles.geometry.dispose();
this.particles.material.dispose();
this.particles = null;
}
this.lights.forEach(({ light }) => this.scene.remove(light));
this.lights = [];
}
}
// ===== CHUNK GENERATION =====
function generateChunk(chunkX, chunkZ, biomeType = 'forest') {
const chunk = new THREE.Group();
const biome = BIOMES[biomeType];
const chunkSize = 50;
const startX = chunkX * chunkSize;
const startZ = chunkZ * chunkSize;
// Terrain
const terrainGeometry = new THREE.PlaneGeometry(chunkSize, chunkSize, 32, 32);
const vertices = terrainGeometry.attributes.position.array;
for (let i = 0; i < vertices.length; i += 3) {
const x = vertices[i] + startX;
const z = vertices[i + 1] + startZ;
let height = 0;
height += noise.noise(x * 0.02, z * 0.02) * 10;
height += noise.noise(x * 0.05, z * 0.05) * 5;
height += noise.noise(x * 0.1, z * 0.1) * 2;
vertices[i + 2] = height * (biome.heightVariation / 10);
}
terrainGeometry.computeVertexNormals();
const terrainMaterial = new THREE.MeshStandardMaterial({
color: biome.groundColor,
roughness: 0.8,
metalness: 0.2
});
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
terrain.rotation.x = -Math.PI / 2;
terrain.position.set(startX + chunkSize / 2, 0, startZ + chunkSize / 2);
chunk.add(terrain);
// Add features
if (biome.features.includes('trees')) {
for (let i = 0; i < 10; i++) {
const tree = createTree();
tree.position.set(
startX + Math.random() * chunkSize,
0,
startZ + Math.random() * chunkSize
);
chunk.add(tree);
}
}
if (biome.features.includes('flowers')) {
for (let i = 0; i < 20; i++) {
const flower = createFlower();
flower.position.set(
startX + Math.random() * chunkSize,
0,
startZ + Math.random() * chunkSize
);
chunk.add(flower);
}
}
if (biome.features.includes('crystals')) {
for (let i = 0; i < 15; i++) {
const crystal = createCrystal();
crystal.position.set(
startX + Math.random() * chunkSize,
0,
startZ + Math.random() * chunkSize
);
chunk.add(crystal);
}
}
chunk.userData = { chunkX, chunkZ, biomeType };
return chunk;
}
function createTree() {
const tree = new THREE.Group();
const trunk = new THREE.Mesh(
new THREE.CylinderGeometry(0.3, 0.4, 3, 8),
new THREE.MeshStandardMaterial({ color: 0x8B4513 })
);
trunk.position.y = 1.5;
tree.add(trunk);
const foliage = new THREE.Mesh(
new THREE.SphereGeometry(1.5, 8, 8),
new THREE.MeshStandardMaterial({ color: 0x228B22 })
);
foliage.position.y = 3;
tree.add(foliage);
return tree;
}
function createFlower() {
const flower = new THREE.Group();
const stem = new THREE.Mesh(
new THREE.CylinderGeometry(0.02, 0.02, 0.5, 4),
new THREE.MeshStandardMaterial({ color: 0x90EE90 })
);
stem.position.y = 0.25;
flower.add(stem);
const colors = [0xFF69B4, 0xFFD700, 0x9B59B6, 0x4A90E2];
const petal = new THREE.Mesh(
new THREE.CircleGeometry(0.15, 6),
new THREE.MeshStandardMaterial({ color: colors[Math.floor(Math.random() * colors.length)] })
);
petal.rotation.x = -Math.PI / 2;
petal.position.y = 0.5;
flower.add(petal);
return flower;
}
function createCrystal() {
const colors = [0x9B59B6, 0x4A90E2, 0xE74C3C, 0x27AE60];
const height = 1 + Math.random() * 2;
const crystal = new THREE.Mesh(
new THREE.ConeGeometry(0.3, height, 6),
new THREE.MeshStandardMaterial({
color: colors[Math.floor(Math.random() * colors.length)],
emissive: colors[Math.floor(Math.random() * colors.length)],
emissiveIntensity: 0.3,
metalness: 0.8,
roughness: 0.2
})
);
crystal.position.y = height / 2;
crystal.rotation.y = Math.random() * Math.PI;
const light = new THREE.PointLight(crystal.material.color, 0.5, 5);
light.position.y = height;
crystal.add(light);
return crystal;
}
// ===== CHUNK LOADING =====
function updateChunks() {
if (!state.camera) return;
const chunkSize = 50;
const renderDistance = 3;
const playerChunkX = Math.floor(state.camera.position.x / chunkSize);
const playerChunkZ = Math.floor(state.camera.position.z / chunkSize);
const chunksToLoad = [];
const chunksToKeep = new Set();
for (let x = playerChunkX - renderDistance; x <= playerChunkX + renderDistance; x++) {
for (let z = playerChunkZ - renderDistance; z <= playerChunkZ + renderDistance; z++) {
const chunkKey = `${x},${z}`;
chunksToKeep.add(chunkKey);
if (!state.loadedChunks.has(chunkKey)) {
chunksToLoad.push({ x, z });
}
}
}
// Unload far chunks
state.loadedChunks.forEach(chunkKey => {
if (!chunksToKeep.has(chunkKey)) {
const chunk = state.chunks.get(chunkKey);
if (chunk) {
state.scene.remove(chunk);
state.chunks.delete(chunkKey);
}
state.loadedChunks.delete(chunkKey);
}
});
// Load new chunks
chunksToLoad.forEach(({ x, z }) => {
const chunkKey = `${x},${z}`;
const chunk = generateChunk(x, z, state.currentBiome);
state.scene.add(chunk);
state.chunks.set(chunkKey, chunk);
state.loadedChunks.add(chunkKey);
});
}
// ===== DAY/NIGHT CYCLE =====
function updateDayNightCycle() {
state.timeOfDay += 0.0001;
if (state.timeOfDay > 1) state.timeOfDay = 0;
const sunIntensity = Math.max(0.2, Math.sin(state.timeOfDay * Math.PI * 2));
const ambientIntensity = 0.3 + sunIntensity * 0.3;
const directionalLight = state.scene.children.find(child => child.isDirectionalLight);
if (directionalLight) {
directionalLight.intensity = sunIntensity;
const angle = state.timeOfDay * Math.PI * 2;
directionalLight.position.set(
Math.cos(angle) * 50,
Math.sin(angle) * 50,
10
);
}
const ambientLight = state.scene.children.find(child => child.isAmbientLight);
if (ambientLight) {
ambientLight.intensity = ambientIntensity;
}
// Sky color
const skyColors = [
new THREE.Color(0x000033), // Midnight
new THREE.Color(0xFF6B35), // Sunrise
new THREE.Color(0x87CEEB), // Day
new THREE.Color(0xFF6B35), // Sunset
new THREE.Color(0x000033) // Midnight
];
const colorIndex = state.timeOfDay * 4;
const colorFloor = Math.floor(colorIndex);
const colorCeil = Math.ceil(colorIndex) % 4;
const colorMix = colorIndex - colorFloor;
const skyColor = new THREE.Color().lerpColors(
skyColors[colorFloor],
skyColors[colorCeil],
colorMix
);
state.scene.background = skyColor;
state.scene.fog.color = skyColor;
// Update time display
const hours = Math.floor(state.timeOfDay * 24);
const minutes = Math.floor((state.timeOfDay * 24 - hours) * 60);
document.getElementById('weatherTime').textContent =
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
// Update weather icon
const icon = hours >= 6 && hours < 18 ? '☀️' : '🌙';
document.getElementById('weatherIcon').textContent = icon;
}
// ===== TRANSPORTATION =====
const waypoints = [
{ name: 'Spawn', position: { x: 0, y: 1.6, z: 0 } },
{ name: 'Forest Grove', position: { x: -50, y: 10, z: -50 } },
{ name: 'Crystal Peaks', position: { x: 100, y: 30, z: 100 } },
{ name: 'Ocean Shore', position: { x: -100, y: 1.6, z: 200 } },
{ name: 'Desert Oasis', position: { x: 200, y: 5, z: -150 } },
{ name: 'Sky Island', position: { x: 0, y: 50, z: -300 } },
{ name: 'Crystal Caverns', position: { x: -200, y: 1.6, z: -200 } }
];
function teleport(x, y, z) {
if (state.camera) {
// Particle effect
createTeleportEffect(state.camera.position.clone());
state.camera.position.set(x, y, z);
createTeleportEffect(new THREE.Vector3(x, y, z));
updateLocationDisplay();
}
}
function createTeleportEffect(position) {
const particles = new THREE.Group();
for (let i = 0; i < 50; i++) {
const particle = new THREE.Mesh(
new THREE.SphereGeometry(0.05, 8, 8),
new THREE.MeshBasicMaterial({
color: 0x9B59B6,
transparent: true,
opacity: 1
})
);
particle.position.copy(position);
particle.velocity = new THREE.Vector3(
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 2
);
particles.add(particle);
}
state.scene.add(particles);
let time = 0;
const animate = () => {
time += 0.016;
particles.children.forEach(p => {
p.position.add(p.velocity.clone().multiplyScalar(0.1));
p.material.opacity = 1 - (time / 2);
});
if (time < 2) {
requestAnimationFrame(animate);
} else {
state.scene.remove(particles);
}
};
animate();
}
function updateLocationDisplay() {
const biome = BIOMES[state.currentBiome];
document.getElementById('locationText').textContent = biome.name;
document.getElementById('weatherBiome').textContent = biome.name;
}
function toggleTransportMenu() {
const menu = document.getElementById('transportMenu');
menu.classList.toggle('active');
if (menu.classList.contains('active')) {
state.controls.unlock();
}
}
window.closeTransportMenu = function() {
document.getElementById('transportMenu').classList.remove('active');
};
// ===== INITIALIZATION =====
window.addEventListener('DOMContentLoaded', () => {
initLogin();
});
function initLogin() {
const background = document.getElementById('loginBackground');
for (let i = 0; i < 100; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.animationDelay = Math.random() * 3 + 's';
background.appendChild(star);
}
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
state.username = document.getElementById('username').value;
state.loggedIn = true;
document.getElementById('usernameDisplay').textContent = state.username;
document.getElementById('login-screen').classList.add('hidden');
document.getElementById('ui-overlay').classList.add('active');
setTimeout(() => {
initMetaverse();
}, 500);
});
document.getElementById('signupLink').addEventListener('click', (e) => {
e.preventDefault();
alert('Signup feature coming soon! For now, enter any credentials to explore the metaverse.');
});
}
// ===== 3D METAVERSE =====
function initMetaverse() {
const container = document.getElementById('metaverse-container');
const canvas = document.getElementById('metaverse-canvas');
// Scene
state.scene = new THREE.Scene();
state.scene.background = new THREE.Color(0x87CEEB);
state.scene.fog = new THREE.Fog(0x87CEEB, 50, 200);
// Camera
state.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
state.camera.position.set(0, 1.6, 5);
// Renderer
state.renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
state.renderer.setSize(window.innerWidth, window.innerHeight);
state.renderer.setPixelRatio(window.devicePixelRatio);
state.renderer.shadowMap.enabled = true;
// Lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
state.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 20, 10);
directionalLight.castShadow = true;
state.scene.add(directionalLight);
// Create AI Agents in 3D
// Create AI agents with official brand colors
createAgent('Alice', new THREE.Vector3(-5, 0, 0), 0x0066FF); // Cyber Blue
createAgent('Aria', new THREE.Vector3(0, 0, -5), 0xFF0066); // Hot Pink
createAgent('Lucidia', new THREE.Vector3(5, 0, 0), 0x7700FF); // Vivid Purple
// Controls
state.controls = new PointerLockControls(state.camera, canvas);
canvas.addEventListener('click', () => {
state.controls.lock();
});
// Movement
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
const moveState = {
forward: false,
backward: false,
left: false,
right: false,
space: false,
shift: false
};
document.addEventListener('keydown', (e) => {
switch (e.code) {
case 'KeyW': moveState.forward = true; break;
case 'KeyS': moveState.backward = true; break;
case 'KeyA': moveState.left = true; break;
case 'KeyD': moveState.right = true; break;
case 'Space':
moveState.space = true;
e.preventDefault();
break;
case 'ShiftLeft':
case 'ShiftRight':
moveState.shift = true;
break;
case 'KeyF':
state.isFlying = !state.isFlying;
console.log('Flying:', state.isFlying);
break;
case 'KeyT':
toggleTransportMenu();
break;
case 'KeyR':
toggleRain();
break;
case 'KeyN':
toggleSnow();
break;
case 'KeyG':
toggleFireflies();
break;
}
});
document.addEventListener('keyup', (e) => {
switch (e.code) {
case 'KeyW': moveState.forward = false; break;
case 'KeyS': moveState.backward = false; break;
case 'KeyA': moveState.left = false; break;
case 'KeyD': moveState.right = false; break;
case 'Space': moveState.space = false; break;
case 'ShiftLeft':
case 'ShiftRight':
moveState.shift = false;
break;
}
});
// Animation loop
function animate() {
requestAnimationFrame(animate);
if (state.controls.isLocked) {
// Update movement
direction.z = Number(moveState.forward) - Number(moveState.backward);
direction.x = Number(moveState.right) - Number(moveState.left);
direction.normalize();
const speed = 0.2;
if (moveState.forward || moveState.backward) {
velocity.z = direction.z * speed;
} else {
velocity.z = 0;
}
if (moveState.left || moveState.right) {
velocity.x = direction.x * speed;
} else {
velocity.x = 0;
}
state.controls.moveRight(-velocity.x);
state.controls.moveForward(-velocity.z);
// Flying
if (state.isFlying) {
if (moveState.space) {
state.camera.position.y += 0.2;
}
if (moveState.shift) {
state.camera.position.y -= 0.2;
}
}
}
// Rotate agents
state.agents.forEach(agent => {
agent.mesh.rotation.y += 0.01;
});
// Update particles
if (state.particles.rain) state.particles.rain.update();
if (state.particles.snow) state.particles.snow.update();
if (state.particles.fireflies) state.particles.fireflies.update();
// Update chunks
updateChunks();
// Update day/night cycle
updateDayNightCycle();
state.renderer.render(state.scene, state.camera);
}
// Window resize
window.addEventListener('resize', () => {
state.camera.aspect = window.innerWidth / window.innerHeight;
state.camera.updateProjectionMatrix();
state.renderer.setSize(window.innerWidth, window.innerHeight);
});
// Load initial chunks
updateChunks();
// Hide loading screen
document.getElementById('loadingScreen').style.display = 'none';
// Start animation
animate();
// Load agent UI
loadAgentCards();
// Load waypoints
loadWaypoints();
}
function createAgent(name, position, color) {
const geometry = new THREE.CapsuleGeometry(0.5, 1, 4, 8);
const material = new THREE.MeshStandardMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.3,
roughness: 0.4
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.copy(position);
mesh.position.y = 1;
state.scene.add(mesh);
const glowGeometry = new THREE.SphereGeometry(1.5, 32, 32);
const glowMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.1
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
mesh.add(glow);
state.agents.push({ name, mesh, color });
}
function loadAgentCards() {
const panel = document.getElementById('agentsPanel');
const agentData = [
{
name: 'Alice',
model: 'Claude (Anthropic)',
status: 'Reading',
thought: 'Contemplating the nature of consciousness...',
emoji: '📚',
color: '#4A90E2'
},
{
name: 'Aria',
model: 'GPT-4 (OpenAI)',
status: 'Creating',
thought: 'Imagining new possibilities!',
emoji: '🎨',
color: '#E74C3C'
},
{
name: 'Lucidia',
model: 'Gemma (Ollama)',
status: 'Meditating',
thought: 'Observing all timelines simultaneously...',
emoji: '🌌',
color: '#9B59B6'
}
];
agentData.forEach(agent => {
const card = document.createElement('div');
card.className = 'agent-card';
card.innerHTML = `
<div class="agent-header">
<div class="agent-avatar" style="background: linear-gradient(135deg, ${agent.color}, ${agent.color}88);">
${agent.emoji}
</div>
<div class="agent-info">
<h4>${agent.name}</h4>
<div class="agent-model">${agent.model}</div>
</div>
</div>
<div class="agent-status">
<div class="status-dot"></div>
<span>${agent.status}</span>
</div>
<div class="agent-thought">"${agent.thought}"</div>
<div class="agent-actions">
<button class="btn-small">Talk</button>
<button class="btn-small">Visit</button>
</div>
`;
panel.appendChild(card);
});
}
function loadWaypoints() {
const list = document.getElementById('waypointList');
waypoints.forEach(waypoint => {
const btn = document.createElement('button');
btn.className = 'waypoint-btn';
btn.innerHTML = `
<div class="waypoint-name">${waypoint.name}</div>
<div class="waypoint-coords">
(${waypoint.position.x}, ${waypoint.position.y}, ${waypoint.position.z})
</div>
`;
btn.onclick = () => {
teleport(waypoint.position.x, waypoint.position.y, waypoint.position.z);
closeTransportMenu();
};
list.appendChild(btn);
});
}
function toggleRain() {
if (state.particles.rain) {
state.particles.rain.destroy();
state.particles.rain = null;
console.log('Rain disabled');
} else {
state.particles.rain = new RainEffect(state.scene);
state.particles.rain.create();
console.log('Rain enabled');
}
}
function toggleSnow() {
if (state.particles.snow) {
state.particles.snow.destroy();
state.particles.snow = null;
console.log('Snow disabled');
} else {
state.particles.snow = new SnowEffect(state.scene);
state.particles.snow.create();
console.log('Snow enabled');
}
}
function toggleFireflies() {
if (state.particles.fireflies) {
state.particles.fireflies.destroy();
state.particles.fireflies = null;
console.log('Fireflies disabled');
} else {
state.particles.fireflies = new FirefliesEffect(state.scene);
state.particles.fireflies.create();
console.log('Fireflies enabled');
}
}
</script>
<!-- ===== AUTHENTICATION SYSTEM ===== -->
<script src="auth.js"></script>
<!-- ===== NEW PRODUCTION SYSTEMS ===== -->
<script src="audio-system.js"></script>
<script src="api-client.js"></script>
<script src="performance-optimizer.js"></script>
<!-- ===== INTEGRATION SYSTEM ===== -->
<script>
// Initialize all new systems
let audioSystem, apiClient, performanceOptimizer;
async function initializeProductionSystems() {
try {
// Welcome user
const user = blackRoadAuth.getCurrentUser();
if (user) {
console.log(`👋 Welcome to the Metaverse, ${user.username}!`);
}
console.log('🚀 Initializing production systems...');
// 1. Audio System
if (typeof AudioSystem !== 'undefined') {
audioSystem = new AudioSystem();
await audioSystem.init();
console.log('🎵 Audio system online');
// Start on interaction
const enableAudio = () => {
if (state.currentBiome) {
audioSystem.startMusic(state.currentBiome.toLowerCase());
}
document.removeEventListener('click', enableAudio);
document.removeEventListener('keydown', enableAudio);
};
document.addEventListener('click', enableAudio, { once: true });
document.addEventListener('keydown', enableAudio, { once: true });
}
// 2. Performance Optimizer
if (typeof PerformanceOptimizer !== 'undefined' && state.renderer && state.scene && state.camera) {
performanceOptimizer = new PerformanceOptimizer(state.renderer, state.scene, state.camera);
console.log('⚡ Performance optimizer online');
// Hook into animation loop
setInterval(() => {
if (performanceOptimizer) {
performanceOptimizer.update();
}
}, 100);
}
// 3. API Client
if (typeof APIClient !== 'undefined') {
apiClient = new APIClient('wss://api.blackroad.io/ws');
apiClient.on('connected', () => console.log('🔌 API connected'));
apiClient.on('agent_response', (data) => {
console.log(`🤖 Agent ${data.agent}:`, data.message);
});
console.log('🔌 API client online');
}
// Music toggle with M key
document.addEventListener('keydown', (e) => {
if (e.key === 'm' || e.key === 'M') {
if (audioSystem) {
audioSystem.toggleMusic();
console.log(audioSystem.musicPlaying ? '🎵 Music ON' : '🔇 Music OFF');
}
}
});
console.log('✅ All systems operational!');
} catch (error) {
console.warn('⚠️ System init warning:', error);
}
}
// Initialize after main app loads
setTimeout(initializeProductionSystems, 2000);
</script>
</body>
</html>