Work mirror of merith-tk/riverdeck (GitHub) for the addon registry + data-directory layout rework. Upstream stays on GitHub; Zach syncs manually after testing.
  • Go 80.9%
  • Lua 8.3%
  • JavaScript 7.5%
  • CSS 2.2%
  • HTML 1%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Merith-TK b974570244
Some checks failed
Build / Build (ubuntu-latest) (push) Failing after 18s
update build steps, remove unessecary lines in claude settings
2026-06-27 18:07:32 -07:00
.claude update build steps, remove unessecary lines in claude settings 2026-06-27 18:07:32 -07:00
.devcontainer begone wails 2026-06-27 17:55:45 -07:00
.github/workflows update build steps, remove unessecary lines in claude settings 2026-06-27 18:07:32 -07:00
.opencode/agents residual-agent for AI 2026-06-26 18:18:33 -07:00
.riverdeck update build steps, remove unessecary lines in claude settings 2026-06-27 18:07:32 -07:00
cmd begone wails 2026-06-27 17:55:45 -07:00
docs begone wails 2026-06-27 17:55:45 -07:00
mcp/riverdeck-wsdevice start work on multidevice 2026-03-16 11:01:07 -07:00
pkg feat: serve layout editor from main CLI via HTTP 2026-06-27 21:04:41 +01:00
resources feat: unified icon system with pkg://name#icon URI and layered compositing 2026-05-25 20:04:23 -07:00
.gitignore convert simulator to WS client, fix concurrent write bug, implement script execution for WS devices 2026-03-16 17:14:40 -07:00
.golangci.yml ci, test, refactor: linting, tests, and shared packages for cmd/ cleanup 2026-05-25 17:51:08 -07:00
.mcp.json ci, test, refactor: linting, tests, and shared packages for cmd/ cleanup 2026-05-25 17:51:08 -07:00
.pre-commit-config.yaml ci, test, refactor: linting, tests, and shared packages for cmd/ cleanup 2026-05-25 17:51:08 -07:00
CLAUDE.md reorganize wsdevice and mcp-wsdevice to match project file conventions 2026-03-16 15:30:05 -07:00
go.mod begone wails 2026-06-27 17:55:45 -07:00
go.sum begone wails 2026-06-27 17:55:45 -07:00
magefile.go begone wails 2026-06-27 17:55:45 -07:00
opencode.json feat: WS server works in all navigation modes; MCP rd_get_frame tool; opencode.json 2026-05-25 20:59:21 -07:00
README.md begone wails 2026-06-27 17:55:45 -07:00
residual-overview.md residual overview 2026-06-26 20:42:44 -07:00

Riverdeck

A programmable interface for Elgato Stream Deck devices. Write Lua scripts to control button actions, appearances, and background tasks. Supports folder-based navigation and a declarative layout system.

Features

  • Device Auto-Detection -- Automatically detects and configures connected Stream Deck devices
  • Lua Scripting -- Program button actions with Lua scripts for maximum flexibility
  • Folder Navigation -- Organize scripts in a hierarchical folder structure
  • Real-time Updates -- Scripts can dynamically update button appearances
  • Passive Loops -- Background script execution for status indicators and animations
  • GIF Support -- Animated GIF rendering on individual buttons
  • Package System -- Modular script packages with templates, shared libraries, and daemon scripts
  • Settings Overlay -- In-device settings menu accessible from any page
  • Display Sleep -- Configurable timeout with activity-based wake
  • WebSocket Devices -- Connect virtual or custom hardware devices over the network
  • Layout Mode (experimental) -- Declarative JSON-based page navigation as an alternative to folder browsing

Requirements

  • Go 1.24+
  • CGO enabled (required for HID library)
  • A connected Elgato Stream Deck device (or a WebSocket device client)

Usage

Before the first build, run mage build to generate embedded icon resources. The build will fail without this step.

go install github.com/magefile/mage@latest
mage build
go run ./cmd/riverdeck

Or build and run:

go build ./cmd/riverdeck
./riverdeck

Configuration

The config directory location depends on platform:

Platform Path
Linux / macOS ~/.config/riverdeck/
Windows %APPDATA%\.riverdeck\

You can override the config directory by passing --config <path> on the command line.

Directory Layout

~/.config/riverdeck/
├── .config.yml              # Application configuration
├── layout.json              # Declarative layout (layout mode)
├── _boot.lua                # Optional boot animation script
├── .config/
│   ├── packages/            # Installed script packages
│   └── devices/             # Per-device geometry snapshots
├── apps/                    # Application launch scripts
├── media/                   # Media control scripts
└── system/                  # System control scripts

.config.yml

application:
  brightness: 75        # Display brightness (0-100)
  passive_fps: 30       # Background script update rate (frames per second)
  timeout: 30           # Display sleep timeout in seconds (0 = never)
  navigation_style: auto  # "folder", "layout", or "auto"

network:
  websocket_enabled: false
  websocket_port: 9000

logging:
  level: info

