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