Elgato Stream Deck Plugins – Overview

The Elgato Stream Deck is a really nifty series of hardware devices. And if you’re researching how to write plugins for them, then I won’t bore you with the details of what they are, or what there’re for. I’ll just say that they’re neat, and their biggest issue with growing the market is that the name implies they’re only for streaming.

The official documentation can be found at https://docs.elgato.com/sdk/ but I found that it is within the categorization of programming where if you already know and understand everything, then it all makes sense. But if you don’t know and understand it, then it’s difficult to follow.

Plugin Architecture

Plugins run on the computer – not the stream deck.

Your code will be executed. You use the execution arguments to open a websocket, and then you communicate changes to the stream deck, and it communicates actions performed on the stream deck.

Plugins can be written in any language that supports websockets. There are a lot of different templates, and much like the famous xkcd comic, there will undoubtedly be more at the time of reading, because they all have different pros and cons.

The plugins are basically a fancy folder with specific things in specific spots, and everything else left up to chance.

The goal of writing a plugin is to get a folder with a manifest.json file inside, and then use elgato’s distribution tool bundle it into a plugin that can be installed.

Depending on the language you pick, you can debug by attaching to a running process, by writing output to a file, or with a registry key in windows, there’s some sort of javascript console you can look at.


Getting Started

The first thing to do is pick a unique id. The documentation will refer to this as your UUID. The unique id must be

  • unique
  • all lowercase
  • only uses alphanumeric characters (a-z, 0-9), hyphen (-), and/or period (.)
  • I think there’s a length limit. I couldn’t find any references to it, but one time I watched a youtube video and they ran into an issue with it being too long

Despite the documentation referring to this as a UUID it isn’t one in the traditional sense of UUID and GUID. Its more of a UUID in the literal sense. Its universally unique, and its an ID

Now that you have an id picked, your goal becomes to have a folder named <id.>sdPlugin with the manifest.json inside.

For example, a Hello World could look like

Note that the extension is sdPlugin, not sdplugin. If it is not capitalized properly, you will get Error: Invalid Input when you try to bundle



The manifest.json file is there to tell the stream deck software how to run your plugin. There are a bunch of things that have to be filled out. And you’ll need to look at the documentation, but we’ll start by pointing out some of the most important things.



CodePath, CodePathMac, CodePathWin

This is the path relative to the sdPlugin folder for where your executable code is.

It must be a single file executable. So in mac, if you have built a .app file, it will not work, because .app is a folder, not a file.

This means that if you want to use dotnet, to create plugins, you may run into trouble. A target framework of net7.0 will result in a normal executable. A target framework of net7.0-macos will let you use Foundation or AppKit (to tie into the event loop, or do other OS specific things) but will result in a .app file.

Within the .app file is probably a single executable you could target, but its honestly a lot of effort so I stopped trying.


Bear in mind that whatever language you pick, the stream deck software will just try to execute the code path. If you go with something like .net framework, it will only work on windows, but there’s no runtime for users to install.

If you use Ruby or Python, users may need to install a runtime. Perfectly fine if you’re just writing for yourself, of maybe you throw it up on a github for people to stumble on, but not submit to elgato. But if you expect random people with no knowledge to install it, you should pick something easier.


UUID (action)

Actions have a UUID. Once again, this is not a UUID in the UUID/GUID sense, but just a unique id. For this, I recommend using <pluginId>.action

once again, it has the same requirements as the app id. alphanumeric, lowercase, hyphen (-), period (.)


The UUID will be sent through the websocket when an action happens on the stream deck so that you can identify which action occurred in the event of a plugin with multiple actions.

If you’re writing something for the Stream Deck+ there will be a single action for the dial rotation, press, and screen tap commands. Not an action for each of them.

If you make an action that supports buttons and dials, it will still be one action.



  • must be in the sdPlugin folder, a missing image will stop the distribution tool from finishing
  • must include a “normal” and @2x file variant
  • I don’t think svg files work
  • If they aren’t square, they will stretch to fill. So fire up Gimp, and resize the canvas to keep things looking the way they should

There’s a bunch, and they’re all different sizes. The style guide has some pictures with labels for what the icons are.


For everything else, just go through the documentation linked above. These were just the pain points and fields that were the most confusing.



Now that you have you sdPlugin folder, which contains your completed manifest.json, all the icons, and your code, you can run the distribution tool

Technically you don’t have to use the distribution tool to build the plugin. But its a better idea to use it until you have everything squared away, and then you can iterate on your code by just copying the executable to the installed plugins directory.

Keep in mind however, that as you iterate on your code, you will need to stop the stream deck app before you copy the new executable out. And then you can relaunch it.


Depending on the language you pick, you can debug by attaching to a running process, by writing output to a file, or with a registry key in windows, there’s some sort of javascript console you can look at.

Using .net framework, you can put in a loop to check if the debugger is attached in main, and then attach to a running process.

If you’re using Ruby, you could just write to a log file, and then tail the file

Either way, there’s no need to fear whether you will be able to inspect your plugin.

Leave a Comment

Your email address will not be published. Required fields are marked *