If you've ever tried building a web-based instrument tuner using the Web Audio API, you probably started where most of us do: feeding microphone data into an AnalyserNode, running a Fast Fourier Transform (FFT), and looking for the highest frequency peak.
It’s a classic, straightforward approach. And for synthesized tones or simple plucked strings, it works reasonably well.
But recently, I built a browser-based tuner specifically for the Erhu (a traditional two-stringed Chinese bowed instrument), and I quickly realized that standard FFT-based pitch detection completely falls apart when faced with a bow.
Here is why bowing breaks standard tuners, and how switching to time-domain Autocorrelation algorithms solved the problem.
The Problem: Bowing Transients and Noise
The erhu has no fingerboard and no frets. The strings are suspended in the air, and the player presses them to change pitch. This makes tuning the open strings incredibly critical—if the open D4 or A4 is off by even a fraction of a Hertz, the player's entire muscle memory for intonation is compromised.
To tune an erhu properly, a player must use their bow. Plucking the string creates a different tension and a slightly different pitch.
But when horsehair coated in rosin drags across a steel string, it creates chaos in the frequency spectrum:
High-frequency scratchiness: The friction of the bow creates continuous, non-harmonic high-frequency noise
Transients: The initial "bite" of the bow creates a burst of overtones
Complex Harmonics: Bowed instruments often have incredibly strong secondary and tertiary harmonics that can temporarily trick an FFT into thinking the fundamental pitch has jumped up an octave.
If you run this signal through a basic zero-crossing algorithm or a simple FFT, the digital needle on your tuner will jump erratically. The noise floor shifts, the harmonics confuse the peak-picker, and the user is left guessing where their pitch actually sits.
The Solution: Autocorrelation in the Time Domain
To get the sub-1Hz accuracy required for fretless instruments, we need an algorithm that ignores frequency noise and focuses strictly on periodicity.
Enter Autocorrelation.
Instead of converting the audio into the frequency domain (like FFT), autocorrelation stays in the time domain. At its core, autocorrelation asks a simple mathematical question: "How much does this audio signal look like a time-shifted version of itself?"
Instead of analyzing frequency bands, think of the algorithm as sliding two identical transparencies of a waveform over each other. The system captures a short snapshot of the audio, makes a digital copy, and begins shifting that copy forward one tiny time-step (or sample) at a time. At each shift, it mathematically compares the overlapping data points. When the peaks and valleys of the shifted copy perfectly lock into the next set of peaks and valleys on the original snapshot, the calculation produces a massive mathematical spike—known as the correlation peak. The exact distance of that shift (the "lag") represents the length of one complete cycle of the soundwave. By simply dividing the audio's sample rate by this lag distance, we instantly get the incredibly precise fundamental frequency in Hertz.
Why this works so well for bowed instruments:
Because the bow noise (the scratchiness) is random and non-periodic, it doesn't align with itself when shifted. The autocorrelation algorithm naturally filters out this transient noise, completely ignoring the scratch and focusing only on the repeating fundamental waveform of the string.
Implementing in the Browser
To make this work seamlessly without lagging the UI thread, the heavy lifting needs to be offloaded. In the past, developers used ScriptProcessorNode, but that is now deprecated.
Modern implementations should use the AudioWorklet interface. By pushing the autocorrelation math into a dedicated Worklet thread, we can process the audio buffers in real-time, completely isolated from the main JavaScript thread handling the DOM animations of the tuner dial.
Dealing with the "Octave Error"
Autocorrelation isn't perfect. Its biggest flaw is the "Octave Error."
If a waveform aligns with itself perfectly at a shift of 100 samples, it will also align with itself perfectly at 200 samples, 300 samples, etc. Sometimes, due to the harmonic makeup of the instrument, the algorithm might lock onto a sub-harmonic, incorrectly reporting the pitch as exactly one octave lower than it actually is.
To combat this, robust tuners implement Peak Picking Heuristics. Instead of just grabbing the highest correlation value, we analyze the first significant peak that crosses a specific threshold, verifying it against surrounding frames to ensure stability.
The Result
By combining time-domain autocorrelation with smooth UI interpolation, we get a tuner that doesn't jump, doesn't get confused by rosin scratch, and allows an Erhu player to lock in their D4 and A4 perfectly.
If you are building audio tools for the web, don't blindly reach for FFT. If your goal is pitch detection—especially for organic, bowed, or noisy instruments—autocorrelation is almost always the better choice.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.