<?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>Forem: Phil Gemellas</title>
    <description>The latest articles on Forem by Phil Gemellas (@manontherun).</description>
    <link>https://forem.com/manontherun</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F98765%2Fcdcdc6de-f30b-49b9-a41a-0865db02ef88.jpeg</url>
      <title>Forem: Phil Gemellas</title>
      <link>https://forem.com/manontherun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/manontherun"/>
    <language>en</language>
    <item>
      <title>GhostFader app - Easily send MIDI CC (Continuous Controller) Data to your DAW or any connected MIDI Device from the browser.</title>
      <dc:creator>Phil Gemellas</dc:creator>
      <pubDate>Wed, 29 Apr 2026 06:26:32 +0000</pubDate>
      <link>https://forem.com/manontherun/ghostfader-app-2gn1</link>
      <guid>https://forem.com/manontherun/ghostfader-app-2gn1</guid>
      <description>&lt;p&gt;I could not afford buying one more hardware controller with faders to just send &lt;strong&gt;MIDI CC **(Continuous Controller) Data to my DAW (Logic Pro) such as **Modulation&lt;/strong&gt; (CC 1) or &lt;strong&gt;Expression&lt;/strong&gt; (CC 11), so i built a virtual one in the browser with the use of &lt;strong&gt;WebMIDI API&lt;/strong&gt;.&lt;br&gt;
It comes convenient because my keyboard controller sits a little bit far for me to reach.&lt;br&gt;
To get the most of it, it would better if you have already pre-recorded the MIDI part (the MIDI Notes) and then, on top of it you overdub the movements of the virtual fader on your screen.&lt;br&gt;
Try it Live here: &lt;a href="https://ghostfader.netlify.app/" rel="noopener noreferrer"&gt;https://ghostfader.netlify.app/&lt;/a&gt;&lt;br&gt;
The repo is here: &lt;a href="https://github.com/Man-on-the-Run/GhostFader-app" rel="noopener noreferrer"&gt;https://github.com/Man-on-the-Run/GhostFader-app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webmidi</category>
      <category>util</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build the most basic DJ App in Vanilla JS</title>
      <dc:creator>Phil Gemellas</dc:creator>
      <pubDate>Wed, 28 Dec 2022 04:50:50 +0000</pubDate>
      <link>https://forem.com/manontherun/build-the-most-basic-dj-app-in-vanilla-js-4kdn</link>
      <guid>https://forem.com/manontherun/build-the-most-basic-dj-app-in-vanilla-js-4kdn</guid>
      <description>&lt;p&gt;&lt;a href="https://www.freepik.com/free-photo/closeup-dj-working-blue-light_10758954.htm#query=dj&amp;amp;position=0&amp;amp;from_view=keyword" rel="noopener noreferrer"&gt;Cover Image by wirestock&lt;/a&gt; on Freepik.&lt;/p&gt;

&lt;p&gt;In a typical DJ setup there are two decks and a mixer.&lt;/p&gt;

&lt;p&gt;On the mixer there is a horizontal fader called &lt;strong&gt;Crossfader&lt;/strong&gt; which is used to blend the volume of the two decks (crossfade) so the DJ can mix the tracks.&lt;/p&gt;

&lt;p&gt;The way it works is that when the fader is in the middle, both decks are on the same level, moving it to the &lt;em&gt;right&lt;/em&gt; it will &lt;em&gt;attenuate&lt;/em&gt; the volume of the &lt;em&gt;left&lt;/em&gt; deck, moving it to the &lt;em&gt;left&lt;/em&gt; will &lt;em&gt;attenuate&lt;/em&gt; the volume of the &lt;em&gt;right&lt;/em&gt; deck.&lt;/p&gt;

