<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Leo Dan</title>
    <description>The latest articles on DEV Community by Leo Dan (@lea_dan).</description>
    <link>https://dev.to/lea_dan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4079432%2F6579c200-588b-4598-b1c0-896ee25e6e07.png</url>
      <title>DEV Community: Leo Dan</title>
      <link>https://dev.to/lea_dan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lea_dan"/>
    <language>en</language>
    <item>
      <title>I Built an Audio Tool That Runs in the Browser. Here's What I Wish I'd Known First</title>
      <dc:creator>Leo Dan</dc:creator>
      <pubDate>Sun, 16 Aug 2026 02:52:02 +0000</pubDate>
      <link>https://dev.to/lea_dan/i-built-an-audio-tool-that-runs-in-the-browser-heres-what-i-wish-id-known-first-36fg</link>
      <guid>https://dev.to/lea_dan/i-built-an-audio-tool-that-runs-in-the-browser-heres-what-i-wish-id-known-first-36fg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqvcyof8dulmob19yaw33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqvcyof8dulmob19yaw33.png" alt=" " width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  I Built an Audio Tool That Runs in the Browser. Here's What I Wish I'd Known First
&lt;/h1&gt;

&lt;p&gt;A few months ago I put out ezaudio. It's a small set of audio tools, a vocal remover, a stem splitter, and an audio to text thing, and the whole point is that you use it straight from your browser. No app to install, no desktop setup. You open a tab, drop a file in, and get the result back.&lt;/p&gt;

&lt;p&gt;I'm not here to sell it. The link is at the bottom and I'll say so plainly. I just want to talk through what building this actually taught me, because a lot of it I had to learn the hard way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why even put audio tools in a browser
&lt;/h2&gt;

&lt;p&gt;The easy answer is the one everyone gives. No install step. Anything with a browser is a supported platform, so you skip the entire nightmare of building native apps for every OS. For one person or a tiny team that matters more than people admit. It's the difference between shipping and not.&lt;/p&gt;

&lt;p&gt;But the reason people actually kept using mine wasn't the convenience alone. It was the zero friction entry. No downloads, no account wall, no "which version runs on my machine" questions. That low barrier is the whole reason a tool like this gets tried at all. I almost treated that as a footnote early on and that was a mistake.&lt;/p&gt;

&lt;p&gt;The tradeoff is real though. You're at the mercy of the network and the backend. Every request has latency, and a flaky connection means a flaky product. You don't get to pretend the heavy work is free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser is the front door, not the engine
&lt;/h2&gt;

&lt;p&gt;This bit tripped me up. The Web Audio API is genuinely good at what it does in the browser: playing the result back, routing it, letting the user scrub and preview. If you've used &lt;code&gt;AudioContext&lt;/code&gt; you know what I mean. &lt;code&gt;decodeAudioData&lt;/code&gt; handles most of the decoding, &lt;code&gt;AudioBuffer&lt;/code&gt; gives you something you can actually process and play, and &lt;code&gt;OfflineAudioContext&lt;/code&gt; lets you run the graph without a live audio output. That last one is more useful than it sounds. It turns the browser into a batch processor for the parts you do want client-side, like preview rendering.&lt;/p&gt;

&lt;p&gt;But the actual separation and transcription, the part that matters, that runs on the server. The heavy compute happens either on our own machines or through third party model APIs, and the browser just sends the audio up, shows progress, and plays the result. So Web Audio in my case is the plumbing, not the brain.&lt;/p&gt;

&lt;p&gt;That changes how you think about the product. You're not shipping compute, you're shipping a thin client with a good UI. The hard problems move to the backend: latency, cost per call, and keeping the models honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The vocal remover and stem splitter aren't browser tricks
&lt;/h2&gt;

&lt;p&gt;Here's the thing nobody tells you before you start: the interesting audio work isn't a browser problem at all. It's a machine learning problem wearing a web app costume.&lt;/p&gt;

