Skip to content

Instantly share code, notes, and snippets.

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

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

Select an option

Save adrenak/70c38f1b5e6ea2e4afbf20b3b6334d7a to your computer and use it in GitHub Desktop.

Revisions

  1. adrenak created this gist Sep 5, 2025.
    101 changes: 101 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    <!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>