This guide walks you through building a browser-based audio-to-MIDI converter that runs entirely on the client side. You’ll capture microphone or file audio, analyze pitch in real time, and map it to MIDI notes, then deploy the result as a static site. No server is needed—everything happens in JavaScript using the Web Audio API and a pitch-detection algorithm.
What You’ll Need
- A modern browser (Chrome, Firefox, Edge, or Safari) for testing
- A code editor (VS Code, Sublime, etc.)
- Node.js and npm (for local dev server and build tooling, optional but recommended)
- Basic familiarity with HTML, CSS, and JavaScript
- A free hosting account (Netlify, Vercel, or GitHub Pages) for deployment
Step-by-Step
- Set up the project structure. Create a folder named
audio-to-midiwith anindex.html,style.css, andapp.js. Initialize npm withnpm init -yif you plan to use build tools. - Build the HTML interface. In
index.html, add a button to start/stop audio input, a file input for uploading audio files, a canvas for waveform display, and a list or piano roll to show detected MIDI notes. Use semantic elements and keep the layout simple. - Access the microphone. In
app.js, usenavigator.mediaDevices.getUserMedia({ audio: true })to get an audio stream. Create anAudioContextand anAnalyserNodeto process the stream. - Implement pitch detection. Use the autocorrelation algorithm (a common, lightweight method). Write a function that takes a buffer of time-domain samples and returns the detected frequency in Hz. You’ll need to find the period of the signal by looking for the first peak in the autocorrelation function.
- Convert frequency to MIDI note. Use the formula
midiNote = 69 + 12 * Math.log2(freq / 440). Round to the nearest integer. Optionally, map to note names (C, C#, D, etc.) for display. - Handle file uploads. For uploaded audio files, decode them using
AudioContext.decodeAudioData(), then play them through the same analyser node. You can process the whole file at once or in chunks. - Visualize the waveform. Use
analyser.getByteTimeDomainData()to get the time-domain data and draw it on a canvas. This helps users see what’s being analyzed. - Record detected notes. Maintain an array of
{ time, note, velocity }objects. Velocity can be derived from the amplitude of the signal. Display these in a list or as a simple piano roll. - Add MIDI export. To export as a Standard MIDI File (SMF), you’ll need to write a binary file. Create a function that constructs the MIDI header and track chunks. Use the Web Audio API’s
currentTimeto timestamp notes. Save the file using a Blob and a download link. - Test locally. Run
npx serveornpm startto serve the folder. Open the browser, grant microphone permission, and test with your voice or an instrument. Also test with a pre-recorded audio file. - Deploy to a static host. Upload the three files to Netlify, Vercel, or GitHub Pages. Since there’s no server-side code, any static host works. Ensure HTTPS is enabled (most hosts do this automatically).
Common Mistakes
- Not handling browser permissions correctly. The microphone permission prompt must be triggered by a user gesture (like a button click). If you try to call
getUserMediaon page load, it may fail. - Using the wrong sample rate. The autocorrelation algorithm works best with a sample rate of 44100 Hz. If the audio context uses a different rate, you’ll need to resample or adjust your expected frequency range.
- Ignoring frequency limits. The pitch detection will produce garbage for very low or very high frequencies. Filter out results below 80 Hz and above 1000 Hz (or whatever your instrument range is).
- Not rounding MIDI notes. If you don’t round the calculated MIDI note, you’ll get fractional notes that are not valid.
- Forgetting to close the audio context. Always call
audioContext.close()when stopping to free resources, especially in SPA-like scenarios.
Tips
- Use
requestAnimationFramefor the visualization loop to keep it smooth. - Implement a simple noise gate: ignore frames where the RMS amplitude is below a threshold to avoid detecting background noise.
- For better accuracy, consider using a more advanced algorithm like YIN or the McLeod Pitch Method, but autocorrelation is fine for a first version.
- Store detected notes in a buffer and allow the user to clear or edit them before export.
- Use the Web MIDI API if you want to send notes directly to a MIDI device, but note that it’s not supported in all browsers (Safari lacks it).
Troubleshooting
- Microphone not working: Check that you’re using HTTPS (or localhost). Check browser permissions—look for a blocked microphone icon in the address bar. Ensure no other app is using the mic.
- No pitch detected: Increase the buffer size (e.g., 2048 or 4096 samples). Ensure the input signal is not too quiet. Try a different instrument or voice.
- MIDI file won’t open: Verify the file extension is
.mid. Check the binary structure—use a hex editor to ensure the header chunk is correct. Many DAWs are picky about the tempo meta event; include it. - High CPU usage: Reduce the analyser’s
fftSizeor lower the canvas drawing resolution. Use a lower sample rate for the audio context if acceptable. - Deployment issues: If the app works locally but not on the deployed site, check that all file paths are relative. Also, ensure your host serves
index.htmlas the default document.
Frequently Asked Questions
Can I convert polyphonic audio (multiple notes at once)?
This simple approach only detects monophonic audio (one pitch at a time). For polyphony, you’d need more complex algorithms like spectral analysis or machine learning.
What is the accuracy of the pitch detection?
Autocorrelation is decent for clean signals like a single instrument, but it struggles with background noise or vibrato. Expect errors on low frequencies or rapid pitch changes.
Can I use this with a MIDI keyboard?
No, this converts audio to MIDI, not the other way. For a keyboard, you’d use the Web MIDI API to receive MIDI input directly.
Does it work in all browsers?
Most modern browsers support the Web Audio API. Safari historically had issues with getUserMedia but now supports it. Web MIDI API is not in Safari at all.
Can I export to other formats like MP3?
No, this exports MIDI files only. To get audio, you’d need to synthesize the MIDI with a sampler or synth.
How can I improve the latency?
Use a smaller buffer size (e.g., 1024) for the AudioContext, but this may increase CPU usage. Also, process audio in chunks rather than per-sample.
Related
General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.
More in this bed
- Fix Device Manager Not Opening in Windows 11
- How to Pause Snapchat Location
- System Restore Points Missing in Windows? Fixes
- Loop Nature Ambience Videos with FFmpeg on Ubuntu
- Install League of Legends on Linux: Full Guide
- Fix Error 0x80070001: Causes & Solutions