<?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: MP3 to WAV Convert</title>
    <description>The latest articles on DEV Community by MP3 to WAV Convert (@mp3towav).</description>
    <link>https://dev.to/mp3towav</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%2F4163444%2Fec046fd2-02c9-4812-8897-66afa70d664c.png</url>
      <title>DEV Community: MP3 to WAV Convert</title>
      <link>https://dev.to/mp3towav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mp3towav"/>
    <language>en</language>
    <item>
      <title>Converting MP3 to WAV in the browser (and the 48 kHz trap nobody warns you about)</title>
      <dc:creator>MP3 to WAV Convert</dc:creator>
      <pubDate>Mon, 05 Oct 2026 09:24:49 +0000</pubDate>
      <link>https://dev.to/mp3towav/converting-mp3-to-wav-in-the-browser-and-the-48-khz-trap-nobody-warns-you-about-1565</link>
      <guid>https://dev.to/mp3towav/converting-mp3-to-wav-in-the-browser-and-the-48-khz-trap-nobody-warns-you-about-1565</guid>
      <description>&lt;p&gt;I built a free MP3 to WAV converter that never uploads your files. Everything runs in the browser with the Web Audio API. The core turned out to be about 2 KB, so I open-sourced it as &lt;a href="https://github.com/mp3towav/mp3-to-wav-browser" rel="noopener noreferrer"&gt;mp3-to-wav-browser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's how it works, and the one bug that almost every "convert audio in JS" snippet has.&lt;/p&gt;

&lt;h2&gt;
  
  
  The naive version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AudioContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// ...write audio.getChannelData() into a WAV&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, but check the result's sample rate. Your 44.1 kHz MP3 comes out at &lt;strong&gt;48 kHz&lt;/strong&gt; (or whatever the sound card runs at). &lt;code&gt;decodeAudioData&lt;/code&gt; always resamples to the context's rate. For a CD (which must be 44.1 kHz) or a speech model trained on 16 kHz, that's wrong, and the file is also about 9% bigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: read the rate first
&lt;/h2&gt;

&lt;p&gt;An MP3 is a sequence of frames, and each frame header carries the sample rate in 2 bits. Skip the ID3v2 tag (its size is a 28-bit "syncsafe" integer at bytes 6–9), find the first frame sync (11 set bits), and look up the rate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MPEG version (bits 19–20) -&amp;gt; sample rates for rate index 0, 1, 2 (bits 10–11)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;44100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32000&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// MPEG-1&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;22050&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// MPEG-2&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;// MPEG-2.5&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then decode on an &lt;code&gt;OfflineAudioContext&lt;/code&gt; created &lt;strong&gt;at that rate&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OfflineAudioContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sourceRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No resampling happens, so you get the samples exactly as the MP3 decoder produced them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the WAV
&lt;/h2&gt;

&lt;p&gt;A WAV file is a 44-byte RIFF header plus interleaved samples. 16-bit is the common case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInt16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mh"&gt;0x8000&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mh"&gt;0x7fff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For 32-bit you write IEEE float and set the format tag to 3 instead of 1. 24-bit needs three bytes per sample written by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Correct WAV headers, verified with Node's built-in test runner&lt;/li&gt;
&lt;li&gt;About 2.3 KB gzipped, zero dependencies&lt;/li&gt;
&lt;li&gt;Batch-converting 20 songs takes a few seconds on a laptop because nothing is uploaded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://mp3-to-wav-browser.vercel.app/" rel="noopener noreferrer"&gt;https://mp3-to-wav-browser.vercel.app/&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Code (MIT):&lt;/strong&gt; &lt;a href="https://github.com/mp3towav/mp3-to-wav-browser" rel="noopener noreferrer"&gt;https://github.com/mp3towav/mp3-to-wav-browser&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you just need to convert files rather than write code, the full app with batch ZIP downloads and presets is at &lt;a href="https://mp3towavconvert.com/" rel="noopener noreferrer"&gt;mp3towavconvert.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One honest note: converting MP3 to WAV &lt;strong&gt;doesn't improve quality&lt;/strong&gt;. It gives you an uncompressed copy that edits cleanly and works with CD burners, samplers and speech-to-text tools.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
