<?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: Web Harmonium</title>
    <description>The latest articles on DEV Community by Web Harmonium (@webharmonium).</description>
    <link>https://dev.to/webharmonium</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%2F4069767%2F1264be5a-5a4c-4b64-8ecf-b59ea58f11e5.png</url>
      <title>DEV Community: Web Harmonium</title>
      <link>https://dev.to/webharmonium</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/webharmonium"/>
    <language>en</language>
    <item>
      <title>Building a Playable Indian Harmonium in the Browser with the Web Audio API</title>
      <dc:creator>Web Harmonium</dc:creator>
      <pubDate>Mon, 10 Aug 2026 09:33:27 +0000</pubDate>
      <link>https://dev.to/webharmonium/building-a-playable-indian-harmonium-in-the-browser-with-the-web-audio-api-2jnf</link>
      <guid>https://dev.to/webharmonium/building-a-playable-indian-harmonium-in-the-browser-with-the-web-audio-api-2jnf</guid>
      <description>&lt;p&gt;The harmonium is everywhere in Indian music — kirtan, bhajan, ghazal, film songs, a&lt;br&gt;
hundred years of Hindustani vocal training. It is also an awkward instrument to own.&lt;br&gt;
A decent one costs a few hundred dollars, weighs as much as a carry-on suitcase, and&lt;br&gt;
goes out of tune if you look at it wrong.&lt;/p&gt;

&lt;p&gt;So I built one that runs in a browser tab. &lt;a href="https://web-harmonium.app/" rel="noopener noreferrer"&gt;Web Harmonium&lt;/a&gt;&lt;br&gt;
is a free, sample-based harmonium you can play with your computer keyboard, a mouse,&lt;br&gt;
a touchscreen, or a MIDI controller. This post is about the parts that turned out to&lt;br&gt;
be harder than expected — and they were almost never the parts I expected.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why sampling, not synthesis
&lt;/h2&gt;

&lt;p&gt;A harmonium makes sound by pushing air across free metal reeds. That is a genuinely&lt;br&gt;
messy physical process: the reed's attack has a breathy transient, the pitch bends&lt;br&gt;
slightly as air pressure stabilises, and two reeds voiced to the same note are never&lt;br&gt;
in perfect agreement. Modelling that from oscillators gets you something that sounds&lt;br&gt;
like an accordion impression of a harmonium.&lt;/p&gt;

&lt;p&gt;Sampling sidesteps the whole problem. I used recordings from Yale's Euterpea Studio,&lt;br&gt;
which are released CC0, so there is no licensing footgun for a free tool.&lt;/p&gt;

&lt;p&gt;The catch with sampling is storage. One sample per key across three octaves is a lot&lt;br&gt;
of audio to ship to someone on a phone connection. The standard trick is to sample&lt;br&gt;
sparsely and interpolate: record nine points across the range, then pitch-shift the&lt;br&gt;
nearest neighbour to fill the gaps.&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;// Nearest sampled note wins; playbackRate does the pitch shift.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semitonesFrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;midiNote&lt;/span&gt;&lt;span class="p"&gt;)&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;nearest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pickNearestSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;midiNote&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;src&lt;/span&gt; &lt;span class="o"&gt;=&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;createBufferSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nearest&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;playbackRate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;semitonesFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nearest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;midiNote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gain&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two semitones of shift in either direction is about the limit before the timbre&lt;br&gt;
starts sounding obviously wrong — pitch up too far and the reed develops a chipmunk&lt;br&gt;
quality, pitch down and it goes slack and hollow. Nine sample points across three&lt;br&gt;
octaves keeps every note within that budget.&lt;/p&gt;
&lt;h2&gt;
  
  
  The detail nobody notices until it is missing
&lt;/h2&gt;

&lt;p&gt;A real harmonium has multiple reed banks. Play a single note and you are usually&lt;br&gt;
hearing two or three reeds sounding together, tuned very slightly apart. That tiny&lt;br&gt;
disagreement is what makes the instrument sound alive rather than like a sine wave&lt;br&gt;
with a costume on.&lt;/p&gt;

&lt;p&gt;Layering the same sample twice and detuning one copy by a few cents reproduces most&lt;br&gt;
of it:&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;voices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="c1"&gt;// cents&lt;/span&gt;
&lt;span class="nx"&gt;voices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cents&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;src&lt;/span&gt; &lt;span class="o"&gt;=&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;createBufferSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detune&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voiceGain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four cents sounds like nothing written down. Toggle it off and the instrument&lt;br&gt;
immediately sounds cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bellows problem
&lt;/h2&gt;

&lt;p&gt;This is the one I underestimated badly.&lt;/p&gt;

&lt;p&gt;On a physical harmonium your left hand pumps the bellows continuously while your&lt;br&gt;
right hand plays. Volume is not a setting — it is a live, continuous gesture, and it&lt;br&gt;
is half of what makes phrasing expressive. A slider labelled "Volume" throws that&lt;br&gt;
away entirely.&lt;/p&gt;

