The Implementation

We’ll skip the part of creating a fancy GUI, as this would draw us away from the core challenge, have a look at the source code of WebApiStudio.com if you want to see how I implemented things in VueJs.

Requirement 1: Control volume of one deck with a slider

We start off with creating a simple slider on an HTML page. We set the min value to 0 and max value to 127 . Remember that our “data” bytes have a range of 0 to 127. Therefore we want our volume slider to limit its range for convenience.

We are now just listening to the input event of the slider. Making sure that when we slide the slider from left to right that our min is 0 and max 127 .

Now we want to send a MIDI Message to an actual MIDI “output”. The “output” we use to send MIDI Messages to, the “Input” we use to receive MIDI messages from. You can have always multiple input and outputs available.

If you don’t have Traktor installed, I suggest you install a MIDI Monitor which can act as a “fake” output to listen to received MIDI messages, ideal for debugging purposes. If you use the MIDI Monitor, make sure that you enable “Act as destination for other programs” once you launched it.

If you are using Traktor, we need to first do some prep work.

Start Traktor

Open settings > Controller Manager > Select Generic MIDI as device.

> > Select as device. If you don’t have Generic MIDI available, click the add button and choose Generic MIDI .

available, click the button and choose . Select for in-port the value traktor virtual input

the value Click add in > mixer > volume adjust .

> > . In the device mapping , click on the n/a dropdown and select ch01 — CC — 006 .

, click on the dropdown and select . In the mapping details choose deck a for the assignment

choose for the You can see all the steps in this video.

Now that Traktor is ready to listen to our MIDI mapping, we can tie things together.

When we now move the slider around, we will send a midi message of 3 bytes ([176, 6, currentSliderValue]) to our Traktor output (which annoyingly has the name “input” in it). We could optimize the code so we only fetch the outputs once, but these are improvements that I don’t want to focus on for now.

If we would run this page now, with Traktor (or MIDI Monitor) running in the background with the proper configuration we should get the following result:

As you can see, when I move the slider in the web page, the volume slider of my deck a changes accordingly. Sadly, when I load the page, with this code snippet I cannot query the “current” state of the volume slider in Traktor. Fixing this is out of our scope.

Requirement 2: Add Play/Resume button

We will add now a simple button that will pause or resume our loaded track in deck a . Again, if you are using Traktor, we need to configure a new MIDI mapping in our Traktor controller manager.

Open settings > Controller Manager > Select Generic MIDI as device.

> > Select as device. Click add in > deck common > play/pause .

> > . In the device mapping , click on the n/a dropdown and select ch01 — CC — 007 . Also, set the interaction mode to toggle . This way we toggle between play and pause basically.

, click on the dropdown and select . Also, set the interaction mode to . This way we toggle between and basically. In the mapping details choose deck a for the assignment

choose for the You can see all the steps in this video.

I have refactored the code a bit to avoid duplication, but basically, I just added a button that will send the correct MIDI Message that maps to our play/pause action.

We can see it in action in the following video:

Seems to be working just fine! The volume slider still works and also the play button does its job.

We were able to make a working example that we can control Traktor from the browser with the help of custom MIDI mapping. It is possible to also make it work in the other direction. When I would change the volume in Traktor, we would auto adjust the slider in the web browser. We can accomplish this with a out mapping instead of an in mapping that we configured in Traktor and both can work side by side. We won’t cover that in detail, but worth mentioning.

Requirement 3: Control the volume with a gamepad.

As we now understand how we can control Traktor via MIDI, we can now use the gamepad API to use our PS4 DualShock. The behavior will be a bit buggy because of the nature of how the Gamepad API works. We need to “poll” the state of the gamepad periodically to check what buttons are being pressed.

Meaning, we might register a button click multiple times or not at all depending on how long we keep a button pressed and based on our “polling” interval. We can avoid this behavior by using RxJs, but I don’t want too much code for our proof-of-concept so I’ll just accept the potential awkward behavior.

We need to connect our PS4 DualShock with a USB cable to our local machine. No special software is required, if you use a mac, check out this tutorial, else Google is your friend. Although they give advice on how to connect it over Bluetooth, use the USB cable approach.

For this example, I’ll use the navigation buttons of our PS4 DualShock. The up button has id/index 12 and the down button has id/index 13 . A full mapping can be found here.

Here is the code:

I removed the HTML controls/buttons for clarity of the example. At line 12 we start our polling interval which will call getButtonState . This function will get the current state of our up and down button and will adjust the volume accordingly. Notice that we do have to keep track of the current volume level so we know what value we need to send (as the volume is expressed with a value between 0 and 127 ).

Here is a small video showing our code in action:

Works like a charm! Now let’s move to the final stage.

Requirement 4: Control Play/Pause with a gamepad.

For the convenience of me being able to film when I showcase the working example, I will use the navigation button right (id/index 15 ) to function as the play/pause button. We just need to extend the getButtonState function to also register any button presses on it and sent out the corresponding MIDI message.

If you have a look at the code, just minor changes were done. On line 24 we also get the navigation button right and on line 39 we send the corresponding MIDI message. Now we can see this all in action in the final video:

You might notice that the “play/pause” sometimes “stutters”, so it pauses and resumes immediately again. This is an example of what was mentioned before, this is because of the nature of the Gamepad API that we “poll” for the status of the buttons and my interval being too fast, so we register a button press twice in a row.