Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Perceptron Passive Radar</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: monospace; | |
| } | |
| body { | |
| background: #001; | |
| color: #0f8; | |
| min-height: 100vh; | |
| overflow: hidden; | |
| } | |
| .main-container { | |
| display: grid; | |
| grid-template-columns: 1fr 400px; | |
| gap: 10px; | |
| padding: 10px; | |
| height: 100vh; | |
| } | |
| .radar-container { | |
| position: relative; | |
| border: 2px solid #0f8; | |
| border-radius: 50%; | |
| background: radial-gradient(circle, rgba(0,255,136,0.05) 0%, rgba(0,0,0,0.9) 100%); | |
| overflow: hidden; | |
| } | |
| .sweep-line { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 50%; | |
| height: 2px; | |
| background: linear-gradient(90deg, #0f8, transparent); | |
| transform-origin: left; | |
| animation: radar-sweep 4s linear infinite; | |
| } | |
| .grid-overlay { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| background: | |
| linear-gradient(#0f81 1px, transparent 1px), | |
| linear-gradient(90deg, #0f81 1px, transparent 1px); | |
| background-size: 50px 50px; | |
| } | |
| @keyframes radar-sweep { | |
| from { transform: rotate(0deg); } | |
| to { transform: rotate(360deg); } | |
| } | |
| .control-panel { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| .panel { | |
| background: rgba(0,255,136,0.1); | |
| border: 1px solid #0f8; | |
| padding: 10px; | |
| } | |
| .perceptron-layer { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| height: 200px; | |
| margin: 10px 0; | |
| position: relative; | |
| } | |
| .layer { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| } | |
| .neuron { | |
| width: 12px; | |
| height: 12px; | |
| background: #0f8; | |
| border-radius: 50%; | |
| position: relative; | |
| opacity: 0.3; | |
| transition: all 0.2s; | |
| } | |
| .neuron.active { | |
| opacity: 1; | |
| box-shadow: 0 0 10px #0f8; | |
| } | |
| .connections { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| } | |
| .spectrum { | |
| height: 120px; | |
| display: flex; | |
| align-items: flex-end; | |
| gap: 2px; | |
| } | |
| .bar { | |
| flex: 1; | |
| background: #0f8; | |
| transition: height 0.1s; | |
| } | |
| .signal { | |
| position: absolute; | |
| width: 8px; | |
| height: 8px; | |
| background: #0f8; | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| } | |
| .signal-echo { | |
| position: absolute; | |
| border: 1px solid #0f8; | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| animation: echo 2s ease-out forwards; | |
| } | |
| @keyframes echo { | |
| 0% { | |
| width: 8px; | |
| height: 8px; | |
| opacity: 1; | |
| } | |
| 100% { | |
| width: 60px; | |
| height: 60px; | |
| opacity: 0; | |
| } | |
| } | |
| button { | |
| background: transparent; | |
| border: 1px solid #0f8; | |
| color: #0f8; | |
| padding: 8px; | |
| cursor: pointer; | |
| transition: 0.3s; | |
| } | |
| button:hover { | |
| background: #0f8; | |
| color: #001; | |
| } | |
| .stats { | |
| font-size: 12px; | |
| line-height: 1.5; | |
| } | |
| canvas { | |
| margin-top: 10px; | |
| border: 1px solid #0f8; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="main-container"> | |
| <div class="radar-container"> | |
| <div class="grid-overlay"></div> | |
| <div class="sweep-line"></div> | |
| <div id="signals"></div> | |
| </div> | |
| <div class="control-panel"> | |
| <div class="panel"> | |
| <h3>Perceptron Network</h3> | |
| <div class="perceptron-layer"> | |
| <canvas id="networkCanvas"></canvas> | |
| </div> | |
| <div class="stats" id="networkStats"> | |
| Accuracy: 0%<br> | |
| Detections: 0<br> | |
| Signal Strength: 0 | |
| </div> | |
| </div> | |
| <div class="panel"> | |
| <h3>Frequency Analysis</h3> | |
| <div class="spectrum" id="spectrum"></div> | |
| </div> | |
| <div class="panel"> | |
| <button onclick="startRadar()">Start Detection</button> | |
| <button onclick="stopRadar()">Stop</button> | |
| <button onclick="trainPerceptrons()">Train Network</button> | |
| <button onclick="toggleLearning()">Toggle Learning</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| class Perceptron { | |
| constructor(inputs) { | |
| this.weights = new Array(inputs).fill(0).map(() => Math.random() * 2 - 1); | |
| this.bias = Math.random() * 2 - 1; | |
| this.learningRate = 0.1; | |
| } | |
| activate(sum) { | |
| return sum > 0 ? 1 : 0; | |
| } | |
| predict(inputs) { | |
| const sum = inputs.reduce((sum, input, i) => sum + input * this.weights[i], 0) + this.bias; | |
| return this.activate(sum); | |
| } | |
| train(inputs, target) { | |
| const prediction = this.predict(inputs); | |
| const error = target - prediction; | |
| for(let i = 0; i < this.weights.length; i++) { | |
| this.weights[i] += error * inputs[i] * this.learningRate; | |
| } | |
| this.bias += error * this.learningRate; | |
| return Math.abs(error); | |
| } | |
| } | |
| class PerceptronNetwork { | |
| constructor(inputSize, hiddenSize, outputSize) { | |
| this.inputLayer = new Array(hiddenSize) | |
| .fill(0) | |
| .map(() => new Perceptron(inputSize)); | |
| this.outputLayer = new Array(outputSize) | |
| .fill(0) | |
| .map(() => new Perceptron(hiddenSize)); | |
| } | |
| predict(inputs) { | |
| const hiddenOutputs = this.inputLayer.map(p => p.predict(inputs)); | |
| return this.outputLayer.map(p => p.predict(hiddenOutputs)); | |
| } | |
| train(inputs, targets) { | |
| const hiddenOutputs = this.inputLayer.map(p => p.predict(inputs)); | |
| const finalOutputs = this.outputLayer.map(p => p.predict(hiddenOutputs)); | |
| let error = 0; | |
| this.outputLayer.forEach((p, i) => { | |
| error += p.train(hiddenOutputs, targets[i]); | |
| }); | |
| this.inputLayer.forEach(p => { | |
| error += p.train(inputs, 1); | |
| }); | |
| return error / (this.inputLayer.length + this.outputLayer.length); | |
| } | |
| } | |
| // Initialize system | |
| const INPUT_SIZE = 32; | |
| const HIDDEN_SIZE = 16; | |
| const OUTPUT_SIZE = 4; | |
| let isRunning = false; | |
| let isLearning = false; | |
| let network = new PerceptronNetwork(INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE); | |
| let audioContext, analyser, dataArray; | |
| // Setup UI | |
| const spectrum = document.getElementById('spectrum'); | |
| for(let i = 0; i < INPUT_SIZE; i++) { | |
| const bar = document.createElement('div'); | |
| bar.className = 'bar'; | |
| spectrum.appendChild(bar); | |
| } | |
| // Setup canvas | |
| const canvas = document.getElementById('networkCanvas'); | |
| canvas.width = 350; | |
| canvas.height = 180; | |
| const ctx = canvas.getContext('2d'); | |
| function drawNetwork(inputs, hiddenOutputs, finalOutputs) { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| ctx.strokeStyle = '#0f8'; | |
| ctx.lineWidth = 0.5; | |
| const drawLayer = (nodes, x, active) => { | |
| const spacing = canvas.height / (nodes.length + 1); | |
| return nodes.map((value, i) => { | |
| const y = spacing * (i + 1); | |
| ctx.beginPath(); | |
| ctx.arc(x, y, 4, 0, Math.PI * 2); | |
| ctx.fillStyle = `rgba(0,255,136,${active ? value : 0.3})`; | |
| ctx.fill(); | |
| return {x, y}; | |
| }); | |
| }; | |
| const inputNodes = drawLayer(inputs, 30, true); | |
| const hiddenNodes = drawLayer(hiddenOutputs, canvas.width/2, true); | |
| const outputNodes = drawLayer(finalOutputs, canvas.width - 30, true); | |
| // Draw connections | |
| ctx.beginPath(); | |
| inputNodes.forEach(input => { | |
| hiddenNodes.forEach(hidden => { | |
| ctx.moveTo(input.x, input.y); | |
| ctx.lineTo(hidden.x, hidden.y); | |
| }); | |
| }); | |
| hiddenNodes.forEach(hidden => { | |
| outputNodes.forEach(output => { | |
| ctx.moveTo(hidden.x, hidden.y); | |
| ctx.lineTo(output.x, output.y); | |
| }); | |
| }); | |
| ctx.stroke(); | |
| } | |
| async function startRadar() { | |
| try { | |
| if(!audioContext) { | |
| audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| const source = audioContext.createMediaStreamSource(stream); | |
| analyser = audioContext.createAnalyser(); | |
| analyser.fftSize = 64; | |
| source.connect(analyser); | |
| dataArray = new Uint8Array(analyser.frequencyBinCount); | |
| } | |
| isRunning = true; | |
| updateRadar(); | |
| } catch(err) { | |
| console.error('Error accessing microphone:', err); | |
| } | |
| } | |
| function stopRadar() { | |
| isRunning = false; | |
| } | |
| function updateRadar() { | |
| if(!isRunning) return; | |
| analyser.getByteFrequencyData(dataArray); | |
| // Update spectrum | |
| const bars = spectrum.children; | |
| for(let i = 0; i < bars.length; i++) { | |
| const height = (dataArray[i] / 255) * 120; | |
| bars[i].style.height = height + 'px'; | |
| } | |
| // Normalize input data | |
| const inputs = Array.from(dataArray).map(x => x / 255); | |
| // Get network predictions | |
| const hiddenOutputs = network.inputLayer.map(p => p.predict(inputs)); | |
| const outputs = network.outputLayer.map(p => p.predict(hiddenOutputs)); | |
| // Draw network state | |
| drawNetwork(inputs, hiddenOutputs, outputs); | |
| // Create signals for strong predictions | |
| outputs.forEach((output, i) => { | |
| if(output > 0.7) { | |
| createSignal(i); | |
| } | |
| }); | |
| // Update stats | |
| const avgOutput = outputs.reduce((a,b) => a+b) / outputs.length; | |
| document.getElementById('networkStats').innerHTML = | |
| `Accuracy: ${Math.round(avgOutput * 100)}%<br>` + | |
| `Detections: ${outputs.filter(x => x > 0.7).length}<br>` + | |
| `Signal Strength: ${Math.round(inputs.reduce((a,b) => a+b) / inputs.length * 100)}`; | |
| if(isLearning) { | |
| const target = Array(OUTPUT_SIZE).fill(0); | |
| target[Math.floor(Math.random() * OUTPUT_SIZE)] = 1; | |
| network.train(inputs, target); | |
| } | |
| requestAnimationFrame(updateRadar); | |
| } | |
| function createSignal(type) { | |
| const radar = document.querySelector('.radar-container'); | |
| const angle = Math.random() * Math.PI * 2; | |
| const distance = Math.random() * (radar.offsetWidth / 3); | |
| const x = Math.cos(angle) * distance + radar.offsetWidth / 2; | |
| const y = Math.sin(angle) * distance + radar.offsetHeight / 2; | |
| const signal = document.createElement('div'); | |
| signal.className = 'signal'; | |
| signal.style.left = x + 'px'; | |
| signal.style.top = y + 'px'; | |
| radar.appendChild(signal); | |
| const echo = document.createElement('div'); | |
| echo.className = 'signal-echo'; | |
| echo.style.left = x + 'px'; | |
| echo.style.top = y + 'px'; | |
| radar.appendChild(echo); | |
| setTimeout(() => { | |
| signal.remove(); | |
| echo.remove(); | |
| }, 2000); | |
| } | |
| function trainPerceptrons() { | |
| let error = 0; | |
| for(let i = 0; i < 100; i++) { | |
| const inputs = Array(INPUT_SIZE).fill(0).map(() => Math.random()); | |
| const targets = Array(OUTPUT_SIZE).fill(0); | |
| targets[Math.floor(Math.random() * OUTPUT_SIZE)] = 1; | |
| error += network.train(inputs, targets); | |
| } | |
| document.getElementById('networkStats').innerHTML = | |
| `Training complete<br>Error: ${Math.round(error * 100) / 100}<br>Network ready`; | |
| } | |
| function toggleLearning() { | |
| isLearning = !isLearning; | |
| } | |
| </script> | |
| </body> | |
| </html> |