Safari Web Audio API frequency analysis fails for ultrasonic range above 19kHz

I’m building an audio frequency detector using Web Audio API that needs to identify high frequency sounds above 18kHz. The application works great on Chrome, Firefox and Edge but Safari has major problems detecting anything over 19kHz.

When I test with ultrasonic frequencies, Safari always shows the peak amplitude in lower frequency bins around 18-19kHz instead of the actual frequency being played. Other browsers handle this perfectly.

Here’s my audio setup code:

async setupAudioCapture() {
    try {
        const audioSettings = {
            echoCancellation: false,
            noiseSuppression: false,
            autoGainControl: false
        };
        
        let mediaStream = await navigator.mediaDevices.getUserMedia({
            audio: audioSettings
        });
        
        mediaStream.getAudioTracks()[0].applyConstraints(audioSettings);
        this.micStream = mediaStream;
        
        const Context = window.AudioContext || window.webkitAudioContext;
        const ctx = new Context();
        this.samplingRate = ctx.sampleRate;
        
        const freqAnalyzer = ctx.createAnalyser();
        freqAnalyzer.fftSize = CONFIG.FFT_BINS;
        freqAnalyzer.smoothingTimeConstant = 0;
        
        const micInput = ctx.createMediaStreamSource(mediaStream);
        micInput.connect(freqAnalyzer);
        
        const bufferProcessor = ctx.createScriptProcessor(CONFIG.BUFFER_LENGTH, 1, 1);
        bufferProcessor.connect(ctx.destination);
        
        this.analyzer = freqAnalyzer;
        this.scriptNode = bufferProcessor;
    } catch (err) {
        console.error('Audio setup failed:', err);
    }
}

And here’s how I analyze the frequency data:

this.scriptNode.onaudioprocess = () => {
    let spectrumData = new Float32Array(analyzer.frequencyBinCount);
    analyzer.getFloatFrequencyData(spectrumData);
    let filteredSpectrum = filterLowFreqs(spectrumData, this.samplingRate);
    let dominantFreq = getPeakFrequency(filteredSpectrum, this.samplingRate);
    console.log('detected frequency:', dominantFreq);
};

Has anyone encountered similar issues with Safari’s Web Audio implementation for high frequency detection? Any workarounds would be really helpful.

Safari’s got known issues with high frequency audio capture - it’s their internal audio processing that’s causing problems. You’re probably running into Safari’s automatic sample rate conversion and hardware layer, which filters way more aggressively than other browsers.

I hit this same thing building a bat detector app last year. The fix was checking what sample rate Safari actually gives you versus what you ask for. Safari constantly downsamples to 44.1kHz even when higher rates work fine, plus it uses anti-aliasing filters that chop off frequencies above the Nyquist frequency way too hard.

Log ctx.sampleRate right after you create your AudioContext - see what Safari’s actually delivering. If it’s lower than you expected, treat Safari as a special case and tweak your frequency analysis. Also test on real iOS devices, not just desktop Safari - they handle audio capture differently.

This looks like Safari’s hardware audio pipeline messing with your frequency detection. I’ve hit this exact issue working on acoustic measurement tools - Safari applies low-pass filtering at the mic input that other browsers don’t. It’s not just sample rate conversion; Safari’s mic preprocessing can’t be fully disabled even with those constraint settings. Try adding sampleRate: 48000 to your audio constraints - might push the cutoff frequency higher. Also check your CONFIG.FFT_BINS value. Safari handles larger FFT sizes weird and sometimes you get better high-frequency resolution with 8192 or 16384 bins instead of 2048. Worth testing if the problem happens with getByteFrequencyData() instead of getFloatFrequencyData() too - Safari processes these methods with slightly different filtering.

webkit browsers cap ultrasonic detection under 20khz even with higher sample rates. hit the same issue on a frequency generator - safari kept aliasing high frequencies into audible range. force the audiocontext sample rate manually: new AudioContext({sampleRate: 96000}) before anything else. safari’s getFloatFrequencyData is also buggy, so use getByteFrequencyData and convert manually instead.