Have you ever wondered about the tempo of a song you're listening to? Whether you're a DJ preparing a set, a musician learning a new piece, or just curious about the rhythm, knowing the beats per minute (BPM) is essential. In this guide, I'll show you how we built a BPM detector that runs entirely in your browser, the same technology behind our free online BPM detector.
Why Build a Browser-Based BPM Detector?
Before diving into the code, let's talk about why you'd want to analyze music tempo locally in the browser.
Instant Analysis
Upload a file and get results immediately. No waiting for server processing or dealing with network latency. The analysis happens as fast as your browser can process the audio.
Privacy First
Your music files stay on your device. For DJs with unreleased tracks, producers working on secret projects, or anyone who values privacy, this is crucial. No audio data ever leaves your browser.
Works Offline
Once the page loads, you can analyze audio files even without an internet connection. Perfect for studio environments or when you're on the go.
Universal Access
No need to install software or create accounts. Just open a web page, drop in an audio file, and get your BPM reading instantly.
The Architecture Overview
Our BPM detector uses the Web Audio API combined with the web-audio-beat-detector library to analyze audio and extract tempo information. Here's how the components work together:
Understanding BPM Detection
BPM (Beats Per Minute) is a measure of tempo in music. It tells you how many beats occur in one minute. For example:
- 60 BPM: One beat per second (slow ballad)
- 120 BPM: Two beats per second (common for pop music)
- 140 BPM: Fast tempo (common for EDM and dance music)
How Beat Detection Works
The web-audio-beat-detector library uses sophisticated signal processing:
- Onset Detection: Identifies when notes or beats start
- Tempo Estimation: Analyzes the timing between onsets
- Peak Picking: Finds the most likely tempo from the analysis
- Refinement: Uses statistical methods to improve accuracy
Core Data Structures
Let's examine the key data structures in our BPM detector:
React State Management
const [file, setFile] = useState<File | null>(null);
const [bpm, setBpm] = useState<number | null>(null);
const [isAnalyzing, setIsAnalyzing] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const audioRef = useRef<HTMLAudioElement | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
We track the uploaded file, detected BPM, analysis state, and audio playback state.
The Complete Processing Flow
Here's the entire journey from audio file to BPM reading:
The BPM Analysis Function
The core of our BPM detector is remarkably simple:
const analyzeBPM = async (audioFile: File) => {
setIsAnalyzing(true);
setError(null);
try {
// Dynamically import the beat detector
const { analyze } = await import('web-audio-beat-detector');
const arrayBuffer = await audioFile.arrayBuffer();
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const tempo = await analyze(audioBuffer);
setBpm(tempo);
audioContext.close();
} catch (err) {
console.error('BPM analysis error:', err);
setError(t.bpmError || 'Failed to analyze BPM. Please try a different file.');
} finally {
setIsAnalyzing(false);
}
};
Let's break down what's happening:
- Dynamic Import: We load the web-audio-beat-detector library only when needed
- File Reading: Convert the File to an ArrayBuffer for processing
- Audio Decoding: Use Web Audio API to decode the audio data
- BPM Analysis: Pass the AudioBuffer to the analyzer
- Result: Get the tempo as a number (e.g., 128.5 BPM)
Handling File Uploads
We support both click-to-upload and drag-and-drop:
const handleFileChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = e.target.files?.[0];
if (selectedFile) {
if (!selectedFile.type.startsWith('audio/')) {
setError(t.bpmInvalidFile || 'Please upload an audio file');
return;
}
setFile(selectedFile);
setBpm(null);
setError(null);
analyzeBPM(selectedFile);
}
}, [t]);
const handleDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
const droppedFile = e.dataTransfer.files[0];
if (droppedFile) {
if (!droppedFile.type.startsWith('audio/')) {
setError(t.bpmInvalidFile || 'Please upload an audio file');
return;
}
setFile(droppedFile);
setBpm(null);
setError(null);
analyzeBPM(droppedFile);
}
}, [t]);
Both handlers validate the file type and automatically start analysis after upload.
Audio Playback with Visual Metronome
Once BPM is detected, users can preview the audio with a visual metronome:
const togglePlay = () => {
if (!file) return;
if (isPlaying && audioRef.current) {
audioRef.current.pause();
setIsPlaying(false);
} else {
const url = URL.createObjectURL(file);
audioRef.current = new Audio(url);
audioRef.current.onended = () => {
setIsPlaying(false);
};
audioRef.current.play();
setIsPlaying(true);
}
};
The playback creates an object URL for the file and manages play/pause state.
Visual Metronome Animation
The most engaging part of our BPM detector is the visual metronome that swings in time with the detected tempo:
<div
className="relative origin-bottom"
style={isPlaying ? {
animation: `conduct ${60 / bpm}s ease-in-out infinite`,
} : {}}
>
{/* Conductor baton / pointer */}
<div className="flex flex-col items-center">
<div className="w-4 h-4 rounded-full bg-gradient-to-br from-yellow-400 to-yellow-600 shadow-lg mb-1"></div>
<div className="w-1 h-20 bg-gradient-to-b from-yellow-500 to-yellow-700 rounded-full shadow-md"></div>
<div className="w-3 h-3 rounded-full bg-yellow-600 mt-1"></div>
</div>
</div>
The animation duration is calculated as 60 / bpm seconds, which gives the exact duration of one beat. For example:
- 60 BPM = 1 second per beat
- 120 BPM = 0.5 seconds per beat
- 140 BPM = ~0.43 seconds per beat
CSS Animation
@keyframes conduct {
0% {
transform: rotate(-15deg);
}
50% {
transform: rotate(15deg);
}
100% {
transform: rotate(-15deg);
}
}
The baton swings from -15 degrees to +15 degrees, creating a natural conducting motion that matches the music's tempo.
Tempo Categorization
We categorize the detected BPM to help users understand the tempo:
<p className="text-sm text-gray-500">
{bpm < 60
? t.bpmSlow || 'Slow tempo'
: bpm < 100
? t.bpmModerate || 'Moderate tempo'
: bpm < 140
? t.bpmFast || 'Fast tempo'
: t.bpmVeryFast || 'Very fast tempo'}
</p>
Categories:
- < 60 BPM: Slow (ballads, ambient)
- 60-99 BPM: Moderate (many pop songs)
- 100-139 BPM: Fast (dance, rock)
- ā„ 140 BPM: Very fast (EDM, drum and bass)
How web-audio-beat-detector Works
The library we use implements a proven beat detection algorithm:
1. Spectral Flux
The algorithm analyzes changes in the frequency spectrum over time. Sudden increases in energy across multiple frequency bands often indicate beats.
2. Onset Detection
It identifies "onsets" - moments when new notes or sounds begin. These onsets typically align with beats in rhythmic music.
3. Autocorrelation
The algorithm uses autocorrelation to find repeating patterns in the onset detection function. The strongest periodicity indicates the tempo.
4. Peak Picking
From the autocorrelation data, it identifies the most likely tempo candidates and selects the best one.
Supported Audio Formats
Our BPM detector works with any format supported by the Web Audio API:
- MP3: Most common, universally supported
- WAV: Uncompressed, highest quality
- OGG: Open format, good compression
- FLAC: Lossless compression
- M4A/AAC: Common in Apple ecosystems
- WebM: Modern web format
Performance Considerations
Fast Analysis
BPM detection is typically very fast because:
- The algorithm only needs to analyze a portion of the song
- No neural network inference required
- Pure signal processing operations
Memory Efficiency
We clean up resources when done:
const clearFile = () => {
setFile(null);
setBpm(null);
setError(null);
setIsPlaying(false);
if (audioRef.current) {
audioRef.current.pause();
audioRef.current = null;
}
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
Dynamic Imports
We only load the beat detector when needed:
const { analyze } = await import('web-audio-beat-detector');
Browser Compatibility
Our BPM detector works in all modern browsers:
- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Full support
Required APIs:
-
AudioContext: Universal support -
decodeAudioData: Universal support - CSS Animations: Universal support
Accuracy and Limitations
Works Well For:
- Electronic music: Clear, consistent beats
- Pop/Rock: Strong drum patterns
- Hip-hop: Prominent kick and snare
- Dance music: Four-on-the-floor patterns
May Struggle With:
- Classical music: Variable tempo, no drums
- Ambient music: No clear beats
- Live recordings: Inconsistent timing
- Complex polyrhythms: Multiple conflicting tempos
Try It Yourself
Ready to analyze some music? Visit our free online BPM detector and try it out. All processing happens locally - your music files never leave your device.
Conclusion
Building a browser-based BPM detector demonstrates how powerful the Web Audio API is:
- Signal processing in the browser: Complex audio analysis runs entirely client-side
- Instant feedback: No server round-trips means immediate results
- Visual engagement: The metronome animation makes the tool more intuitive
- Universal compatibility: Works with any audio format the browser supports
The complete source is available in our repository. Whether you're building DJ tools, music education apps, or fitness applications, I hope this guide helps you add tempo detection to your projects.
Happy beat counting! šµš„


Top comments (0)