Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>3D Neural Network Visualization</title> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
font-family: Arial, sans-serif; | |
color: white; | |
background-color: #111; | |
} | |
#canvas-container { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
} | |
#info-panel { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
background-color: rgba(0, 0, 0, 0.7); | |
padding: 15px; | |
border-radius: 5px; | |
max-width: 300px; | |
z-index: 100; | |
} | |
#controls { | |
position: absolute; | |
bottom: 10px; | |
left: 10px; | |
background-color: rgba(0, 0, 0, 0.7); | |
padding: 15px; | |
border-radius: 5px; | |
z-index: 100; | |
} | |
button, input { | |
margin: 5px; | |
padding: 8px; | |
background-color: #444; | |
color: white; | |
border: none; | |
border-radius: 3px; | |
cursor: pointer; | |
} | |
button:hover { | |
background-color: #666; | |
} | |
h1 { | |
margin-top: 0; | |
font-size: 1.2em; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="canvas-container"></div> | |
<div id="info-panel"> | |
<h1>3D Neural Network</h1> | |
<p>This visualization represents a simple feedforward neural network with multiple layers.</p> | |
<p>The connections between neurons light up when data flows through the network.</p> | |
<p><strong>Controls:</strong> Drag to rotate, scroll to zoom.</p> | |
</div> | |
<div id="controls"> | |
<button id="toggle-animation">Pause Animation</button> | |
<button id="reset-view">Reset View</button> | |
<label for="layer-count">Layers: </label> | |
<input type="range" id="layer-count" min="2" max="7" value="4"> | |
<span id="layer-count-value">4</span> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
<script> | |
// Main configuration for the network | |
const config = { | |
neuronSize: 0.15, | |
layerDistance: 1.5, | |
neuronsPerLayer: [8, 12, 10, 6], // Default structure | |
neuronColors: { | |
input: 0x4286f4, | |
hidden: 0x42b0f4, | |
output: 0xf442a7 | |
}, | |
animationSpeed: 0.01, | |
connectionOpacity: 0.3, | |
activeConnectionOpacity: 0.8, | |
activeConnectionColor: 0xffffff, | |
pulseDuration: 100, // Number of frames for a complete pulse | |
}; | |
// Global variables | |
let scene, camera, renderer, neurons = [], connections = [], animationActive = true; | |
let networkGroup, frame = 0; | |
// Initialize the scene | |
function init() { | |
// Create scene | |
scene = new THREE.Scene(); | |
scene.background = new THREE.Color(0x111111); | |
// Create camera | |
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
camera.position.z = 5; | |
// Create renderer | |
renderer = new THREE.WebGLRenderer({ antialias: true }); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
document.getElementById('canvas-container').appendChild(renderer.domElement); | |
// Add lights | |
const ambientLight = new THREE.AmbientLight(0x404040); | |
scene.add(ambientLight); | |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); | |
directionalLight.position.set(1, 1, 1); | |
scene.add(directionalLight); | |
// Create a group for the entire network | |
networkGroup = new THREE.Group(); | |
scene.add(networkGroup); | |
// Create the neural network | |
createNeuralNetwork(); | |
// Set up controls for orbiting | |
setupOrbitControls(); | |
// Event listeners | |
window.addEventListener('resize', onWindowResize); | |
document.getElementById('toggle-animation').addEventListener('click', toggleAnimation); | |
document.getElementById('reset-view').addEventListener('click', resetView); | |
const layerSlider = document.getElementById('layer-count'); | |
const layerCountDisplay = document.getElementById('layer-count-value'); | |
layerSlider.addEventListener('input', function() { | |
layerCountDisplay.textContent = this.value; | |
updateNetworkStructure(parseInt(this.value)); | |
}); | |
// Start animation loop | |
animate(); | |
} | |
// Create a neural network with specified layers | |
function createNeuralNetwork() { | |
neurons = []; | |
connections = []; | |
// Remove existing network if any | |
while(networkGroup.children.length > 0) { | |
networkGroup.remove(networkGroup.children[0]); | |
} | |
// Material for neurons | |
const inputMaterial = new THREE.MeshPhongMaterial({ color: config.neuronColors.input }); | |
const hiddenMaterial = new THREE.MeshPhongMaterial({ color: config.neuronColors.hidden }); | |
const outputMaterial = new THREE.MeshPhongMaterial({ color: config.neuronColors.output }); | |
// Geometry for neurons | |
const neuronGeometry = new THREE.SphereGeometry(config.neuronSize, 16, 16); | |
// Create neurons for each layer | |
for (let layerIndex = 0; layerIndex < config.neuronsPerLayer.length; layerIndex++) { | |
const layerNeurons = []; | |
const neuronsInLayer = config.neuronsPerLayer[layerIndex]; | |
let material; | |
if (layerIndex === 0) material = inputMaterial; | |
else if (layerIndex === config.neuronsPerLayer.length - 1) material = outputMaterial; | |
else material = hiddenMaterial; | |
// Calculate vertical spacing | |
const layerHeight = (neuronsInLayer - 1) * 0.6; | |
for (let neuronIndex = 0; neuronIndex < neuronsInLayer; neuronIndex++) { | |
const neuron = new THREE.Mesh(neuronGeometry, material); | |
// Position each neuron | |
neuron.position.x = layerIndex * config.layerDistance - (config.neuronsPerLayer.length - 1) * config.layerDistance / 2; | |
neuron.position.y = neuronIndex * 0.6 - layerHeight / 2; | |
neuron.layerIndex = layerIndex; | |
neuron.neuronIndex = neuronIndex; | |
networkGroup.add(neuron); | |
layerNeurons.push(neuron); | |
} | |
neurons.push(layerNeurons); | |
} | |
// Create connections between neurons | |
for (let layerIndex = 0; layerIndex < neurons.length - 1; layerIndex++) { | |
const currentLayer = neurons[layerIndex]; | |
const nextLayer = neurons[layerIndex + 1]; | |
for (let i = 0; i < currentLayer.length; i++) { | |
for (let j = 0; j < nextLayer.length; j++) { | |
const startNeuron = currentLayer[i]; | |
const endNeuron = nextLayer[j]; | |
const connectionGeometry = new THREE.BufferGeometry(); | |
const lineMaterial = new THREE.LineBasicMaterial({ | |
color: 0xaaaaaa, | |
transparent: true, | |
opacity: config.connectionOpacity | |
}); | |
// Create a line between neurons | |
const points = [ | |
startNeuron.position, | |
endNeuron.position | |
]; | |
connectionGeometry.setFromPoints(points); | |
const connection = new THREE.Line(connectionGeometry, lineMaterial); | |
// Store connections for animation | |
connection.startNeuron = startNeuron; | |
connection.endNeuron = endNeuron; | |
connection.originalColor = 0xaaaaaa; | |
connection.activationTime = -1; | |
networkGroup.add(connection); | |
connections.push(connection); | |
} | |
} | |
} | |
} | |
// Update the network when layer count changes | |
function updateNetworkStructure(layerCount) { | |
// Generate a new structure with the specified number of layers | |
config.neuronsPerLayer = []; | |
// Input layer has 8 neurons | |
config.neuronsPerLayer.push(8); | |
// Generate hidden layers with varying sizes | |
for (let i = 0; i < layerCount - 2; i++) { | |
const layerSize = Math.round(6 + Math.sin(i * Math.PI / (layerCount - 2)) * 6); | |
config.neuronsPerLayer.push(layerSize); | |
} | |
// Output layer has 6 neurons | |
config.neuronsPerLayer.push(6); | |
// Recreate the network | |
createNeuralNetwork(); | |
} | |
// Animation loop | |
function animate() { | |
requestAnimationFrame(animate); | |
if (animationActive) { | |
// Increment frame counter | |
frame += 1; | |
// Trigger activations | |
if (frame % 30 === 0) { | |
triggerNetworkActivation(); | |
} | |
// Update all connections | |
updateConnections(); | |
} | |
renderer.render(scene, camera); | |
} | |
// Trigger a new activation wave through the network | |
function triggerNetworkActivation() { | |
// Activate a random neuron in the input layer | |
const inputLayer = neurons[0]; | |
const randomNeuronIndex = Math.floor(Math.random() * inputLayer.length); | |
// Get all connections from this neuron | |
for (let i = 0; i < connections.length; i++) { | |
const connection = connections[i]; | |
if (connection.startNeuron === inputLayer[randomNeuronIndex]) { | |
connection.activationTime = frame; | |
connection.material.opacity = config.activeConnectionOpacity; | |
connection.material.color.set(config.activeConnectionColor); | |
} | |
} | |
} | |
// Update connection animations | |
function updateConnections() { | |
const activationLayerDuration = config.pulseDuration / (config.neuronsPerLayer.length - 1); | |
for (let i = 0; i < connections.length; i++) { | |
const connection = connections[i]; | |
// If connection is active | |
if (connection.activationTime > 0) { | |
const elapsedFrames = frame - connection.activationTime; | |
// If this connection has been active long enough, propagate to next layer | |
if (elapsedFrames >= activationLayerDuration && | |
connection.endNeuron.layerIndex < config.neuronsPerLayer.length - 1) { | |
// Find connections from the end neuron | |
for (let j = 0; j < connections.length; j++) { | |
const nextConnection = connections[j]; | |
if (nextConnection.startNeuron === connection.endNeuron && | |
nextConnection.activationTime < connection.activationTime) { | |
nextConnection.activationTime = frame; | |
nextConnection.material.opacity = config.activeConnectionOpacity; | |
nextConnection.material.color.set(config.activeConnectionColor); | |
} | |
} | |
} | |
// Fade out connection over time | |
if (elapsedFrames >= config.pulseDuration) { | |
connection.material.opacity = config.connectionOpacity; | |
connection.material.color.set(connection.originalColor); | |
connection.activationTime = -1; | |
} | |
} | |
} | |
} | |
// Toggle animation state | |
function toggleAnimation() { | |
animationActive = !animationActive; | |
document.getElementById('toggle-animation').textContent = | |
animationActive ? 'Pause Animation' : 'Resume Animation'; | |
} | |
// Reset the camera view | |
function resetView() { | |
camera.position.set(0, 0, 5); | |
camera.lookAt(0, 0, 0); | |
} | |
// Handle window resize | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
} | |
// Basic orbit controls implementation | |
function setupOrbitControls() { | |
let isDragging = false; | |
let previousMousePosition = { x: 0, y: 0 }; | |
const rotationSpeed = 0.01; | |
renderer.domElement.addEventListener('mousedown', function(e) { | |
isDragging = true; | |
previousMousePosition = { x: e.clientX, y: e.clientY }; | |
}); | |
renderer.domElement.addEventListener('mousemove', function(e) { | |
if (isDragging) { | |
const deltaMove = { | |
x: e.clientX - previousMousePosition.x, | |
y: e.clientY - previousMousePosition.y | |
}; | |
networkGroup.rotation.y += deltaMove.x * rotationSpeed; | |
networkGroup.rotation.x += deltaMove.y * rotationSpeed; | |
previousMousePosition = { x: e.clientX, y: e.clientY }; | |
} | |
}); | |
renderer.domElement.addEventListener('mouseup', function() { | |
isDragging = false; | |
}); | |
renderer.domElement.addEventListener('mouseleave', function() { | |
isDragging = false; | |
}); | |
// Zoom functionality | |
renderer.domElement.addEventListener('wheel', function(e) { | |
e.preventDefault(); | |
if (e.deltaY < 0) { | |
// Zoom in | |
camera.position.z = Math.max(2, camera.position.z - 0.5); | |
} else { | |
// Zoom out | |
camera.position.z = Math.min(10, camera.position.z + 0.5); | |
} | |
}); | |
} | |
// Initialize the visualization when page loads | |
window.onload = init; | |
</script> | |
</body> | |
</html> |