# SteelSeries Apex 7 × Ableton Live — Scale-Aware Key Lighting > Handoff brief for a Claude Code agent. Translated and structured from a > planning conversation. Goal: build the tooling to highlight the notes of the > currently selected Ableton scale on the physical keyboard's RGB backlight. ## Project goal Use the per-key RGB backlight of a **SteelSeries Apex 7** to highlight, in real time, the keys that correspond to the notes of the scale currently selected in **Ableton Live 12**. The physical keyboard is fitted with transparent keycaps so the light reads clearly as note highlighting. The full desired flow: ``` Live 12 (Live API: root_note + scale_intervals) → Max for Live device (computes the pitch classes in the scale) → Node for Max (POST JSON over HTTP) → GameSense server (127.0.0.1) → Apex 7 per-key RGB ``` ## Hardware notes - **Keyboard:** SteelSeries Apex 7 — per-key RGB, MX-compatible (cross-stem) switches, so standard MX keycaps fit. - **Keycaps:** transparent / "pudding" MX keycaps for light bleed. (Already swapped; not part of the software build.) - Requires **SteelSeries GG** running in the background for GameSense to work. ## Key ↔ note mapping (Ableton computer MIDI keyboard) When Ableton's computer MIDI keyboard is enabled, one octave maps like a piano. White keys on the bottom row, black keys on the row above: | Note | Key | Note | Key | |------|-----|------|-----| | C | A | F# | T | | C# | W | G | G | | D | S | G# | Y | | D# | E | A | H | | E | D | A# | U | | F | F | B | J | | | | C | K | `Z` / `X` = octave down/up, `C` / `V` = velocity down/up. Pitch-class → key lookup (index 0–11): ``` 0 C -> A 1 C# -> W 2 D -> S 3 D# -> E 4 E -> D 5 F -> F 6 F# -> T 7 G -> G 8 G# -> Y 9 A -> H 10 A# -> U 11 B -> J (12 C -> K, the octave key) ``` ## Reading the scale from Live 12 Live 12 exposes the global/selected scale on the `Song` object in the Live Object Model (LOM), accessible from Max for Live: - `root_note` — root, integer `0–11` (0 = C … 11 = B) - `scale_intervals` — list of ints, e.g. major = `[0, 2, 4, 5, 7, 9, 11]` - `scale_name` — display name string - `scale_mode` — whether scale mode is on Compute the lit pitch classes: ``` lit = { (root_note + interval) % 12 for interval in scale_intervals } ``` Then map each pitch class to its QWERTY key via the table above. **Caveat:** in Live 12 the scale is set **per clip**. Decide whether to observe the selected clip's scale or fix a global scale (existing "Scale Awareness" M4L packs can help enforce a global scale). Observe the relevant LOM property so the lighting updates when the scale changes. ## Controlling the Apex 7 (SteelSeries GameSense SDK) GameSense runs a **local REST server**. Flow: 1. Read the server port from `coreProps.json`: - Windows: `%PROGRAMDATA%\SteelSeries\SteelSeries Engine 3\coreProps.json` - macOS: `/Library/Application Support/SteelSeries Engine 3/coreProps.json` - The file gives an address like `127.0.0.1:`. 2. Register a game + event + handler, then POST event data to `http://127.0.0.1:/game_event` (register via `/game_metadata` and `/bind_game_event`). 3. Per-key control options: - **`custom-zone-keys`** — address specific keys by **USB HID usage code**, with `context-color` to set their color at runtime. - **`bitmap` mode** — paint the whole keyboard at once (a 132-length array of `[R,G,B]`, interpreted as a 22×6 grid mapped to nearest keys). Best for "dim background + bright scale keys" because it sets every key each update. **HID usage codes** for the relevant letter keys (USB HID keyboard page): ``` A 0x04 S 0x16 D 0x07 F 0x09 G 0x0A H 0x0B J 0x0D K 0x0E W 0x1A E 0x08 T 0x17 Y 0x1C U 0x18 ``` **Caveat:** as soon as a GameSense event arrives, the keyboard enters "GameSense mode" and other keys go dark unless you set them. Set a base color for all keys (bitmap mode handles this cleanly), then override the scale keys. ## The bridge: Max for Live → GameSense Cleanest option is **Node for Max** running inside the M4L device: it can read `coreProps.json` and issue the HTTP POSTs directly — no separate app needed. Alternative: Max sends OSC/UDP to a small external Python/Node script that talks to GameSense. ## Suggested build order 1. Standalone script (Node or Python) that talks to GameSense: register a game, light a hardcoded set of keys by HID code, set a dim background. Verify on hardware. 2. M4L device that reads `root_note` / `scale_intervals` from the LOM and prints the computed key set. Verify against the mapping table. 3. Wire the two together (Node for Max inside the device), observe the scale property so lighting updates live. 4. Polish: base color, highlight color, octave key, handle scale-mode off. ## Open decisions for the user - Platform: **Windows or macOS** (changes the `coreProps.json` path). - Lighting style: **bitmap** (background + highlight) vs. just lighting the scale keys. - Scale source: selected clip vs. enforced global scale. ## References - SteelSeries GameSense SDK (JSON handlers, per-key / bitmap lighting): https://github.com/SteelSeries/gamesense-sdk - Live Object Model — `Song` (scale properties): https://docs.cycling74.com/apiref/lom/song/ - Controlling Live using Max for Live: https://help.ableton.com/hc/en-us/articles/5402681764242-Controlling-Live-using-Max-for-Live - Keys and Scales in Live 12 FAQ: https://help.ableton.com/hc/en-us/articles/11425083250972-Keys-and-Scales-in-Live-12-FAQ