Here is a complete, single-file HTML implementation of the **El Roble Geo-Social Reconnaissance Interface**. This project translates the serene, agricultural description of El Roble into a high-tech, **Cyberpunk/Hard Sci-Fi HUD**. It uses Three.js to render a procedural wireframe terrain representing Sucre's flat landscape, with "rivers" depicted as lower-poly valleys. The data panels decode in real-time, and the entire UI glows with neon cyan, magenta, and amber against a deep dark background. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>EL ROBLE // GEO-SOCIAL RECON // SUCRE DEPT</title> <!-- Tailwind CSS --> <script src="https://cdn.tailwindcss.com"></script> <!-- Three.js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <!-- Monospace Font --> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;700;800&display=swap" rel="stylesheet"> <style> :root { --neon-cyan: #00f3ff; --neon-magenta: #ff00ff; --neon-amber: #ffb000; --bg-dark: #050505; --grid-line: rgba(0, 243, 255, 0.1); } body { background-color: var(--bg-dark); color: var(--neon-cyan); font-family: 'JetBrains Mono', monospace; overflow: hidden; margin: 0; padding: 0; width: 100vw; height: 100vh; user-select: none; } /* Scanning Grid Background */ .grid-bg { position: absolute; inset: 0; background-image: linear-gradient(var(--grid-line) 1px, transparent 1px), linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); background-size: 40px 40px; z-index: 0; pointer-events: none; } /* Main UI Layer */ #ui-layer { position: absolute; inset: 0; z-index: 10; display: flex; flex-direction: column; pointer-events: none; /* Let clicks pass to canvas where needed */ } .interactive-layer { pointer-events: auto; } /* HUD Panels */ .hud-panel { background: rgba(5, 10, 20, 0.75); border: 1px solid rgba(0, 243, 255, 0.3); backdrop-filter: blur(6px); position: relative; box-shadow: 0 0 15px rgba(0, 243, 255, 0.05); transition:all 0.3s ease; } .hud-panel:hover { border-color: var(--neon-cyan); box-shadow: 0 0 20px rgba(0, 243, 255, 0.15); z-index: 20; } /* Corner Decorations */ .corner { position: absolute; width: 8px; height: 8px; border: 2px solid var(--neon-cyan); transition: all 0.3s ease; } .tl { top: -1px; left: -1px; border-bottom-right-radius: 4px; border-bottom: none; border-right: none; } .tr { top: -1px; right: -1px; border-bottom-left-radius: 4px; border-bottom: none; border-left: none; } .bl { bottom: -1px; left: -1px; border-top-right-radius: 4px; border-top: none; border-right: none; } .br { bottom: -1px; right: -1px; border-top-left-radius: 4px; border-top: none; border-left: none; } .hud-panel:hover .corner { width: 12px; height: 12px; border-color: var(--neon-magenta); box-shadow: 0 0 5px var(--neon-magenta); } /* Typography & Glows */ .text-cyan-glow { color: var(--neon-cyan); text-shadow: 0 0 5px rgba(0, 243, 255, 0.6); } .text-magenta-glow { color: var(--neon-magenta); text-shadow: 0 0 5px rgba(255, 0, 255, 0.6); } .text-amber-glow { color: var(--neon-amber); text-shadow: 0 0 5px rgba(255, 176, 0, 0.6); } .label { font-size: 0.7rem; opacity: 0.7; letter-spacing: 1px; text-transform: uppercase; } /* Decoding Effect */ .decoding span { display: inline-block; min-width: 8px; } /* Scanning Line Animation */ .scanner { position: absolute; top: 0; left: 0; width: 100%; height: 4px; background: var(--neon-cyan); opacity: 0.5; box-shadow: 0 0 10px var(--neon-cyan); animation: scan 4s linear infinite; pointer-events: none; display: none; /* Hidden by default */ } .scanner.active { display: block; } @keyframes scan { 0% { top: 0%; opacity: 0; } 10% { opacity: 0.8; } 90% { opacity: 0.8; } 100% { top: 100%; opacity: 0; } } /* Glitch Effect on Title */ .glitch-title { position: relative; } .glitch-title::before, .glitch-title::after { content: attr(data-text); position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: var(--bg-dark); } .glitch-title::before { left: 2px; text-shadow: -1px 0 var(--neon-magenta); clip: rect(24px, 550px, 90px, 0); animation: glitch-anim-1 2.5s infinite linear alternate-reverse; } .glitch-title::after { left: -2px; text-shadow: -1px 0 var(--neon-amber); clip: rect(85px, 550px, 140px, 0); animation: glitch-anim-2 3s infinite linear alternate-reverse; } @keyframes glitch-anim-1 { 0% { clip: rect(20px, 9999px, 15px, 0); } 20% { clip: rect(60px, 9999px, 70px, 0); } 40% { clip: rect(30px, 9999px, 80px, 0); } 60% { clip: rect(80px, 9999px, 10px, 0); } 80% { clip: rect(10px, 9999px, 50px, 0); } 100% { clip: rect(50px, 9999px, 90px, 0); } } @keyframes glitch-anim-2 { 0% { clip: rect(90px, 9999px, 10px, 0); } 20% { clip: rect(10px, 9999px, 40px, 0); } 40% { clip: rect(50px, 9999px, 20px, 0); } 60% { clip: rect(20px, 9999px, 80px, 0); } 80% { clip: rect(70px, 9999px, 30px, 0); } 100% { clip: rect(30px, 9999px, 60px, 0); } } /* Canvas Radar */ #radar-canvas { width: 100%; height: 100%; } /* Custom Scrollbar */ ::-webkit-scrollbar { width: 4px; } ::-webkit-scrollbar-track { background: #000; } ::-webkit-scrollbar-thumb { background: var(--neon-cyan); } /* Pulse Animation */ .pulse { animation: pulse-anim 2s infinite; } @keyframes pulse-anim { 0% { opacity: 0.4; } 50% { opacity: 1; } 100% { opacity: 0.4; } } </style> </head> <body> <div class="grid-bg"></div> <!-- 3D Background --> <div id="canvas-container" class="absolute inset-0 z-0 opacity-60"></div> <!-- UI Overlay --> <div id="ui-layer" class="p-4 flex flex-col justify-between"> <!-- HEADER --> <header class="flex justify-between items-center mb-2 border-b border-cyan-900 pb-2 interactive-layer"> <div class="flex flex-col"> <h1 class="text-2xl font-bold glitch-title text-cyan-glow tracking-wider" data-text="EL ROBLE SECTOR"> EL ROBLE SECTOR </h1> <div class="text-xs text-magenta-glow mt-1"> <CORREGIMIENTO_VEREDA / SUCRE_DEPT / COLOMBIA_ANDES> </div> </div> <div class="text-right flex flex-col text-xs gap-1"> <div class="label">Coordinates</div> <div id="coords" class="font-bold">LAT: 09.3047 N | LONG: 75.1143 W</div> <div class="label">System Time</div> <div id="clock" class="font-bold">00:00:00</div> </div> </header> <!-- MAIN CONTENT GRID --> <main class="flex-1 grid grid-cols-12 gap-4 mb-2"> <!-- LEFT SIDEBAR: Geographic & Economic Data --> <aside class="col-span-12 md:col-span-3 flex flex-col gap-4 interactive-layer"> <!-- Panel: Terrain Analysis --> <div class="hud-panel p-3 flex-1 flex flex-col relative corner tl corner tr"> <div class="corner bl"></div> <div class="corner br"></div> <div class="label text-cyan-glow mb-2">// TERRAIN_ANALYSIS</div> <div class="text-sm mb-2 leading-relaxed text-cyan-100"> <p class="decoding" data-value="Flat topology with riverine traces. Ideal for extensive agriculture.">Flat topology with riverine traces. Ideal for extensive agriculture.</p> </div> <div class="mt-auto space-y-2"> <div> <div class="flex justify-between text-xs"> <span class="text-amber-glow">Cultivable Land</span> <span>84%</span> </div> <div class="w-full h-1 bg-gray-800 mt-1"> <div class="h-full bg-amber-500 shadow-[0_0_10px_#ffb000]" style="width: 84%"></div> </div> </div> <div> <div class="flex justify-between text-xs"> <span class="text-magenta-glow">Hydrology</span> <span>12%</span> </div> <div class="w-full h-1 bg-gray-800 mt-1"> <div class="h-full bg-magenta-500 shadow-[0_0_10px_#ff00ff]" style="width: 12%"></div> </div> </div> </div> </div> <!-- Panel: Economic Output --> <div class="hud-panel p-3 h-1/3 relative corner tl corner tr corner bl corner br flex flex-col"> <div class="label text-cyan-glow mb-2">// AGRICULT_OUTPUT</div> <div class="grid grid-cols-3 gap-2 text-center flex-1"> <div class="flex flex-col justify-center border border-cyan-900 p-1 rounded hover:bg-cyan-900/20 transition"> <span class="text-xl font-bold text-cyan-glow pulse">RICE</span> <span class="text-xs text-gray-400">32k Ton</span> </div> <div class="flex flex-col justify-center border border-cyan-900 p-1 rounded hover:bg-cyan-900/20 transition"> <span class="text-xl font-bold text-amber-glow pulse" style="animation-delay: 0.5s">MAIZE</span> <span class="text-xs text-gray-400">18k Ton</span> </div> <div class="flex flex-col justify-center border border-cyan-900 p-1 rounded hover:bg-cyan-900/20 transition"> <span class="text-xl font-bold text-magenta-glow pulse" style="animation-delay: 1s">COTTON</span> <span class="text-xs text-gray-400">4.2k B</span> </div> </div> </div> </aside> <!-- CENTER: 3D View --> <section class="col-span-12 md:col-span-6 relative interactive-layer flex flex-col"> <!-- Top Reticle Overlay --> <div class="absolute top-4 left-1/2 -translate-x-1/2 z-20 pointer-events-none"> <div class="w-64 h-8 border border-cyan-900 bg-black/50 flex items-center justify-between px-2"> <span class="text-[10px] text-cyan-700">TERRAIN SCANNER ACTIVE</span> <span class="text-[10px] text-cyan-700">WIREFRAME RENDER</span> </div> </div> <!-- The Canvas is actually in background, but this div acts as container for our DOM overlays --> <div class="hud-panel relative w-full h-full flex items-center justify-center overflow-hidden corner tl corner tr corner bl corner br"> <!-- Scanner line moving across 3D view --> <div class="scanner active" style="width: 100%; height: 100%; background: linear-gradient(to bottom, transparent, rgba(0, 243, 255, 0.2), transparent); top: 0; animation: scanY 8s linear infinite;"></div> <!-- Center Reticle --> <div class="absolute w-16 h-16 border border-cyan-500/50 rounded-full flex items-center justify-center pointer-events-none"> <div class="w-0.5 h-0.5 bg-cyan-400 shadow-[0_0_10px_#0ff]"></div> </div> <!-- Rotating Ring --> <div class="absolute w-24 h-24 border-t border-b border-cyan-500/30 rounded-full animate-[spin_4s_linear_infinite]"></div> </div> </section> <!-- RIGHT SIDEBAR: Cultural & Social --> <aside class="col-span-12 md:col-span-3 flex flex-col gap-4 interactive-layer"> <!-- Panel: Cultural Frequencies --> <div class="hud-panel p-3 flex-1 relative corner tl corner tr corner bl corner br flex flex-col"> <div class="label text-magenta-glow mb-2">// CULT SPECTRAL</div> <div class="text-xs mb-2 text-cyan-100 flex-1"> <p class="decoding" data-value="Vallenato & Porro frequencies dominant. Oral history blending Indigenous/Afro/Spanish roots.">Vallenato & Porro frequencies dominant. Oral history blending...</p> </div> <!-- Canvas for Waveform --> <canvas id="waveform-canvas" class="w-full h-16 bg-black/50 border border-cyan-900 mb-2"></canvas> <div class="grid grid-cols-2 gap-2 text-center"> <div class="border border-cyan-900 p-1 hover:bg-cyan-900/20 transition"> <span class="text-xs block text-magenta-400">VALLENATO</span> <span class="text-xs block text-gray-400">87% Match</span> </div> <div class="border border-cyan-900 p-1 hover:bg-cyan-900/20 transition"> <span class="text-xs block text-amber-400">PORRO</span> <span class="text-xs block text-gray-400">76% Match</span> </div> </div> </div> <!-- Panel: Community Structure --> <div class="hud-panel p-3 flex-1 relative corner tl corner tr corner bl corner br flex flex-col"> <div class="label text-amber-glow mb-2">// CIVIC NODES</div> <div class="flex-1 flex flex-col gap-2 text-sm"> <div class="flex items-center gap-2 p-1 border-l-2 border-amber-500 bg-amber-900/10 hover:bg-amber-900/30 transition cursor-pointer"> <span class="w-2 h-2 bg-amber-500 rounded-full"></span> <span>CHAPEL (SAN JOSE)</span> </div> <div class="flex items-center gap-2 p-1 border-l-2 border-cyan-500 bg-cyan-900/10 hover:bg-cyan-900/30 transition cursor-pointer"> <span class="w-2 h-2 bg-cyan-500 rounded-full"></span> <span>RURAL SCHOOL</span> </div> <div class="flex items-center gap-2 p-1 border-l-2 border-magenta-500 bg-magenta-900/10 hover:bg-magenta-900/30 transition cursor-pointer"> <span class="w-2 h-2 bg-magenta-500 rounded-full"></span> <span>MAIN SQUARE</span> </div> </div> </div> </aside> </main> <!-- FOOTER --> <footer class="flex justify-between items-center text-[10px] border-t border-cyan-900 pt-1 interactive-layer"> <div class="flex gap-4"> <span class="text-cyan-700">SYS.STATUS: ONLINE</span> <span class="text-cyan-700">NET: SECURE</span> <span class="text-amber-500 blink">● LIVE FEED</span> </div> <div class="text-cyan-700" id="data-stream"> PACKET.LOSS 0.00% // BUFFER CLEAR </div> </footer> </div> <!-- JavaScript Logic --> <script> // ========================================== // 1. Utility: Decoding Text Effect // ========================================== const decodeElements = document.querySelectorAll('.decoding'); const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*'; function decodeText(element, duration = 2000) { const finalText = element.getAttribute('data-value'); let iterations = 0; const interval = setInterval(() => { element.innerText = finalText .split('') .map((letter, index) => { if (index < iterations) { return finalText[index]; } return chars[Math.floor(Math.random() * chars.length)]; }) .join(''); if (iterations >= finalText.length) { clearInterval(interval); } iterations += 1 / 2; // Speed control }, 30); } // Initialize decoding window.addEventListener('DOMContentLoaded', () => { decodeElements.forEach(el => decodeText(el)); updateClock(); setInterval(updateClock, 1000); }); // Clock function updateClock() { const now = new Date(); document.getElementById('clock').innerText = now.toISOString().split('T')[1].split('.')[0] + " UTC-5"; } // Random Data Stream Text const dataStream = document.getElementById('data-stream'); setInterval(() => { const loss = (Math.random() * 0.1).toFixed(2); dataStream.innerText = `PACKET.LOSS ${loss}% // BUFFER CLEAR // SIG STRENGTH ${Math.floor(80 + Math.random() * 20)}`; }, 2000); // ========================================== // 2. Three.js: Wireframe Terrain (The "Fields" of El Roble) // ========================================== const initThreeJS = () => { const container = document.getElementById('canvas-container'); const scene = new THREE.Scene(); // Fog to fade out distant geometry into background scene.fog = new THREE.FogExp2(0x050505, 0.002); const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 30, 40); camera.lookAt(0, 0, 0); const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); // Geometry: Plane representing the fields const planeSize = 100; const geometry = new THREE.PlaneGeometry(planeSize, planeSize, 40, 40); // Modify vertices to create "rivers" (valleys) and "fields" (hills) const positionAttribute = geometry.attributes.position; const vertex = new THREE.Vector3(); for (let i = 0; i < positionAttribute.count; i++) { vertex.fromBufferAttribute(positionAttribute, i); // Simple pseudo-height // Combine sine waves for randomness let noise = Math.sin(vertex.x * 0.2) * Math.cos(vertex.y * 0.2) * 4; noise += Math.sin(vertex.x * 0.5 + vertex.y * 0.5) * 1; // Create specific "rivers": areas with lower Y values // Debug: vertex.x/y range is approx -50 to 50 if (vertex.x > -30 && vertex.x < -10) noise -= 5; // River bed if (vertex.x > 10 && vertex.x < 30) noise -= 4; if (vertex.y > -20 && vertex.y < 20) noise -= 2; // Ensure flat areas are mostly flat but slight bump for texture positionAttribute.setZ(i, Math.max(0, noise)); // Z is up in geometry before rotation } geometry.computeVertexNormals(); // Material: Wireframe const material = new THREE.MeshBasicMaterial({ color: 0x00f3ff, wireframe: true, transparent: true, opacity: 0.6 }); const terrain = new THREE.Mesh(geometry, material); terrain.rotation.x = -Math.PI / 2; // Lay flat scene.add(terrain); // Add "Nodes" representing Community centers const nodeGeo = new THREE.SphereGeometry(0.5, 8, 8); const nodeMat = new THREE.MeshBasicMaterial({ color: 0xff00ff }); const positions = [ {x: -15, z: -10}, // Chapel area {x: -15, z: 0}, // School area {x: 0, z: 0} // Square ]; positions.forEach(pos => { const node = new THREE.Mesh(nodeGeo, nodeMat); node.position.set(pos.x, 2, pos.z); // Elevated 2 units scene.add(node); // Glow sprite/light around node const light = new THREE.PointLight(0xff00ff, 1, 5); light.position.set(pos.x, 2, pos.z); scene.add(light); }); // Lines connecting nodes (Community flows) const lineGeo = new THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(-15, 2, -10), new THREE.Vector3(-15, 2, 0), new THREE.Vector3(0, 2, 0) ]); const lineMat = new THREE.LineBasicMaterial({ color: 0x00f3ff }); const line = new THREE.Line(lineGeo, lineMat); scene.add(line); // Animation Loop let mouseX = 0; let mouseY = 0; document.addEventListener('mousemove', (e) => { mouseX = (e.clientX / window.innerWidth) * 2 - 1; mouseY = -(e.clientY / window.innerHeight) * 2 + 1; }); const animate = () => { requestAnimationFrame(animate); // Subtle terrain rotation based on mouse const targetRotX = -Math.PI / 2 + mouseY * 0.1; const targetRotZ = mouseX * 0.05; terrain.rotation.x += (targetRotX - terrain.rotation.x) * 0.05; terrain.rotation.z += (targetRotZ - terrain.rotation.z) * 0.05; // Floating animation for nodes const time = Date.now() * 0.001; scene.children.forEach(child => { if (child.geometry && child.geometry.type === 'SphereGeometry') { child.position.y = 2 + Math.sin(time * 2) * 0.3; } }); renderer.render(scene, camera); }; animate(); // Resize handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); }; initThreeJS(); // ========================================== // 3. Canvas API: Audio Waveform Viz (Vallenato/Porro) // ========================================== const initWaveform = () => { const canvas = document.getElementById('waveform-canvas'); const ctx = canvas.getContext('2d'); // Fix resolution const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * 2; canvas.height = rect.height * 2; ctx.scale(2, 2); let phase = 0; const draw = () => { const width = canvas.width / 2; const height = canvas.height / 2; ctx.clearRect(0, 0, width, height); // Draw grid lines ctx.strokeStyle = 'rgba(0, 243, 255, 0.1)'; ctx.beginPath(); ctx.moveTo(0, height / 2); ctx.lineTo(width, height / 2); ctx.stroke(); // Draw waveform ctx.beginPath(); ctx.strokeStyle = '#ff00ff'; ctx.lineWidth = 1.5; for (let x = 0; x < width; x++) { // Combine two sines for complex sound look const y = height / 2 + Math.sin(x * 0.05 + phase) * 10 + Math.sin(x * 0.15 + phase * 2) * 5; if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.stroke(); // Second line (amber) slightly offset to simulate reverb/filter ctx.beginPath(); ctx.strokeStyle = '#ffb000'; ctx.lineWidth = 1; for (let x = 0; x < width; x++) { const y = height / 2 + Math.cos(x * 0.03 + phase) * 8 + Math.sin(x * 0.1 + phase * 1.5) * 4; if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.stroke(); phase += 0.1; requestAnimationFrame(draw); }; draw(); }; initWaveform(); // ========================================== // 4. Canvas API: Radar for "Hydrology/Community" // ========================================== // We already used a canvas for waveform. For the right panel we could add another, // but the prompt mentions Canvas API for charts. Let's add a small dynamic chart // to the "Cultivable Land / Hydrology" bars using a tiny script that updates the numbers... // Actually, let's make the progress bars have a dynamic fill effect that eases in on load. </script> </body> </html> ```
Thanks to our Virtual Reality technology, we transport you to El Roble for unique observations.
This feature requires payment.
The data below describes the current air quality at El Roble. Based on the European Air Quality Index (AQI), calculated using the data below, The weather conditions are passable.
| Dust | 0 μg/m³ |
|---|---|
| Carbon Dioxide CO2 | 472 ppm |
| Nitrogen Dioxide NO2 | 6.8 μg/m³ |
| Sulphur Dioxide SO2 | 0.8 μg/m³ |
| Ammonia NH3 | 2.8 μg/m³ |
The data below describes the current weather in El Roble.
| Temperature | 5.5 °C |
|---|---|
| Rain | 0 mm |
| Showers | 0 mm |
| Snowfall | 0 cm |
| Cloud Cover Total | 0 % |
| Sea Level Pressure | 1024.7 hPa |
| Wind Speed | 2.5 km/h |