&lt;p&gt;To simulate that functionality we will use two HTML audio elements (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement" rel="noopener noreferrer"&gt;HTMLAudioElement on MDN&lt;/a&gt;)&lt;br&gt;
that correspond to the two decks,&lt;br&gt;
and for the crossfader we will use an input of type range &lt;br&gt;
(&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range" rel="noopener noreferrer"&gt;input type range on MDN&lt;/a&gt;)&lt;br&gt;
which looks and behaves like a fader and of which we will adapt its functionality in JavaScript to make it work like a crossfader.&lt;/p&gt;
&lt;h3&gt;
  
  
  The HTML
&lt;/h3&gt;

&lt;p&gt;In our HTML, after the boilerplate code, we create two audio elements with the id's of "deck1" and "deck2" which will be the left and right deck respectively.&lt;/p&gt;

&lt;p&gt;We also need to specify the &lt;em&gt;controls&lt;/em&gt; attribute in order to display the graphical user interface (play - pause buttons etc.) of the audio element.&lt;/p&gt;



&lt;p&gt;In the same folder of the project add at least two audio files, so you can have something to listen to for testing as we go.&lt;/p&gt;

&lt;p&gt;Assign the &lt;em&gt;src&lt;/em&gt; attribute of each of the audio players to the path to one of your audio files.&lt;/p&gt;

&lt;p&gt;For demo purposes i will refer to the imaginary filenames 'track1' and 'track2' as the names of the audio files.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fi6xo4m0k0xydzv3d5evr.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.amazonaws.com%2Fuploads%2Farticles%2Fi6xo4m0k0xydzv3d5evr.png" alt="Image description" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Important
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Keep in mind that the maximum value that we can set the volume of an audio element is 1.&lt;br&gt;
This comes from the specification and we will need this information for later use.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Then we create an input with an id of &lt;em&gt;crossfader&lt;/em&gt;  and of type &lt;em&gt;range&lt;/em&gt; that resembles our crossfader.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="range" id="crossfader"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A range input accepts four basic attributes, namely &lt;em&gt;min, max, step&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Although these attribute names explain themselves let me be clear ... &lt;br&gt;
the &lt;em&gt;min&lt;/em&gt; attribute sets the minimum value of the range of the input,&lt;br&gt;
the &lt;em&gt;max&lt;/em&gt; the maximum value of the range,&lt;br&gt;
the &lt;em&gt;step&lt;/em&gt; sets the amount of increment / decrement for each step of the fader,&lt;br&gt;
and the &lt;em&gt;value&lt;/em&gt; attribute sets the default value for it.&lt;/p&gt;

&lt;p&gt;We will set the minimum to 0, &lt;br&gt;
the maximum to 1 (which is the maximum allowed value for the volume of the audio players),&lt;br&gt;
the step to 0.01 so we can have a greater resolution on each step of the fader (and avoid jumps on each step)&lt;br&gt;
and the value attribute to .5, which means equal volume for both audio players.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="range" id="crossfader" min="0" max="1" step="0.01" value=".5"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, add a &lt;em&gt;script&lt;/em&gt; tag that links to your javascript file.&lt;br&gt;
I will give it the name of &lt;em&gt;main.js&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="main.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&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.amazonaws.com%2Fuploads%2Farticles%2Fymd5l7vm07312q867nsg.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.amazonaws.com%2Fuploads%2Farticles%2Fymd5l7vm07312q867nsg.png" alt="Image description" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The JavaScript
&lt;/h3&gt;

&lt;p&gt;In the javascript file let 's first access all the elements that we need, that is, the two audio elements and the range input.&lt;/p&gt;

&lt;p&gt;Then, we immediately initialize the volume property of each audio player to the current value of the crossfader(.5), so that both players will have the same volume.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fsuicnrg8umzh3jaft9vg.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.amazonaws.com%2Fuploads%2Farticles%2Fsuicnrg8umzh3jaft9vg.png" alt="Image description" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we need to assign an &lt;em&gt;event listener&lt;/em&gt; to our crossfader element.&lt;/p&gt;

&lt;p&gt;It will 'listen' for the &lt;em&gt;input&lt;/em&gt; event which is fired every time we move the crossfader by the value of its &lt;em&gt;step&lt;/em&gt; attribute to the left or to the right.&lt;/p&gt;

&lt;p&gt;All the magic happens inside the body of the callback function that we will provide.&lt;br&gt;
Consider the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;crossfader.addEventListener('input', function() {
    deck1.volume = this.max - this.value; // This line is actually turning the range input into a crossfader //
    deck2.volume = this.value;
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(in context, the keyword &lt;em&gt;this&lt;/em&gt;, refers to the &lt;em&gt;crossfader&lt;/em&gt; itself).&lt;/p&gt;




&lt;p&gt;So, in the first line of our callback function we set the volume of deck1 to &lt;br&gt;
&lt;em&gt;crossfader.max&lt;/em&gt; (which is 1, which is the maximum allowed volume for the audio element) &lt;br&gt;
&lt;em&gt;minus&lt;/em&gt; the &lt;br&gt;
&lt;em&gt;crossfader.value&lt;/em&gt;  which is determined by the crossfader head's current position.&lt;/p&gt;

&lt;p&gt;That means that when the fader is on the right end,&lt;br&gt;
its value is at its maximum &lt;br&gt;
which is 1 (set by its &lt;em&gt;max&lt;/em&gt; attribute in the HTML code),&lt;br&gt;
so the volume of the &lt;em&gt;deck1&lt;/em&gt; will be:&lt;br&gt;
this.max - this.value = 1 - 1 = 0,&lt;br&gt;
in other words moving the fader to the right &lt;strong&gt;&lt;em&gt;decreases&lt;/em&gt;&lt;/strong&gt; the volume of the &lt;em&gt;deck1&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;At the same time it behaves normally for the &lt;em&gt;deck2&lt;/em&gt; (second line of the callback function).&lt;br&gt;
It sets the volume of &lt;em&gt;deck2&lt;/em&gt; to its current value, so moving it to the right &lt;strong&gt;&lt;em&gt;increases&lt;/em&gt;&lt;/strong&gt; its volume.&lt;/p&gt;

&lt;p&gt;We're almost there, so far the javascript code should be working and looking like this:&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F5o21u8w61ym3q5by1m23.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.amazonaws.com%2Fuploads%2Farticles%2F5o21u8w61ym3q5by1m23.png" alt="Image description" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now press the play button on both audio players and mix the tracks with the crossfader.&lt;/p&gt;

&lt;p&gt;I hope that you enjoyed it thus far.&lt;br&gt;
If that is the case i will continue next time by adding a playlist of tracks that we will be able to load dynamically to our audio players in javascript and also add some basic CSS styling.&lt;br&gt;
Thank you.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ai</category>
      <category>privacy</category>
      <category>security</category>
    </item>
  </channel>
</rss>
