Audio Visualizer/Analyzer Vanilla JavaScript issue or possibly CORS

So I’m working on an app that compares heavy metal vocals from popular artists (fundamental freq. key, dB, all that good stuff). However my audio recorder works (which was some of the harder work so that’s good), but my audio visualizer (canvas) and audio element on the left which is loaded by by src=”” but I think something in my JS is making it bug out because it was working in the beginning. It wasn’t until I got to messing with the harmonics, decibels, key, pitch, etc. did things go wrong. And this is after forgetting to save the better working version of the code. Now I’m completely lost. I’m also running an apache server with XAMPP. If you guys have any questions about my code or what I’m trying to accomplish, feel free to ask. Also I should mentioned I did half to coding and I use chatGPT for some of the more technical issues. Which also keeps messy up my code as well so I thought I’d rejoin this site and see if I can get any help. I’m really passionate about about sound engineering and heavy music so I would really like to see this site/app come to life for people to practice their vocals. Any help would be awesome!!! This my code.

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Audio Visualizer</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        
        @font-face {
            font-family: 'Deathcore';
            src: url('fonts/Faceless.ttf') format('truetype');
        }

        @font-face {
            font-family: 'Crunk';
            src: url('fonts/TheDark.ttf') format('truetype');
        }
        
        div#content-1 {
            display: block;
            width: 100%;
            height: 100%;
            background-color: #000000;
        }
        div#nav-bar {
            position: fixed;
            width: 100%;
            height: 50px;
            background-color: #131313;
            border-bottom: solid 1px #ff0000;
        }
        select {
            border-radius: 15px;
            border: 2px solid #ffffff;
            color: #ffffff;
            background-color: #000000;
            width: 175px;
            height: 36px;
            margin-top: 9px;
            margin-left: 20px;
        }
        img#profile-picture {
            margin-top: 30px;
            width: 350px;
            height: 300px;
            border: 1px solid #ffffff;
        }
        audio {
            height: 45px;
            border-radius: 0px;
            border: 2px solid #ffffff;
            background-color: #131313;
        }
        canvas {
            width: 48%;
            height: 135px;
            display: block;
            background-color: #000000;
        }
        #controls {
            position: absolute;
            bottom: 193px;
            right: 30px;
        }
        button {
            margin-left: 5px;
            padding: 10px;
            background-color: #ff0000;
            border: none;
            color: white;
            cursor: pointer;
            border-radius: 5px;
        }
        button:hover {
            background-color: #cc0000;
        }
        /* New styles for info divs */
        .info {
            font-family: Arial;
            position: absolute;
            width: 320px;
            height: 150px;
            background-color: #808080;
            color: #ffffff;
            padding: 10px;
            border-radius: 5px;
            font-size: 18px;
        }
        #info-artist {
            bottom: 235px;
            left: 30px;
        }
        #info-user {
            bottom: 235px;
            right: 30px;
        }
        
        div#bio {
            float: left;
        }
        div#name-info {
            margin-left: 20px;
            font-size: 30px;
            font-family: Deathcore;
            color: #ffffff;
        
        }
        div#band-info {
            margin-left: 28px;
            font-size: 20px;
            font-family: Arial;
            color: #ffffff;
        
        }
        div#lyrics {
            position: absolute;
            left: 500px;
            width: 930px;
            height: 350px;
            border-radius: 15px;
            background-color: #131313;
            color: #ffffff;
            font-family: Arial;
        }
    </style>
