Created
September 5, 2025 19:51
-
-
Save adrenak/d78d362401d051692d21d93a902b8b50 to your computer and use it in GitHub Desktop.
app.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Mic Spectrum Visualizer</title> | |
| <style> | |
| html, body { | |
| margin: 0; | |
| padding: 0; | |
| overflow: hidden; | |
| width: 100%; | |
| height: 100%; | |
| background: #000; | |
| } | |
| canvas { | |
| display: block; | |
| } | |
| #info { | |
| position: absolute; | |
| top: 10px; | |
| left: 10px; | |
| color: white; | |
| font-family: sans-serif; | |
| font-size: 14px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="info">🎤 Allow microphone access to see visualization</div> | |
| <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script> | |
| <script> | |
| // Setup Three.js scene | |
| const scene = new THREE.Scene(); | |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| camera.position.z = 100; | |
| const renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| document.body.appendChild(renderer.domElement); | |
| // Bars for spectrum | |
| const barCount = 64; // number of frequency bars | |
| const bars = []; | |
| const spacing = 2; | |
| for (let i = 0; i < barCount; i++) { | |
| const geometry = new THREE.BoxGeometry(1, 1, 1); | |
| const material = new THREE.MeshBasicMaterial({ color: 0x44ccff }); | |
| const bar = new THREE.Mesh(geometry, material); | |
| bar.position.x = (i - barCount / 2) * (spacing); | |
| scene.add(bar); | |
| bars.push(bar); | |
| } | |
| // Handle resize | |
| window.addEventListener('resize', () => { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // Web Audio setup | |
| let analyser, dataArray; | |
| async function initAudio() { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); | |
| const source = audioCtx.createMediaStreamSource(stream); | |
| analyser = audioCtx.createAnalyser(); | |
| analyser.fftSize = 128; // resolution of frequency bins | |
| const bufferLength = analyser.frequencyBinCount; | |
| dataArray = new Uint8Array(bufferLength); | |
| source.connect(analyser); | |
| document.getElementById("info").style.display = "none"; | |
| animate(); | |
| } catch (err) { | |
| document.getElementById("info").innerText = "Microphone access denied."; | |
| } | |
| } | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| if (analyser) { | |
| analyser.getByteFrequencyData(dataArray); | |
| for (let i = 0; i < bars.length; i++) { | |
| const value = dataArray[i]; | |
| bars[i].scale.y = value / 20; // scale height | |
| bars[i].position.y = bars[i].scale.y / 2; // lift bar from bottom | |
| bars[i].material.color.setHSL(value / 255, 1, 0.5); // color shift | |
| } | |
| } | |
| renderer.render(scene, camera); | |
| } | |
| initAudio(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment