3d-effects / index.html
LukasBe's picture
Add 2 files
dcdc8d7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quantum Nexus™ 3D Energy Matrix</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.11.4/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/postprocessing/EffectComposer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/postprocessing/RenderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/postprocessing/ShaderPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/postprocessing/UnrealBloomPass.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/shaders/CopyShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/shaders/LuminosityHighPassShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/shaders/HorizontalBlurShader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/shaders/VerticalBlurShader.js"></script>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
font-family: 'Inter', sans-serif;
}
#three-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
#ui-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.ui-panel {
pointer-events: all;
background: rgba(15, 23, 42, 0.7);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
border-radius: 16px;
transition: all 0.3s ease;
}
.ui-panel:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.4);
}
.energy-cell {
background: linear-gradient(145deg, rgba(30, 41, 59, 0.8), rgba(15, 23, 42, 0.9));
border-radius: 12px;
transition: all 0.3s ease;
}
.energy-cell:hover {
transform: translateY(-3px) scale(1.02);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.4);
}
.flow-line {
position: relative;
overflow: hidden;
}
.flow-line::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, currentColor, transparent);
background-size: 200% 100%;
animation: energy-flow 2s linear infinite;
opacity: 0.5;
}
@keyframes energy-flow {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
.pulse-glow {
animation: pulse-glow 2s ease-in-out infinite;
}
@keyframes pulse-glow {
0% { opacity: 0.7; box-shadow: 0 0 5px currentColor; }
50% { opacity: 1; box-shadow: 0 0 15px currentColor; }
100% { opacity: 0.7; box-shadow: 0 0 5px currentColor; }
}
.gradient-text {
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
.glow-border {
position: relative;
}
.glow-border::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.5), rgba(14, 165, 233, 0.5));
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.energy-badge {
transition: all 0.3s ease;
}
.energy-badge:hover {
transform: scale(1.05);
box-shadow: 0 0 15px currentColor;
}
.grid-node {
position: absolute;
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: white;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
}
.grid-node:hover {
transform: scale(1.2);
}
/* New styles for advanced effects */
.particle-trail {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.energy-pulse {
position: absolute;
border-radius: 50%;
background: rgba(59, 130, 246, 0.5);
transform: scale(0);
animation: energy-pulse 3s infinite;
pointer-events: none;
}
@keyframes energy-pulse {
0% { transform: scale(0); opacity: 1; }
100% { transform: scale(3); opacity: 0; }
}
</style>
</head>
<body class="bg-slate-950 text-slate-100">
<!-- Three.js Container -->
<div id="three-container"></div>
<!-- Particle Trails -->
<div id="particle-trail" class="particle-trail"></div>
<!-- UI Container -->
<div id="ui-container">
<!-- Header -->
<header class="flex flex-col md:flex-row justify-between items-start md:items-center p-6">
<div class="mb-6 md:mb-0">
<div class="flex items-center">
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-cyan-400 flex items-center justify-center mr-4 shadow-lg">
<i class="fas fa-bolt text-white text-xl"></i>
</div>
<div>
<h1 class="text-4xl font-bold bg-gradient-to-r from-blue-400 to-cyan-300 gradient-text">
Quantum Nexus™
</h1>
<p class="text-slate-400 font-medium">3D Energy Matrix Intelligence</p>
</div>
</div>
</div>
<div class="flex flex-col md:flex-row items-start md:items-center space-y-4 md:space-y-0 md:space-x-6 w-full md:w-auto">
<div class="flex items-center space-x-4">
<div class="ui-panel p-3 rounded-xl shadow-sm flex items-center">
<div class="w-3 h-3 rounded-full bg-green-500 mr-2 pulse-glow"></div>
<div>
<p class="text-xs text-slate-400">Matrix Status</p>
<p class="font-medium">Live: <span id="update-time" class="text-slate-100">Just now</span></p>
</div>
</div>
<button class="ui-panel bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white px-6 py-3 rounded-xl flex items-center shadow-lg transition-all duration-300 hover:shadow-xl">
<i class="fas fa-network-wired mr-2"></i> Matrix Control
</button>
</div>
</div>
</header>
<!-- Main Dashboard -->
<div class="absolute bottom-0 left-0 right-0 p-6">
<div class="ui-panel p-6 rounded-2xl max-w-4xl mx-auto">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold flex items-center">
<i class="fas fa-project-diagram text-blue-400 mr-3 pulse-glow"></i>
Quantum Energy Matrix
</h2>
<div class="bg-slate-800/70 px-4 py-1 rounded-full text-sm border border-slate-700/50">
<span class="text-green-400 font-medium">Optimal</span> Performance
</div>
</div>
<!-- Grid Layout -->
<div class="grid grid-cols-3 gap-4">
<!-- Solar -->
<div class="col-span-1 flex flex-col items-center">
<div class="energy-cell w-full h-32 bg-yellow-900/20 border border-yellow-500/30 flex flex-col items-center justify-center p-4 glow-border" id="solar-cell">
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-yellow-400 to-yellow-600 flex items-center justify-center mb-2 shadow-lg">
<i class="fas fa-solar-panel text-white text-2xl"></i>
</div>
<h3 class="font-bold text-yellow-400 text-center">Solar Array</h3>
<p class="text-sm text-yellow-300" id="solar-value">5.2 kW</p>
</div>
</div>
<!-- Battery -->
<div class="col-span-1 flex flex-col items-center">
<div class="energy-cell w-full h-32 bg-orange-900/20 border border-orange-500/30 flex flex-col items-center justify-center p-4 glow-border" id="battery-cell">
<div class="w-16 h-16 rounded-lg bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center mb-2 shadow-lg">
<i class="fas fa-battery-three-quarters text-white text-2xl"></i>
</div>
<h3 class="font-bold text-orange-400 text-center">Battery Bank</h3>
<p class="text-sm text-orange-300" id="battery-value">78%</p>
</div>
</div>
<!-- Grid -->
<div class="col-span-1 flex flex-col items-center">
<div class="energy-cell w-full h-32 bg-slate-800/20 border border-slate-500/30 flex flex-col items-center justify-center p-4 glow-border" id="grid-cell">
<div class="w-16 h-16 rounded-lg bg-gradient-to-br from-slate-400 to-slate-600 flex items-center justify-center mb-2 shadow-lg">
<i class="fas fa-plug text-white text-2xl"></i>
</div>
<h3 class="font-bold text-slate-300 text-center">Grid</h3>
<p class="text-sm text-slate-300" id="grid-value">1.9 kW</p>
</div>
</div>
<!-- Status -->
<div class="col-span-3 mt-4 text-center">
<p class="text-slate-400 text-sm">Matrix Status</p>
<p class="text-xl font-bold text-green-400" id="flow-mode">Solar Exporting (1.9 kW to grid)</p>
</div>
</div>
</div>
</div>
<!-- Side Panels -->
<div class="absolute top-24 right-6 w-80 space-y-6">
<!-- System Overview -->
<div class="ui-panel p-6 rounded-2xl">
<h2 class="text-xl font-semibold mb-6 flex items-center">
<i class="fas fa-chart-network text-blue-400 mr-3"></i>
System Overview
</h2>
<div class="space-y-4">
<div class="flex justify-between items-center">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-blue-900/50 flex items-center justify-center mr-3">
<i class="fas fa-solar-panel text-yellow-400"></i>
</div>
<div>
<p class="text-sm text-slate-400">Solar Production</p>
<p class="font-bold text-yellow-400">5.2 kW</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-slate-400">Today</p>
<p class="font-bold text-yellow-400">24.8 kWh</p>
</div>
</div>
<div class="flex justify-between items-center">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-orange-900/50 flex items-center justify-center mr-3">
<i class="fas fa-battery-three-quarters text-orange-400"></i>
</div>
<div>
<p class="text-sm text-slate-400">Battery Storage</p>
<p class="font-bold text-orange-400">78%</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-slate-400">Capacity</p>
<p class="font-bold text-orange-400">14.2 kWh</p>
</div>
</div>
<div class="flex justify-between items-center">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-blue-900/50 flex items-center justify-center mr-3">
<i class="fas fa-home text-blue-400"></i>
</div>
<div>
<p class="text-sm text-slate-400">Home Usage</p>
<p class="font-bold text-blue-400">3.3 kW</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-slate-400">Today</p>
<p class="font-bold text-blue-400">18.6 kWh</p>
</div>
</div>
<div class="flex justify-between items-center">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-slate-700/50 flex items-center justify-center mr-3">
<i class="fas fa-plug text-slate-300"></i>
</div>
<div>
<p class="text-sm text-slate-400">Grid Export</p>
<p class="font-bold text-green-400">1.9 kW</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-slate-400">Today</p>
<p class="font-bold text-green-400">6.2 kWh</p>
</div>
</div>
</div>
</div>
<!-- Efficiency Metrics -->
<div class="ui-panel p-6 rounded-2xl">
<h2 class="text-xl font-semibold mb-6 flex items-center">
<i class="fas fa-tachometer-alt text-cyan-400 mr-3"></i>
Efficiency Metrics
</h2>
<div class="space-y-6">
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-slate-400">Solar Utilization</span>
<span class="text-sm font-medium text-yellow-400">92%</span>
</div>
<div class="w-full bg-slate-800/50 rounded-full h-2">
<div class="bg-gradient-to-r from-yellow-500 to-yellow-300 h-2 rounded-full" style="width: 92%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-slate-400">Battery Efficiency</span>
<span class="text-sm font-medium text-orange-400">88%</span>
</div>
<div class="w-full bg-slate-800/50 rounded-full h-2">
<div class="bg-gradient-to-r from-orange-500 to-orange-300 h-2 rounded-full" style="width: 88%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-slate-400">Energy Independence</span>
<span class="text-sm font-medium text-green-400">94%</span>
</div>
<div class="w-full bg-slate-800/50 rounded-full h-2">
<div class="bg-gradient-to-r from-green-500 to-green-300 h-2 rounded-full" style="width: 94%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 3D Grid Nodes -->
<div class="grid-node" style="top: 30%; left: 20%; background: rgba(245, 158, 11, 0.2); border: 2px solid rgba(245, 158, 11, 0.5);" data-type="solar">
<i class="fas fa-solar-panel text-yellow-400"></i>
</div>
<div class="grid-node" style="top: 40%; left: 70%; background: rgba(59, 130, 246, 0.2); border: 2px solid rgba(59, 130, 246, 0.5);" data-type="home">
<i class="fas fa-home text-blue-400"></i>
</div>
<div class="grid-node" style="top: 60%; left: 30%; background: rgba(249, 115, 22, 0.2); border: 2px solid rgba(249, 115, 22, 0.5);" data-type="battery">
<i class="fas fa-battery-three-quarters text-orange-400"></i>
</div>
<div class="grid-node" style="top: 70%; left: 80%; background: rgba(156, 163, 175, 0.2); border: 2px solid rgba(156, 163, 175, 0.5);" data-type="grid">
<i class="fas fa-plug text-slate-300"></i>
</div>
</div>
<script>
// Three.js Scene Setup
let scene, camera, renderer, controls, composer;
let energyLines = [];
let energyParticles = [];
let bloomPass;
let energyNodes = [];
let floatingTexts = [];
let floatingIcons = [];
let particleSystems = [];
let holographicGrid;
let energyPulseEffects = [];
let floatingEnergyOrbs = [];
// Post-processing effects
function initPostProcessing() {
const renderScene = new THREE.RenderPass(scene, camera);
bloomPass = new THREE.UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // strength
0.4, // radius
0.85 // threshold
);
composer = new THREE.EffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);
}
// Create a holographic grid floor
function createHolographicGrid() {
const size = 50;
const divisions = 50;
const gridHelper = new THREE.GridHelper(size, divisions, 0x3b82f6, 0x1e293b);
gridHelper.position.y = 0.01;
gridHelper.material.transparent = true;
gridHelper.material.opacity = 0.3;
scene.add(gridHelper);
holographicGrid = gridHelper;
// Add pulsing center point
const centerGeometry = new THREE.CircleGeometry(1, 32);
const centerMaterial = new THREE.MeshBasicMaterial({
color: 0x3b82f6,
transparent: true,
opacity: 0.5,
blending: THREE.AdditiveBlending
});
const centerCircle = new THREE.Mesh(centerGeometry, centerMaterial);
centerCircle.rotation.x = -Math.PI / 2;
centerCircle.position.y = 0.02;
scene.add(centerCircle);
// Animation
gsap.to(centerCircle.scale, {
x: 1.5,
y: 1.5,
z: 1.5,
duration: 3,
repeat: -1,
yoyo: true,
ease: "sine.inOut"
});
gsap.to(centerCircle.material, {
opacity: 0.8,
duration: 3,
repeat: -1,
yoyo: true,
ease: "sine.inOut"
});
}
// Create floating energy orbs
function createFloatingEnergyOrbs() {
const orbCount = 15;
const orbGeometry = new THREE.SphereGeometry(0.3, 32, 32);
for (let i = 0; i < orbCount; i++) {
const color = new THREE.Color(
Math.random() * 0.5 + 0.5,
Math.random() * 0.5 + 0.5,
Math.random() * 0.5 + 0.5
);
const orbMaterial = new THREE.MeshStandardMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.5,
roughness: 0.2,
metalness: 0.7,
transparent: true,
opacity: 0.8
});
const orb = new THREE.Mesh(orbGeometry, orbMaterial);
// Random position in a sphere
const radius = 15;
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
orb.position.x = radius * Math.sin(phi) * Math.cos(theta);
orb.position.y = radius * Math.cos(phi) + 5;
orb.position.z = radius * Math.sin(phi) * Math.sin(theta);
// Random velocity
orb.userData = {
velocity: new THREE.Vector3(
(Math.random() - 0.5) * 0.02,
(Math.random() - 0.5) * 0.02,
(Math.random() - 0.5) * 0.02
),
origin: orb.position.clone()
};
scene.add(orb);
floatingEnergyOrbs.push(orb);
// Add glow
const glowGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const glowMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.3,
blending: THREE.AdditiveBlending
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
glow.position.copy(orb.position);
scene.add(glow);
// Animation
gsap.to(orb.scale, {
x: 1.5,
y: 1.5,
z: 1.5,
duration: 2 + Math.random() * 3,
repeat: -1,
yoyo: true,
ease: "sine.inOut"
});
}
}
// Create particle system
function createParticleSystem() {
const particleCount = 2000;
const particles = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const sizes = new Float32Array(particleCount);
const radius = 30;
for (let i = 0; i < particleCount; i++) {
// Position particles in a sphere
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = radius * Math.cos(phi);
positions[i * 3 + 2] = radius * Math.sin(phi) * Math.sin(theta);
// Random colors
colors[i * 3] = 0.5 + Math.random() * 0.5; // R
colors[i * 3 + 1] = 0.5 + Math.random() * 0.5; // G
colors[i * 3 + 2] = 0.5 + Math.random() * 0.5; // B
// Random sizes
sizes[i] = 0.1 + Math.random() * 0.5;
}
particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));
particles.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const particleMaterial = new THREE.PointsMaterial({
size: 0.2,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending,
sizeAttenuation: true
});
const particleSystem = new THREE.Points(particles, particleMaterial);
scene.add(particleSystem);
particleSystems.push(particleSystem);
}
// Create energy pulse effects
function createEnergyPulseEffects() {
const pulseCount = 5;
for (let i = 0; i < pulseCount; i++) {
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshBasicMaterial({
color: 0x3b82f6,
transparent: true,
opacity: 0,
blending: THREE.AdditiveBlending
});
const pulse = new THREE.Mesh(geometry, material);
pulse.position.set(
(Math.random() - 0.5) * 20,
Math.random() * 5,
(Math.random() - 0.5) * 20
);
scene.add(pulse);
energyPulseEffects.push({
mesh: pulse,
speed: 0.5 + Math.random(),
maxSize: 5 + Math.random() * 10,
delay: Math.random() * 5
});
}
}
// Create floating text
function createFloatingText(text, position, color) {
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 256;
const context = canvas.getContext('2d');
context.font = "Bold 48px Arial";
context.fillStyle = `rgba(${color.r * 255}, ${color.g * 255}, ${color.b * 255}, 0.8)`;
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(text, canvas.width / 2, canvas.height / 2);
const texture = new THREE.CanvasTexture(canvas);
const material = new THREE.SpriteMaterial({
map: texture,
transparent: true,
blending: THREE.AdditiveBlending
});
const sprite = new THREE.Sprite(material);
sprite.scale.set(5, 2.5, 1);
sprite.position.copy(position);
scene.add(sprite);
floatingTexts.push({
sprite: sprite,
originalPosition: position.clone(),
offset: Math.random() * Math.PI * 2
});
}
// Create floating icons
function createFloatingIcons() {
const iconTypes = [
{ icon: 'fa-bolt', color: 0xf59e0b },
{ icon: 'fa-charging-station', color: 0x3b82f6 },
{ icon: 'fa-leaf', color: 0x10b981 },
{ icon: 'fa-fire', color: 0xf97316 },
{ icon: 'fa-water', color: 0x0ea5e9 }
];
const iconCount = 10;
for (let i = 0; i < iconCount; i++) {
const type = iconTypes[Math.floor(Math.random() * iconTypes.length)];
const canvas = document.createElement('canvas');
canvas.width = 128;
canvas.height = 128;
const context = canvas.getContext('2d');
// Draw icon
context.font = "Normal 72px FontAwesome";
context.fillStyle = `rgba(${(type.color >> 16) & 0xff}, ${(type.color >> 8) & 0xff}, ${type.color & 0xff}, 0.8)`;
context.textAlign = 'center';
context.textBaseline = 'middle';
// Get icon character from FontAwesome
const iconChar = String.fromCharCode(parseInt(getIconUnicode(type.icon), 16));
context.fillText(iconChar, canvas.width / 2, canvas.height / 2);
const texture = new THREE.CanvasTexture(canvas);
const material = new THREE.SpriteMaterial({
map: texture,
transparent: true,
blending: THREE.AdditiveBlending
});
const sprite = new THREE.Sprite(material);
sprite.scale.set(2, 2, 1);
// Position in a sphere
const radius = 10 + Math.random() * 10;
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
sprite.position.set(
radius * Math.sin(phi) * Math.cos(theta),
radius * Math.cos(phi) + 5,
radius * Math.sin(phi) * Math.sin(theta)
);
scene.add(sprite);
floatingIcons.push({
sprite: sprite,
originalPosition: sprite.position.clone(),
speed: 0.01 + Math.random() * 0.02,
offset: Math.random() * Math.PI * 2
});
}
}
// Helper function to get FontAwesome unicode
function getIconUnicode(iconName) {
const icons = {
'fa-bolt': 'f0e7',
'fa-charging-station': 'f5e7',
'fa-leaf': 'f06c',
'fa-fire': 'f06d',
'fa-water': 'f773'
};
return icons[iconName] || 'f111'; // default circle
}
function initThreeJS() {
// Create scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0f172a);
scene.fog = new THREE.FogExp2(0x0f172a, 0.002);
// Create camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 20);
// Create renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputEncoding = THREE.sRGBEncoding;
document.getElementById('three-container').appendChild(renderer.domElement);
// Add controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 5;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI * 0.9;
controls.minPolarAngle = Math.PI * 0.2;
// Add lights
const ambientLight = new THREE.AmbientLight(0x404040, 2);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0x4fd1ff, 3, 20);
pointLight.position.set(0, 5, 0);
pointLight.castShadow = true;
scene.add(pointLight);
// Add hemisphere light for more natural lighting
const hemisphereLight = new THREE.HemisphereLight(0x3b82f6, 0xf59e0b, 0.5);
scene.add(hemisphereLight);
// Initialize post-processing
initPostProcessing();
// Create complex environment
createHolographicGrid();
createParticleSystem();
createFloatingEnergyOrbs();
createEnergyPulseEffects();
createFloatingIcons();
// Create floating text
createFloatingText("QUANTUM NEXUS", new THREE.Vector3(0, 15, 0), new THREE.Color(0x3b82f6));
createFloatingText("ENERGY MATRIX", new THREE.Vector3(0, 12, 0), new THREE.Color(0xf59e0b));
// Create energy nodes
createEnergyNode('solar', new THREE.Vector3(-10, 2, -8), 0xf59e0b);
createEnergyNode('battery', new THREE.Vector3(0, 2, 8), 0xf97316);
createEnergyNode('home', new THREE.Vector3(10, 2, -8), 0x3b82f6);
createEnergyNode('grid', new THREE.Vector3(0, 2, -15), 0x9ca3af);
// Create energy connections
createEnergyConnection('solar', 'home', 0xf59e0b);
createEnergyConnection('solar', 'battery', 0xf59e0b);
createEnergyConnection('battery', 'home', 0xf97316);
createEnergyConnection('home', 'grid', 0x3b82f6);
// Create secondary connections
createEnergyConnection('solar', 'grid', 0xf59e0b);
createEnergyConnection('battery', 'grid', 0xf97316);
// Create decorative connections
createDecorativeConnections();
// Handle window resize
window.addEventListener('resize', onWindowResize);
// Start animation loop
animate();
}
// Create decorative connections between floating orbs
function createDecorativeConnections() {
for (let i = 0; i < floatingEnergyOrbs.length; i++) {
for (let j = i + 1; j < Math.min(i + 3, floatingEnergyOrbs.length); j++) {
const fromOrb = floatingEnergyOrbs[i];
const toOrb = floatingEnergyOrbs[j];
// Only connect if within certain distance
if (fromOrb.position.distanceTo(toOrb.position) < 15) {
const lineGeometry = new THREE.BufferGeometry().setFromPoints([
fromOrb.position,
toOrb.position
]);
const lineMaterial = new THREE.LineBasicMaterial({
color: 0x3b82f6,
transparent: true,
opacity: 0.1,
linewidth: 1
});
const line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line);
// Store reference to update positions
energyLines.push({
line: line,
from: fromOrb,
to: toOrb
});
}
}
}
}
function createEnergyNode(type, position, color) {
// Main sphere
const geometry = new THREE.SphereGeometry(1.5, 32, 32);
const material = new THREE.MeshStandardMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.5,
roughness: 0.2,
metalness: 0.8
});
const sphere = new THREE.Mesh(geometry, material);
sphere.position.copy(position);
sphere.castShadow = true;
sphere.receiveShadow = true;
sphere.userData = { type: type };
scene.add(sphere);
energyNodes.push(sphere);
// Pulsing animation
gsap.to(sphere.scale, {
x: 1.3,
y: 1.3,
z: 1.3,
duration: 2,
repeat: -1,
yoyo: true,
ease: "sine.inOut"
});
// Glow effect
const glowGeometry = new THREE.SphereGeometry(2, 32, 32);
const glowMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.3,
blending: THREE.AdditiveBlending
});
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
glow.position.copy(position);
scene.add(glow);
// Ring around the sphere
const ringGeometry = new THREE.TorusGeometry(2.2, 0.1, 16, 100);
const ringMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.5,
blending: THREE.AdditiveBlending
});
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
ring.position.copy(position);
ring.rotation.x = Math.PI / 2;
scene.add(ring);
// Rotate ring
gsap.to(ring.rotation, {
y: Math.PI * 2,
duration: 20,
repeat: -1,
ease: "none"
});
// Floating particles around node
createNodeParticles(position, color);
return sphere;
}
// Create particles swirling around energy nodes
function createNodeParticles(position, color) {
const particleCount = 50;
const particlesGeometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
// Position particles in a sphere around the node
const radius = 3;
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
positions[i * 3] = position.x + radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = position.y + radius * Math.cos(phi);
positions[i * 3 + 2] = position.z + radius * Math.sin(phi) * Math.sin(theta);
// Color particles
colors[i * 3] = (color >> 16 & 255) / 255;
colors[i * 3 + 1] = (color >> 8 & 255) / 255;
colors[i * 3 + 2] = (color & 255) / 255;
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particlesGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const particleMaterial = new THREE.PointsMaterial({
size: 0.2,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending,
sizeAttenuation: true
});
const particleSystem = new THREE.Points(particlesGeometry, particleMaterial);
particleSystem.userData = {
center: position.clone(),
time: Math.random() * 100,
speed: 0.01 + Math.random() * 0.02
};
scene.add(particleSystem);
particleSystems.push(particleSystem);
}
function createEnergyConnection(fromType, toType, color) {
const fromNode = scene.children.find(obj => obj.userData && obj.userData.type === fromType);
const toNode = scene.children.find(obj => obj.userData && obj.userData.type === toType);
if (!fromNode || !toNode) return;
// Create curved line
const curve = new THREE.QuadraticBezierCurve3(
fromNode.position,
new THREE.Vector3(
(fromNode.position.x + toNode.position.x) / 2 + (Math.random() - 0.5) * 3,
(fromNode.position.y + toNode.position.y) / 2 + 3 + Math.random() * 3,
(fromNode.position.z + toNode.position.z) / 2 + (Math.random() - 0.5) * 3
),
toNode.position
);
const points = curve.getPoints(50);
const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
// Create line material with gradient
const lineMaterial = new THREE.LineBasicMaterial({
color: color,
transparent: true,
opacity: 0.5,
linewidth: 3
});
const line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line);
// Create particles along the line
const particleCount = 30;
const particlesGeometry = new THREE.BufferGeometry();
const particlesMaterial = new THREE.PointsMaterial({
color: color,
size: 0.3,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
const particles = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const t = i / (particleCount - 1);
const point = curve.getPoint(t);
particles[i * 3] = point.x;
particles[i * 3 + 1] = point.y;
particles[i * 3 + 2] = point.z;
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(particles, 3));
const particleSystem = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particleSystem);
// Store for animation
energyLines.push({
line: line,
curve: curve,
from: fromNode.position,
to: toNode.position
});
energyParticles.push({
system: particleSystem,
curve: curve,
speed: 0.01 + Math.random() * 0.02,
offset: Math.random() * 100
});
// Create energy pulses along the connection
createConnectionPulses(curve, color);
}
// Create pulsing energy spheres along connections
function createConnectionPulses(curve, color) {
const pulseCount = 5;
for (let i = 0; i < pulseCount; i++) {
const geometry = new THREE.SphereGeometry(0.5, 16, 16);
const material = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
const pulse = new THREE.Mesh(geometry, material);
// Position along curve
const t = i / pulseCount;
const point = curve.getPoint(t);
pulse.position.copy(point);
scene.add(pulse);
// Animation
gsap.to(pulse.scale, {
x: 1.5,
y: 1.5,
z: 1.5,
duration: 2 + Math.random(),
repeat: -1,
yoyo: true,
ease: "sine.inOut",
delay: i * 0.3
});
gsap.to(pulse.position, {
x: curve.getPoint((t + 0.1) % 1).x,
y: curve.getPoint((t + 0.1) % 1).y,
z: curve.getPoint((t + 0.1) % 1).z,
duration: 5,
repeat: -1,
ease: "none",
delay: i * 0.3
});
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
bloomPass.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
// Update controls
controls.update();
// Animate energy particles along connections
energyParticles.forEach((particle) => {
const positions = particle.system.geometry.attributes.position.array;
const time = Date.now() * particle.speed + particle.offset;
for (let i = 0; i < positions.length / 3; i++) {
const t = (i / (positions.length / 3 - 1) + time * 0.001) % 1;
const point = particle.curve.getPoint(t);
positions[i * 3] = point.x;
positions[i * 3 + 1] = point.y;
positions[i * 3 + 2] = point.z;
}
particle.system.geometry.attributes.position.needsUpdate = true;
});
// Animate decorative connections
energyLines.forEach((connection) => {
if (connection.curve && connection.line) {
const points = connection.curve.getPoints(50);
connection.line.geometry.setFromPoints(points);
connection.line.geometry.attributes.position.needsUpdate = true;
} else if (connection.from && connection.to && connection.line) {
// Update line between moving objects
connection.line.geometry.setFromPoints([connection.from, connection.to]);
connection.line.geometry.attributes.position.needsUpdate = true;
}
});
// Animate floating energy orbs
floatingEnergyOrbs.forEach((orb) => {
// Move orb with slight randomness
orb.position.x += orb.userData.velocity.x;
orb.position.y += orb.userData.velocity.y;
orb.position.z += orb.userData.velocity.z;
// Add some randomness to movement
orb.userData.velocity.x += (Math.random() - 0.5) * 0.001;
orb.userData.velocity.y += (Math.random() - 0.5) * 0.001;
orb.userData.velocity.z += (Math.random() - 0.5) * 0.001;
// Keep orbs within bounds
const distance = orb.position.distanceTo(orb.userData.origin);
if (distance > 20) {
orb.userData.velocity.x = -orb.userData.velocity.x * 0.5;
orb.userData.velocity.y = -orb.userData.velocity.y * 0.5;
orb.userData.velocity.z = -orb.userData.velocity.z * 0.5;
}
});
// Animate node particles
particleSystems.forEach((system) => {
if (system.userData && system.userData.center) {
const positions = system.geometry.attributes.position.array;
const center = system.userData.center;
const time = system.userData.time += system.userData.speed;
for (let i = 0; i < positions.length / 3; i++) {
const radius = 2 + Math.sin(time + i * 0.1) * 0.5;
const angle = time * 0.1 + i * 0.1;
positions[i * 3] = center.x + radius * Math.cos(angle);
positions[i * 3 + 1] = center.y + radius * Math.sin(angle * 0.7);
positions[i * 3 + 2] = center.z + radius * Math.sin(angle);
}
system.geometry.attributes.position.needsUpdate = true;
}
});
// Animate energy pulse effects
const now = Date.now();
energyPulseEffects.forEach((effect) => {
const elapsed = (now * 0.001 - effect.delay) % 5;
if (elapsed >= 0) {
const progress = elapsed / 5;
effect.mesh.scale.setScalar(progress * effect.maxSize);
effect.mesh.material.opacity = 1 - progress;
}
});
// Animate floating text
floatingTexts.forEach((text) => {
text.sprite.position.y = text.originalPosition.y + Math.sin(now * 0.001 + text.offset) * 0.5;
});
// Animate floating icons
floatingIcons.forEach((icon) => {
const time = now * 0.001 + icon.offset;
icon.sprite.position.x = icon.originalPosition.x + Math.sin(time * icon.speed) * 2;
icon.sprite.position.z = icon.originalPosition.z + Math.cos(time * icon.speed) * 2;
icon.sprite.position.y = icon.originalPosition.y + Math.sin(time * icon.speed * 0.7) * 1;
// Make icons face camera
icon.sprite.lookAt(camera.position);
});
// Rotate energy nodes
energyNodes.forEach((node) => {
node.rotation.y += 0.005;
});
// Render with post-processing
composer.render();
}
// Update timestamp
function updateTimestamp() {
const now = new Date();
const timeString = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
const dateString = now.toLocaleDateString([], {month: 'short', day: 'numeric'});
document.getElementById('update-time').textContent = `${timeString}`;
}
// Update energy grid visualization
function updateEnergyGrid() {
// Get current flow values (simulated)
const solarProduction = 5.2;
const homeConsumption = 3.3;
const batteryLevel = 78;
const gridExport = 1.9;
const gridImport = 0;
// Update numeric values with slight variations
const solarVariation = (Math.random() * 0.5 - 0.25);
const newSolarValue = Math.max(0, solarProduction + solarVariation).toFixed(1);
document.getElementById('solar-value').textContent = `${newSolarValue} kW`;
const homeVariation = (Math.random() * 0.3 - 0.15);
const newHomeValue = Math.max(0, homeConsumption + homeVariation).toFixed(1);
// Update battery percentage with small random change
let batteryChange = (Math.random() * 3 - 1.5);
if (solarProduction > homeConsumption && batteryLevel < 95) {
batteryChange = Math.abs(batteryChange); // More likely to charge
} else if (solarProduction < homeConsumption && batteryLevel > 5) {
batteryChange = -Math.abs(batteryChange); // More likely to discharge
}
const newBatteryLevel = Math.max(0, Math.min(100, batteryLevel + batteryChange));
document.getElementById('battery-value').textContent = `${Math.round(newBatteryLevel)}%`;
// Update grid value based on other changes
const newGridValue = (newSolarValue - newHomeValue).toFixed(1);
if (newGridValue > 0) {
document.getElementById('grid-value').textContent = `${Math.abs(newGridValue)} kW`;
} else {
document.getElementById('grid-value').textContent = `${Math.abs(newGridValue)} kW`;
}
// Update flow mode text
let flowMode = '';
if (newGridValue > 0) {
flowMode = `Solar Exporting (${Math.abs(newGridValue)} kW to grid)`;
document.getElementById('flow-mode').className = 'text-xl font-bold text-green-400';
} else if (newGridValue < 0) {
flowMode = `Grid Importing (${Math.abs(newGridValue)} kW from grid)`;
document.getElementById('flow-mode').className = 'text-xl font-bold text-yellow-400';
} else if (newSolarValue >= newHomeValue) {
flowMode = 'Solar Powering Home';
document.getElementById('flow-mode').className = 'text-xl font-bold text-blue-400';
} else {
flowMode = 'Mixed Power Sources';
document.getElementById('flow-mode').className = 'text-xl font-bold text-purple-400';
}
document.getElementById('flow-mode').textContent = flowMode;
// Visual feedback for UI elements
if (newGridValue > 0) {
gsap.to(document.getElementById('grid-cell'), {
boxShadow: '0 0 20px rgba(16, 185, 129, 0.7)',
duration: 0.5,
yoyo: true,
repeat: 1
});
} else {
gsap.to(document.getElementById('grid-cell'), {
boxShadow: '0 0 20px rgba(245, 158, 11, 0.7)',
duration: 0.5,
yoyo: true,
repeat: 1
});
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
initThreeJS();
updateTimestamp();
// Initial energy grid setup
updateEnergyGrid();
// Update data every 2 seconds
setInterval(() => {
updateTimestamp();
updateEnergyGrid();
}, 2000);
// Add click handlers for grid nodes
document.querySelectorAll('.grid-node').forEach(node => {
node.addEventListener('click', () => {
const type = node.dataset.type;
// Focus camera on the corresponding 3D node
const targetNode = scene.children.find(obj => obj.userData && obj.userData.type === type);
if (targetNode) {
gsap.to(camera.position, {
x: targetNode.position.x * 1.5,
y: targetNode.position.y + 5,
z: targetNode.position.z * 1.5,
duration: 1,
ease: "power2.inOut"
});
// Create pulse effect in the UI
const pulse = document.createElement('div');
pulse.className = 'energy-pulse';
pulse.style.left = `${node.offsetLeft + node.offsetWidth / 2}px`;
pulse.style.top = `${node.offsetTop + node.offsetHeight / 2}px`;
document.getElementById('particle-trail').appendChild(pulse);
// Remove after animation
setTimeout(() => {
pulse.remove();
}, 3000);
}
});
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/3d-effects" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>