</head>
<body>
    <div id='content-1'>
        <div id='nav-bar'>
            <select id='vocal-style'>
                <option>Fry</option>
                <option>False Chord</option>
                <option>Gutterals</option>
            </select>
            <select id='vocal-artist'>
                <option>Danny Worsnop</option>
                <option>Alex Terrible</option>
                <option>Cj McCreery</option>
            </select>
            <button id="generateSampleBtn">Generate New Sample</button>
            <button id="downloadSampleBtn">Download Sample</button>
        </div>
        <br>
        
        <div style='margin-top: 20px;margin-left: 10px;'>
            <img id='profile-picture' src='profile_pics/CJ_McCreery.jpg'>
        </div>
        <div id='bio'>
            <div id='name-info'>
                Alex Terrible
            </div>
            <div id='band-info'>
                Band: Slaughter to Prevail
            </div>
        </div>
        
        <div id="controls">
            <button id="recordBtn">Record</button>
            <button id="stopBtn" disabled>Stop</button>
            <button id="downloadBtn" disabled>Download</button>
            <button id="compareBtn" disabled>Compare Audio</button>
        </div>
        <div style='position: absolute; bottom: 5px;width:100%;left: 30px;'>
            <canvas style='margin-left: 932px; margin-bottom: -135px;' id='canvas-user'></canvas>
            <canvas style='margin-left: 2px;' id='canvas-artist'></canvas>
            <audio id='artist-audio' style='width:48%;' src='samples/false_chord/alex_terrible/sample-1.mp3' controls></audio>
            <audio id='user-audio' style='width:48%;' controls></audio>
        </div>
        
        <!-- New divs for displaying audio info -->
        <div id="info-artist" class="info">
            <strong>Artist Audio:</strong>
            <div id="dB-artist">dB: </div>
            <div id="fundamental-artist">Fundamental Frequency: </div>
            <div id="harmonics-artist">Harmonics: </div>
            <div id="pitch-artist">Pitch: </div>
            <div id="key-artist">Key: </div>
        </div>
        <div id="info-user" class="info">
            <strong>User Audio:</strong>
            <div id="dB-user">dB: </div>
            <div id="fundamental-user">Fundamental Frequency: </div>
            <div id="harmonics-user">Harmonics: </div>
            <div id="pitch-user">Pitch: </div>
            <div id="key-user">Key: </div>
        </div>
        <div id='lyrics'>
            Hell is right here! You've been abandoned, you've been abandoned! Hell is right here! Kingdom of storm and thunder
        </div>
    </div>

    <script>
    window.onload = function () {
    // All your JavaScript code goes here

    let isRecording = false; // State for recording
    let mediaRecorder;
    let recordedChunks = [];

    // Record functionality
    recordBtn.addEventListener('click', function () {
        recordedChunks = []; // Reset recorded chunks
        navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
            mediaRecorder = new MediaRecorder(stream);
            mediaRecorder.start();
            isRecording = true;
            recordBtn.disabled = true;
            stopBtn.disabled = false;

            // Save audio data chunks
            mediaRecorder.ondataavailable = function (event) {
                recordedChunks.push(event.data);
            };

            // Handle media recorder stop event
            mediaRecorder.onstop = function () {
                const blob = new Blob(recordedChunks, { type: 'audio/webm' });
                userAudio.src = URL.createObjectURL(blob);
                downloadBtn.disabled = false;
            };
        });
    });

    stopBtn.addEventListener('click', function () {
        if (isRecording) {
            mediaRecorder.stop();
            isRecording = false;
            recordBtn.disabled = false;
            stopBtn.disabled = true;
        }
    });

    // Download functionality
    downloadBtn.addEventListener('click', function () {
        const blob = new Blob(recordedChunks, { type: 'audio/webm' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        a.download = 'recording.webm';
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
    });

    const artistAudio = document.getElementById('artist-audio');
    const userAudio = document.getElementById('user-audio');
    const canvasArtist = document.getElementById('canvas-artist');
    const canvasUser = document.getElementById('canvas-user');
    const ctxArtist = canvasArtist.getContext('2d');
    const ctxUser = canvasUser.getContext('2d');

    // Set canvas dimensions based on their offset sizes
    canvasArtist.width = canvasArtist.offsetWidth;
    canvasArtist.height = canvasArtist.offsetHeight;
    canvasUser.width = canvasUser.offsetWidth;
    canvasUser.height = canvasUser.offsetHeight;

    // Function to calculate and update audio information
    function calculateAudioSpecs(analyser, dataArray, bufferLength, infoPrefix) {
        analyser.getByteFrequencyData(dataArray);

        let sum = 0;
        for (let i = 0; i < bufferLength; i++) {
            sum += dataArray[i] ** 2;
        }
        const rms = Math.sqrt(sum / bufferLength);
        const dB = 20 * Math.log10(rms / 255);
        document.getElementById(`dB-${infoPrefix}`).innerText = `dB: ${dB.toFixed(2)}`;

        let maxIndex = 0;
        let maxValue = -Infinity;
        for (let i = 0; i < bufferLength; i++) {
            if (dataArray[i] > maxValue) {
                maxValue = dataArray[i];
                maxIndex = i;
            }
        }
        const fundamentalFreq = maxIndex * analyser.context.sampleRate / analyser.fftSize;
        document.getElementById(`fundamental-${infoPrefix}`).innerText = `Fundamental Frequency: ${fundamentalFreq.toFixed(2)} Hz`;

        const harmonics = maxValue / 255;
        document.getElementById(`harmonics-${infoPrefix}`).innerText = `Harmonics: ${harmonics.toFixed(2)}`;

        const pitch = fundamentalFreq;
        document.getElementById(`pitch-${infoPrefix}`).innerText = `Pitch: ${pitch.toFixed(2)} Hz`;

        const key = determineKey(fundamentalFreq);
        document.getElementById(`key-${infoPrefix}`).innerText = `Key: ${key}`;
    }

    // Function to determine musical key from frequency
    function determineKey(frequency) {
        const keys = [
            { note: "C", frequency: 261.63 },
            { note: "C#", frequency: 277.18 },
            { note: "D", frequency: 293.66 },
            { note: "D#", frequency: 311.13 },
            { note: "E", frequency: 329.63 },
            { note: "F", frequency: 349.23 },
            { note: "F#", frequency: 369.99 },
            { note: "G", frequency: 392.00 },
            { note: "G#", frequency: 415.30 },
            { note: "A", frequency: 440.00 },
            { note: "A#", frequency: 466.16 },
            { note: "B", frequency: 493.88 }
        ];

        let closestKey = keys[0].note;
        let closestFreqDiff = Math.abs(frequency - keys[0].frequency);

        keys.forEach(key => {
            const freqDiff = Math.abs(frequency - key.frequency);
            if (freqDiff < closestFreqDiff) {
                closestFreqDiff = freqDiff;
                closestKey = key.note;
            }
        });

        return closestKey;
    }

    // AudioContext setup for Artist audio
    const audioContextArtist = new (window.AudioContext || window.webkitAudioContext)();
    const analyserArtist = audioContextArtist.createAnalyser();
    const sourceArtist = audioContextArtist.createMediaElementSource(artistAudio);
    sourceArtist.connect(analyserArtist);
    analyserArtist.connect(audioContextArtist.destination);

    const bufferLengthArtist = analyserArtist.frequencyBinCount;
    const dataArrayArtist = new Uint8Array(bufferLengthArtist);
    analyserArtist.fftSize = 2048;

    // AudioContext setup for User audio
    const audioContextUser = new (window.AudioContext || window.webkitAudioContext)();
    const analyserUser = audioContextUser.createAnalyser();
    const sourceUser = audioContextUser.createMediaElementSource(userAudio);
    sourceUser.connect(analyserUser);
    analyserUser.connect(audioContextUser.destination);

    const bufferLengthUser = analyserUser.frequencyBinCount;
    const dataArrayUser = new Uint8Array(bufferLengthUser);
    analyserUser.fftSize = 2048;

    // Function to draw visualization and update audio info
    function draw() {
        requestAnimationFrame(draw);

        // Clear canvas for Artist
        ctxArtist.clearRect(0, 0, canvasArtist.width, canvasArtist.height);
        analyserArtist.getByteFrequencyData(dataArrayArtist);

        ctxArtist.fillStyle = '#ff0000';
        for (let i = 0; i < bufferLengthArtist; i++) {
            const barHeight = dataArrayArtist[i] / 2;
            ctxArtist.fillRect(i * 2, canvasArtist.height - barHeight, 1, barHeight);
        }

        // Clear canvas for User
        ctxUser.clearRect(0, 0, canvasUser.width, canvasUser.height);
        analyserUser.getByteFrequencyData(dataArrayUser);

        ctxUser.fillStyle = '#ff0000';
        for (let i = 0; i < bufferLengthUser; i++) {
            const barHeight = dataArrayUser[i] / 2;
            ctxUser.fillRect(i * 2, canvasUser.height - barHeight, 1, barHeight);
        }

        // Update audio info for Artist
        calculateAudioSpecs(analyserArtist, dataArrayArtist, bufferLengthArtist, 'artist');

        // Update audio info for User
        calculateAudioSpecs(analyserUser, dataArrayUser, bufferLengthUser, 'user');
    }

    draw();
};

    </script>
</body>
</html>

I’ve tried different approaches of loading the audio element, I’ve tried changing the cross-origin header on my apache server, I’ve even tried using chatGPT, and now I’m here and I’ll be honest I’m not the best programmer either, I’m better at sound engineering and music production but thought I’d give it a shot.