Skip to content

Instantly share code, notes, and snippets.

@adrenak
Created September 5, 2025 19:51
Show Gist options
  • Select an option

  • Save adrenak/d78d362401d051692d21d93a902b8b50 to your computer and use it in GitHub Desktop.

Select an option

Save adrenak/d78d362401d051692d21d93a902b8b50 to your computer and use it in GitHub Desktop.
app.html
<!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