&lt;p&gt;Vocal removal and stem splitting both come down to source separation, and the models that do it well are trained on spectrograms, not waveforms. You take the mix, transform it into a time-frequency representation, and the model learns to predict a mask for each stem, a value per time-frequency bin that says "this is mostly vocals" or "this is mostly drums." Multiply the mix by the mask, invert back to audio, and you've got your stem.&lt;/p&gt;

&lt;p&gt;Open source has gotten frighteningly good at this. Meta's Demucs family is the one people keep coming back to, and it splits into four stems, drums, bass, other, and vocals. There's a trick worth knowing: if you want a clean karaoke track, separating into all four and summing everything except the vocals usually sounds better than any dedicated two-stem vocal remover, because each instrument mask was learned with its own structure instead of one catch-all "accompaniment" blob.&lt;/p&gt;

&lt;p&gt;None of this runs in a browser tab. These models want a GPU, or at least a very patient CPU. So the whole "browser tool" is really a server-side inference pipeline with a clean upload and playback wrapper. The browser does the parts it's good at, and the server does the parts it's actually good at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transcription has its own trap
&lt;/h2&gt;

&lt;p&gt;Speech to text is the same story from a different angle. The model is open and excellent, OpenAI's Whisper family runs locally and fast, but production is where the edges show up.&lt;/p&gt;

&lt;p&gt;Whisper has a documented habit of hallucinating. Give it silence, music, or heavy noise and it can invent speech out of nothing, sometimes repeating a phrase until the audio stops. The fix is voice activity detection, you gate what gets fed to the model so silence and music beds never reach it. That one change cut our weird outputs by a huge margin.&lt;/p&gt;

&lt;p&gt;Long files are their own problem. Feed a two hour recording to a model with a context window and quality drifts, so you chunk with overlap and stitch the results back together. And if you want word-level timestamps, which people actually use for search and captions, you end up adding a forced alignment step on top of the base transcription. Every one of these is a known problem with a known fix, but they're all work, and they're all yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stuff that bit me anyway
&lt;/h2&gt;

&lt;p&gt;Mobile Safari is its own world. Things that are totally fine on desktop Chrome will just behave differently or refuse outright on iOS. Test there early.&lt;/p&gt;

&lt;p&gt;Formats are inconsistent too. The browser can decode a lot but not everything, and of course the weird files are exactly the ones your users upload. We normalized everything server side before it touched a model, and that one habit saved us a dozen support tickets.&lt;/p&gt;

&lt;p&gt;Large files will blow up upload time and memory if you're not careful, so you end up chunking and showing progress whether you like it or not. And there's a real tension between quality and how long the user waits. You can't fully win. You just pick which one your tool is for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy isn't a feature, it's the default
&lt;/h2&gt;

&lt;p&gt;Audio is personal. People upload recordings of themselves, their meetings, their songs, and they notice when you keep them. Our posture is simple: files get auto-deleted within 24 hours, no permanent storage, no "keep it for your convenience." That's not a marketing line, it's an engineering decision that shapes the storage layer, the database, everything. It's also the cheapest trust you can buy.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're thinking about building something like this
&lt;/h2&gt;

&lt;p&gt;Start with the UX. Get the upload, the preview, and the result flow feeling instant even when the backend is slow. A spinner that lies is worse than an honest progress bar.&lt;/p&gt;

&lt;p&gt;Keep the client thin. Push as much logic to the server as you reasonably can, and treat the browser as a view layer.&lt;/p&gt;

&lt;p&gt;Test on the worst device and the worst connection you can find, not the laptop on your fast wifi.&lt;/p&gt;

&lt;p&gt;And be straight about limits. A tool that admits what it's bad at earns more trust than one that promises everything.&lt;/p&gt;

&lt;p&gt;If any of this was useful, ezaudio is at &lt;a href="https://ezaudio.io" rel="noopener noreferrer"&gt;ezaudio.io&lt;/a&gt;. Free to try, runs in your browser.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>llm</category>
      <category>api</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
