A MIDI keyboard and OBS as a hardware controller (Hammerspoon)
I have several MIDI keyboards, so a simple idea came up: use one of them as a physical control panel where keys launch commands. Hammerspoon works here as “firmware”: it receives MIDI events, translates them into actions and controls OBS over WebSocket.
Example:
The config has an action map smkActions[…], and note 48 is assigned:
smkActions[48].label = "OBS: Scene #01"
smkActions[48].fn = function() obsSetSceneByIndex(1) end
What this means:
-
smkActions[48]: the slot for a specific MIDI note (48).
-
.label: a human-readable name (for logs and understanding).
-
.fn = function() … end: the function performed when this MIDI key is pressed.
-
obsSetSceneByIndex(1): switch OBS to the scene with index 1 (the index comes from the OBS scene list).
The MIDI event handler then has this logic: perform actions only in command mode (which is turned on by the sustain pedal/CC64).
The key fragment (the essence):
if smkCommandMode and cmdType == "noteOn" then
local note = mdTable.note
local vel = mdTable.velocity or 0
if not note or vel <= 0 then return end
if note < SMK_NOTE_MIN or note > SMK_NOTE_MAX then return end
local act, layer = smkResolveAction(note)
if not act then return end
if not act.hotkey and not act.fn then return end
-- then debounce and run act.fn / act.hotkey
end
-
smkCommandMode: a boolean flag “command mode on”. -
cmdType == "noteOn": react to a key press (Note On). -
mdTable.note: the note number (for example, 48). -
mdTable.velocity: the force of the press; vel <= 0 is cut off (so as not to trigger “empty” events). -
SMK_NOTE_MIN/MAX: a range limit, to process only the 25 keys needed. -
smkResolveAction(note): gets the action for a specific note (taking possible layers and overrides into account). -
act.fn: if the note has a function, it will be called.
Then obsSetSceneByIndex(1) does roughly this chain:
-
Gets the list of OBS scenes (through a cache with a small TTL, so as not to poke OBS on every click).
-
Takes the scene name by index 1.
-
Sends OBS the SetCurrentProgramScene command over WebSocket.
The final logic of one key
MIDI note 48 → (command mode ON) → find smkActions[48] → call obsSetSceneByIndex(1) → OBS switches the scene to #1.