PHPnews.io

WebHID Demo: Elgato Stream Deck Daft Punk Soundboard

Written by Bram.us / Original link on Feb. 12, 2021

Sparked by Pete LePage’s work on talking to a Elgato Stream Deck device from within the browser, I wanted to play with WebHID myself. First thing that came to my mind was to create a DrumPad.

What first started out as a simple/classic DrumPad …

🔥 Have been playing with the #WebHID API tonight, connecting my @elgato Stream Deck to @googlechrome.

Here’s a little drum pad that respons to clicks, keyboard key presses, and the Stream Deck buttons.

🔗 https://t.co/Nkfoh39dr3 (Chrome Only) pic.twitter.com/kn2IsP0jrx

— Bramus! (@bramus) February 10, 2021

… soon led to creating a Soundboard which uses samples from Daft Punk’s “Harder, Better, Faster, Stronger”.

Et8Rna4XIAIT8en.png
Screenshot of the Soundboard I built.

Before linking to the final version of the Daft Punk Soundboard (which has evolved quite a bit when compared to the screenshot above), let’s take a look at how it works.

~

Building the Drumpad/Soundboard and responding to clicks

All audio samples are defined as a small object on an array, and consist of three properties:

  1. A label
  2. A link to an audio fragment
  3. A Keyboard keyCode to respond to

ℹ️ In the final version I added some extra features such as the ability to use an image instead of a label and to customize the action when the button is being pressed, but these are not the focus here.

Each fragment is rendered as a <button> element and a (non-visible) <audio> element. The <audio> element its id is set to the keyCode.

<button data-keycode="${keyCode}">
    <span>${label}</span>
    <audio id="${keyCode}" src=${url} preload="auto"></audio>
</button>

Upon pressing a button, its linked <audio> element (fetched using the button’s data-keycode attribute value, instead of relying on DOM traversal) is selected and a play action is triggered on the fragment.

const playSound = (keyCode) => {
	const $el = document.getElementById(keyCode);
	
	if (!$el) return;

	$el.currentTime = 0;
	$el.play();
}

~

Responding to Keyboard Key Presses

To capture key presses a listener on the keydown event of the document is added. Using the pressed key’s code the correct button is selected and a click on it is triggered.

document.addEventListener('keydown', (e) => {
	const $button = document.querySelector(&grave;button[data-keycode="${e.code}"]&grave;);
	if ($button) $button.click();
});

~

Attaching the Stream Deck

☝️ Do note that connecting a Stream Deck is entirely optional: using a Stream Deck is considered to be an enhancement.

The Stream Deck code itself was borrowed from Pete’s Google Meet Stream Deck Chrome Plug-in, and launched using similar logic. If a Stream Deck device is found and connected, it is attached to the DrumPad instance.

const go = async () => {
	const drumPad = new DrumPad(config, document.querySelector("#app"));
	await drumPad.init();

	if (navigator.hid) {
		const streamDeck = new StreamDeck();

		// Connect to previously connected device
		await streamDeck.connect();

		// A previously connected device was found
		if (streamDeck.isConnected) {
			drumPad.attachStreamDeck(streamDeck);
		}

		// No Previously connected device was found
		else {
			// Add button to connect new device
			const elem = document.createElement("button");
			elem.innerText = "Connect Stream Deck";
			elem.addEventListener("click", async () => {
				elem.remove();
				await streamDeck.connect(true);
				drumPad.attachStreamDeck(streamDeck);
			});
			document.body.appendChild(elem);
		}
	}
}
go();

💡 As not all browsers support top-level await, we wrap the whole logic in a async function

~

Responding to Stream Deck button presses

To also respond to button presses on the Stream Deck, a map that maps a Stream Deck button ID (0, 1, 2, …) to a certain keyCode is built.

const buttonIdToKeyCodeMap = {
  0: "KeyQ",
  1: "KeyW",
  2: "KeyE",
  …
}

Upon pressing a Stream Deck button it will — using the buttonIdToKeyCodeMap — fetch the corresponding HTML button and trigger a click on it, similar to how the keyboard key presses work.

This is set up in the call to drumPad.attachStreamDeck(streamDeck); (see above) and looks like this:

streamDeck.addEventListener('keydown', (e) => {
	const keyCode = buttonIdToKeyCodeMap[e.detail.buttonId] ?? '';
	const $button = document.querySelector(&grave;button[data-keycode="${keyCode}"]&grave;);
	if ($button) $button.click();
});

In that same attachStreamDeck method the buttons on the Stream Deck are also drawn.

~

Stretching it a bit more …

The switch to the Daft Punk board didn’t sit 100% well me with me though: there are 16 samples to use, but the Stream Deck “only” has 15 buttons available …

But then it hit me: what if I paginated the samples, and allowed you to switch between two sets of 8 samples each? In that idea the 1st row would be filled with buttons to switch between different sample sets, and the 2nd+3rd row would respond to that.

With that refactor being worked on, I also took the time to update the UI to closely reflect the layout of the Stream Deck device.

In the end, it ended up like this:

webhid-elgato-stream-deck-daft-punk-soundboard.png

Here’s a video of it, so see how it works and behaves, including with a connected Stream Deck:

~

During lunch today I polished the code a bit further and pushed everything online. The Source Code can be found in GitHub, and the app is deployed on Netlify.

Elgato Stream Deck Daft Punk Soundboard Demo →
Elgato Stream Deck Daft Punk Soundboard Source Code →

👨‍🔬 The demo website is registered for the WebHID Origin Trial, and therefore WebHID should be enabled by default. If you however don’t see a connect button, go to chrome://flags/ and manually enable ”Experimental Web Platform Features”.

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

bram bram bram bram bram

« “This installation collapses together images and objects referencing American colonialist... - Use the Elgato Stream Deck in Google Meet with this Chrome Plugin »