Setting navigation_style: auto (the default) uses layout mode when layout.json is present and falls back to folder navigation otherwise.

See docs/config-reference.md for the full schema.

Navigation Modes

Folder Navigation

The default mode. Scripts and directories inside the config directory are mapped directly to buttons. Subdirectories become nested pages with automatic pagination. A reserved column provides Back, Home, and Settings buttons.

Layout Mode (experimental)

Layout mode is experimental and not finished. Expect missing features, bugs, and breaking changes.

Layout mode uses layout.json to declaratively define pages and button positions. Buttons are placed at explicit grid slots rather than derived from the filesystem.

Enable it explicitly:

application:
  navigation_style: layout

layout.json format (v2):

{
  "layouts": {
    "default": {
      "pages": {
        "main": {
          "buttons": {
            "0": { "label": "Home", "script": "home.lua" },
            "1": { "label": "Media", "script": "media.lua" }
          }
        }
      }
    }
  },
  "devices": {
    "DEVICE-SERIAL-ID": "default"
  }
}

The "devices" map assigns each physical device to a named layout. Devices not in the map use the "default" layout if it exists.

WebSocket Devices (experimental)

Riverdeck can accept virtual devices over a WebSocket connection. These devices declare their grid size and input layout on connect and receive image/label updates in return. This allows anything that can open a WebSocket to act as a Riverdeck device -- a web app, mobile app, Raspberry Pi Zero W with GPIO buttons, or a bridge to other hardware.

Enable the WebSocket server:

network:
  websocket_enabled: true
  websocket_port: 9000

WebSocket devices only support layout mode. See cmd/wsdevice/README.md for the protocol spec.

Editor (experimental)

The editor is experimental and largely unfinished. Large portions do not work.

Layout mode is edited via a web UI served by an embedded HTTP server (pkg/editorserver), which serves the editor frontend and exposes endpoints for reading and writing layout.json and script files. When network.editor_enabled is set, use the tray menu's "Open Editor" item (or visit the configured editor URL directly).

Supported Models

Model PID Cols Rows Keys
Stream Deck Original 0x0060 5 3 15
Stream Deck Mini 0x0063 3 2 6
Stream Deck XL 0x006c 8 4 32
Stream Deck Original V2 0x006d 5 3 15
Stream Deck MK.2 0x0080 5 3 15
Stream Deck XL V2 0x0084 8 4 32
Stream Deck Pedal 0x0086 3 1 3
Stream Deck Neo 0x0090 4 2 8
Stream Deck + 0x009a 4 2 8

See docs/hardware-implementation-plan.md for notes on hardware support status and models that require additional investigation.

Scripting

Scripts are Lua files placed in the config directory. See docs/lua/README.md for the full scripting reference including all available modules.

Quick example:

-- Button that shows current time and opens a URL on press
local time = require("time")
local shell = require("shell")

return {
    label = function()
        local d = time.date()
        return string.format("%02d:%02d", d.hour, d.minute)
    end,
    trigger = function()
        shell.open("https://example.com")
    end
}

Architecture

Commands (cmd/)

Command Description
riverdeck Main application -- device communication, scripting, systray, and editor server
riverdeck-debug-prober Device inspection and HID feature report tool (GUI + CLI)
riverdeck-simulator Software simulator for testing without hardware
mcp-wsdevice MCP server for AI-driven testing via WebSocket
wsdevice/ Reference client implementations (planned, not yet implemented)

Packages (pkg/)

Package Description
pkg/streamdeck/ Low-level HID device communication, navigation, key handling, device models
pkg/scripting/ Lua script execution, lifecycle management, ScriptManager
pkg/scripting/modules/ Built-in Lua modules: config, file, http, shell, store, pkg_data, streamdeck, system
pkg/lualib/ Lua stdlib extensions: json, base64, crypto, regex, strings, time, log, path, mathx
pkg/layout/ Layout v2 model types and layout.json load/save logic
pkg/editorserver/ HTTP API server for the layout editor
pkg/imaging/ Image loading, resizing, and GIF frame extraction
pkg/platform/ Config directory resolution (cross-platform paths)
pkg/prober/ Device enumeration and HID feature report probing
pkg/resolver/ pkg:// URI resolver for package-relative resources
pkg/appearance/ Layered key rendering (color → icon → text)
pkg/gitpkg/ Git backend for package installation (native git exec)
pkg/pkgmanager/ Package install/remove/update/list with lock file
pkg/wsdevice/ WebSocket virtual device implementing the device interface
pkg/wsclient/ Shared client-side types for building WebSocket hello messages
pkg/util/ Internal utility helpers

Dependencies

  • github.com/sstallion/go-hid -- HID device communication
  • github.com/yuin/gopher-lua -- Lua script execution
  • golang.org/x/image -- Image processing
  • github.com/getlantern/systray -- System tray integration
  • github.com/gorilla/websocket -- WebSocket server for virtual devices
  • fyne.io/fyne/v2 -- GUI toolkit for riverdeck-debug-prober