&lt;p&gt;The compromise: a draggable bellows handle that maps drag velocity to gain, so&lt;br&gt;
pumping the mouse or a finger actually produces the swell you would get from pumping&lt;br&gt;
air. Notes decay when you stop, the way a real one does when the bellows empty.&lt;/p&gt;

&lt;p&gt;The thing that makes it usable rather than a novelty is a sustain key. Holding the&lt;br&gt;
space bar keeps notes ringing after you let go of the keyboard, which frees your&lt;br&gt;
other hand for the bellows. It is not how the physical instrument works — but a&lt;br&gt;
computer keyboard is not a physical instrument, and pretending otherwise makes the&lt;br&gt;
tool worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ragas, and why the keyboard dims
&lt;/h2&gt;

&lt;p&gt;Indian classical music is built on ragas, and a raga is not a scale — it is closer to&lt;br&gt;
a grammar. It specifies which notes belong, but also how you approach them, which&lt;br&gt;
ones you can rest on, and which direction you can move through them.&lt;/p&gt;

&lt;p&gt;A web app is not going to teach that. What it &lt;em&gt;can&lt;/em&gt; do is stop you playing notes that&lt;br&gt;
are flatly outside the raga. Pick one and the out-of-scale keys dim; the remaining&lt;br&gt;
keys are the ones that belong. For a beginner working through&lt;br&gt;
&lt;a href="https://web-harmonium.app/ragas/yaman/" rel="noopener noreferrer"&gt;Raga Yaman&lt;/a&gt;, that constraint is the single&lt;br&gt;
most useful thing the software does. Twelve ragas ship as presets, from Bilawal&lt;br&gt;
(which maps neatly onto a Western major scale) through to Marwa and Todi, which&lt;br&gt;
do not map onto anything comfortable at all.&lt;/p&gt;

&lt;p&gt;There is also a drone. Indian music is not played against silence — there is a&lt;br&gt;
tanpura or a sustained reed holding Sa and Pa underneath everything. Playing without&lt;br&gt;
one feels wrong in a way that is difficult to articulate until you switch it on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sargam, or: your users may not read your notation
&lt;/h2&gt;

&lt;p&gt;Western staff notation is not what most harmonium students use. They use&lt;br&gt;
&lt;a href="https://web-harmonium.app/sargam/" rel="noopener noreferrer"&gt;sargam&lt;/a&gt; — Sa Re Ga Ma Pa Dha Ni — which is&lt;br&gt;
movable-do and maps onto the instrument differently depending on where you place Sa.&lt;/p&gt;

&lt;p&gt;Shipping only C-D-E-F-G labels would have made the tool useless to a large part of&lt;br&gt;
the audience it is actually for. The labels are a toggle, and the drone root is&lt;br&gt;
adjustable across all twelve pitches so Sa lands wherever the singer needs it.&lt;/p&gt;

&lt;p&gt;If you are building anything for a non-Western musical tradition, treat notation as a&lt;br&gt;
localisation problem, not a display preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  MIDI, recording, and the boring wins
&lt;/h2&gt;

&lt;p&gt;Three things that took an afternoon each and were worth more than a week of polish&lt;br&gt;
elsewhere:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MIDI input.&lt;/strong&gt; &lt;code&gt;navigator.requestMIDIAccess()&lt;/code&gt; is a small API and it works. Plug in&lt;br&gt;
a keyboard, get velocity sensitivity, and the browser tab stops feeling like a toy.&lt;br&gt;
Chrome, Edge and Opera support it; Firefox and Safari do not, so it degrades to the&lt;br&gt;
on-screen keyboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recording.&lt;/strong&gt; &lt;code&gt;MediaRecorder&lt;/code&gt; pointed at a &lt;code&gt;MediaStreamDestination&lt;/code&gt; gives you a&lt;br&gt;
downloadable WebM with about fifteen lines of code. People record two-minute practice&lt;br&gt;
takes and send them to their teacher.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shareable links.&lt;/strong&gt; All the settings — raga, drone, root, notation — serialise into&lt;br&gt;
the URL fragment. A teacher can send a student a link that opens with everything&lt;br&gt;
already configured. Zero backend, and it is the feature that gets used most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install-free is the whole point
&lt;/h2&gt;

&lt;p&gt;It is a PWA, so "Add to Home Screen" gives you an offline-capable app without an app&lt;br&gt;
store, a download, or an account. For a practice tool that someone might use for ten&lt;br&gt;
minutes before a class, every one of those steps is a place to lose them.&lt;/p&gt;

&lt;p&gt;If you want to poke at it, it is free and there is nothing to sign up for:&lt;br&gt;
&lt;a href="https://web-harmonium.app/" rel="noopener noreferrer"&gt;web-harmonium.app&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with the Web Audio API and Tone.js. Samples from Yale's Euterpea Studio (CC0).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webaudio</category>
      <category>javascript</category>
      <category>music</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
