Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>WiFi Vision Radar</title> | |
| <style> | |
| body { | |
| background: #000; | |
| margin: 0; | |
| padding: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| font-family: monospace; | |
| color: #0f0; | |
| overflow: hidden; | |
| } | |
| #container { | |
| display: flex; | |
| gap: 20px; | |
| } | |
| .vision-container { | |
| position: relative; | |
| width: 640px; | |
| height: 480px; | |
| border: 2px solid #0f0; | |
| } | |
| #wifiVision { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| #heatmap { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| opacity: 0.7; | |
| } | |
| .controls { | |
| width: 300px; | |
| padding: 15px; | |
| border: 1px solid #0f0; | |
| } | |
| .stats { | |
| margin-top: 10px; | |
| padding: 10px; | |
| border: 1px solid #0f0; | |
| } | |
| button { | |
| background: #000; | |
| color: #0f0; | |
| border: 1px solid #0f0; | |
| padding: 10px; | |
| width: 100%; | |
| margin: 5px 0; | |
| cursor: pointer; | |
| } | |
| button:hover { | |
| background: #0f0; | |
| color: #000; | |
| } | |
| .reading { | |
| margin: 5px 0; | |
| padding: 5px; | |
| border-bottom: 1px solid #0f0; | |
| } | |
| #signalGraph { | |
| width: 300px; | |
| height: 100px; | |
| border: 1px solid #0f0; | |
| margin-top: 10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>WiFi Vision System</h1> | |
| <div id="container"> | |
| <div class="vision-container"> | |
| <canvas id="wifiVision" width="640" height="480"></canvas> | |
| <canvas id="heatmap" width="640" height="480"></canvas> | |
| </div> | |
| <div class="controls"> | |
| <button id="startBtn">Start Scanning</button> | |
| <button id="stopBtn">Stop Scanning</button> | |
| <div class="stats"> | |
| <div class="reading">Status: <span id="status">Inactive</span></div> | |
| <div class="reading">Access Points: <span id="apCount">0</span></div> | |
| <div class="reading">Signal Strength: <span id="signalStrength">0 dBm</span></div> | |
| <div class="reading">Frame Rate: <span id="fps">0 FPS</span></div> | |
| <div class="reading">Resolution: <span>32x24</span></div> | |
| </div> | |
| <canvas id="signalGraph"></canvas> | |
| </div> | |
| </div> | |
| <script> | |
| const visionCanvas = document.getElementById('wifiVision'); | |
| const heatmapCanvas = document.getElementById('heatmap'); | |
| const signalGraph = document.getElementById('signalGraph'); | |
| const visionCtx = visionCanvas.getContext('2d'); | |
| const heatmapCtx = heatmapCanvas.getContext('2d'); | |
| const graphCtx = signalGraph.getContext('2d'); | |
| const GRID_WIDTH = 32; | |
| const GRID_HEIGHT = 24; | |
| const CELL_WIDTH = visionCanvas.width / GRID_WIDTH; | |
| const CELL_HEIGHT = visionCanvas.height / GRID_HEIGHT; | |
| let isScanning = false; | |
| let wifiGrid = Array(GRID_HEIGHT).fill().map(() => Array(GRID_WIDTH).fill(0)); | |
| let signalHistory = []; | |
| let lastFrame = performance.now(); | |
| let frameCount = 0; | |
| class WifiPoint { | |
| constructor() { | |
| this.x = Math.random() * GRID_WIDTH; | |
| this.y = Math.random() * GRID_HEIGHT; | |
| this.strength = -Math.random() * 50 - 30; // -30 to -80 dBm | |
| this.velocity = { | |
| x: (Math.random() - 0.5) * 0.1, | |
| y: (Math.random() - 0.5) * 0.1 | |
| }; | |
| } | |
| update() { | |
| this.x += this.velocity.x; | |
| this.y += this.velocity.y; | |
| if (this.x < 0 || this.x >= GRID_WIDTH) this.velocity.x *= -1; | |
| if (this.y < 0 || this.y >= GRID_HEIGHT) this.velocity.y *= -1; | |
| this.strength += (Math.random() - 0.5) * 2; | |
| this.strength = Math.max(-90, Math.min(-30, this.strength)); | |
| } | |
| } | |
| let wifiPoints = []; | |
| function generateSignalMap() { | |
| wifiGrid = Array(GRID_HEIGHT).fill().map(() => Array(GRID_WIDTH).fill(-100)); | |
| wifiPoints.forEach(point => { | |
| point.update(); | |
| for (let y = 0; y < GRID_HEIGHT; y++) { | |
| for (let x = 0; x < GRID_WIDTH; x++) { | |
| const distance = Math.sqrt( | |
| Math.pow(x - point.x, 2) + | |
| Math.pow(y - point.y, 2) | |
| ); | |
| const signal = point.strength - (20 * Math.log10(distance + 1)); | |
| wifiGrid[y][x] = Math.max(wifiGrid[y][x], signal); | |
| } | |
| } | |
| }); | |
| } | |
| function drawVisionView() { | |
| visionCtx.clearRect(0, 0, visionCanvas.width, visionCanvas.height); | |
| for (let y = 0; y < GRID_HEIGHT; y++) { | |
| for (let x = 0; x < GRID_WIDTH; x++) { | |
| const signal = wifiGrid[y][x]; | |
| const intensity = Math.min(255, Math.max(0, (signal + 100) * 3)); | |
| visionCtx.fillStyle = `rgb(0, ${intensity}, 0)`; | |
| visionCtx.fillRect( | |
| x * CELL_WIDTH, | |
| y * CELL_HEIGHT, | |
| CELL_WIDTH, | |
| CELL_HEIGHT | |
| ); | |
| } | |
| } | |
| } | |
| function drawHeatmap() { | |
| heatmapCtx.clearRect(0, 0, heatmapCanvas.width, heatmapCanvas.height); | |
| for (let y = 0; y < GRID_HEIGHT; y++) { | |
| for (let x = 0; x < GRID_WIDTH; x++) { | |
| const signal = wifiGrid[y][x]; | |
| const value = (signal + 100) / 70; | |
| const hue = Math.max(0, Math.min(240, (1 - value) * 240)); | |
| heatmapCtx.fillStyle = `hsla(${hue}, 100%, 50%, 0.5)`; | |
| heatmapCtx.fillRect( | |
| x * CELL_WIDTH, | |
| y * CELL_HEIGHT, | |
| CELL_WIDTH, | |
| CELL_HEIGHT | |
| ); | |
| } | |
| } | |
| } | |
| function updateSignalGraph() { | |
| const avgSignal = wifiGrid.flat().reduce((a, b) => a + b, 0) / (GRID_WIDTH * GRID_HEIGHT); | |
| signalHistory.push(avgSignal); | |
| if (signalHistory.length > 100) signalHistory.shift(); | |
| graphCtx.clearRect(0, 0, signalGraph.width, signalGraph.height); | |
| graphCtx.beginPath(); | |
| graphCtx.strokeStyle = '#0f0'; | |
| signalHistory.forEach((signal, i) => { | |
| const x = (i / 100) * signalGraph.width; | |
| const y = ((signal + 100) / 70) * signalGraph.height; | |
| if (i === 0) graphCtx.moveTo(x, y); | |
| else graphCtx.lineTo(x, y); | |
| }); | |
| graphCtx.stroke(); | |
| } | |
| function updateStats() { | |
| const now = performance.now(); | |
| frameCount++; | |
| if (now - lastFrame >= 1000) { | |
| document.getElementById('fps').textContent = `${frameCount} FPS`; | |
| frameCount = 0; | |
| lastFrame = now; | |
| } | |
| const avgSignal = wifiGrid.flat().reduce((a, b) => a + b, 0) / (GRID_WIDTH * GRID_HEIGHT); | |
| document.getElementById('signalStrength').textContent = `${Math.round(avgSignal)} dBm`; | |
| document.getElementById('apCount').textContent = wifiPoints.length; | |
| } | |
| function animate() { | |
| if (!isScanning) return; | |
| generateSignalMap(); | |
| drawVisionView(); | |
| drawHeatmap(); | |
| updateSignalGraph(); | |
| updateStats(); | |
| requestAnimationFrame(animate); | |
| } | |
| function startScanning() { | |
| if (isScanning) return; | |
| isScanning = true; | |
| document.getElementById('status').textContent = 'Active'; | |
| // Generate random WiFi points | |
| wifiPoints = Array(5).fill().map(() => new WifiPoint()); | |
| animate(); | |
| } | |
| function stopScanning() { | |
| isScanning = false; | |
| document.getElementById('status').textContent = 'Inactive'; | |
| wifiPoints = []; | |
| signalHistory = []; | |
| } | |
| document.getElementById('startBtn').addEventListener('click', startScanning); | |
| document.getElementById('stopBtn').addEventListener('click', stopScanning); | |
| // Initial clear | |
| visionCtx.fillStyle = '#000'; | |
| visionCtx.fillRect(0, 0, visionCanvas.width, visionCanvas.height); | |
| graphCtx.fillStyle = '#000'; | |
| graphCtx.fillRect(0, 0, signalGraph.width, signalGraph.height); | |
| </script> | |
| </body> | |
| </html><script async data-explicit-opt-in="true" data-cookie-opt-in="true" src="https://vercel.live/_next-live/feedback/feedback.js"></script> |