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