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> |