WebHID Demo: Elgato Stream Deck Daft Punk Soundboard

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 …

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

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}">
    <audio id="${keyCode}" src=${url} preload="auto"></audio>

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;


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(`button[data-keycode="${e.code}"]`);
	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) {

		// 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 () => {
				await streamDeck.connect(true);

💡 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(`button[data-keycode="${keyCode}"]`);
	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:

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.

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Leave a comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.