Skip to content
Kira

Reverse engineering · Web · Audio

JamJum JP-1 Studio: reverse engineering a MIDI controller

I decoded the undocumented configuration protocol of a MIDI controller and built my own web app to edit it: pads, knobs, buttons, RGB color and effects.

Year
2026
Status
Validated on hardware
Role
Reverse engineering & development · personal project
Repository
Repository coming soon

01 · Problem

The JamJum JP-1 (16 pads, 8 knobs, 6 transport buttons, USB and Bluetooth) can only be configured with a limited official app. The protocol is undocumented and the Holtek microcontroller is read-protected: its firmware cannot be dumped.

02 · Approach

I captured the USB-MIDI traffic with USBPcap and Wireshark, diffed dumps bit by bit and tested checksum hypotheses with brute force and RANSAC until I could reproduce every message of the official app byte for byte. Then I wrote JamJum Studio, a Web MIDI app that runs in Chrome.

03 · Result

Protocol decoded: the app reads the full dump of each preset on connect and writes pads, knobs, transport buttons and per-pad RGB color. All four checksums are solved and verified byte for byte against the official app (for color, 384 out of 384 blocks). It adds dynamic RGB effects, including a reactive “ripple” when you hit the pads.

04 · Stack

  • Web MIDI API
  • JavaScript
  • USBPcap
  • Wireshark / tshark
  • Python
  • Node.js (tests)

The protocol in one line

Every configuration message is a proprietary SysEx: F0 78 32 <3-byte command> … F7. The payload is not made of regular bytes: it is a 7-bit, LSB-first bitstream (the low 7 bits of each byte). Fields do not align to byte boundaries, so they had to be mapped bit by bit.

Command Direction What it does
45 00 00 00 40 7F PC → JP-1 Opens the edit session (without it, writes are ignored)
1D 0B … PC → JP-1 Selects a preset and triggers its dump
4D 0A 04 … PC → JP-1 Writes the active pad bank
51 4A 02 … PC → JP-1 Writes the active knob bank
49 02 03 … PC → JP-1 Writes the transport buttons
45 3A 03 … PC → JP-1 RGB color of the 16 pads
21 03 00 00 40 7F PC → JP-1 Saves to flash

The checksum that wasn’t a CRC

Every write block carries a checksum. The first hypothesis —a linear CRC— turned out to be wrong: it came from a solvability test with too few equations. All four checksums are modular sums from the same family:

checksums.py
def color_checksum(channels: list[int], pad_index: int) -> tuple[int, int]:
"""channels: the 48 8-bit values (16 pads × G, R, B)."""
s = sum(channels) + pad_index # the missing piece: add the index
chk = (2720 - 32 * s) % 8192
return chk & 0x7F, (chk >> 7) & 0x7F # bytes 64 and 65
def pads_checksum(pads: list[dict]) -> tuple[int, int]:
s = sum(p["channel"] + 16 * p["type"] + p["note"] + p["min"] + p["max"] for p in pads)
c = (510 - 2 * s) % 512
return c & 0x7F, (c >> 7) & 0x7F # bytes 76 and 77

The buttons checksum is (8175 − 32·Σ) mod 8192 and the knobs one is (15456 − 32·Σ) mod 16384. For color, a “uniform” model fit the first captures by pure aliasing; a new capture with varied indices and brightness, plus a (K + w·Σ) mod M search over several definitions of Σ, finally cracked it.

What the app does

  • Auto-load on connect: it infers the active preset and bank from the handshake and decodes the dump (including the color of all 3 banks, starting at bit 2517).
  • Per-pad editor: type (note, CC, program change), channel, velocity range and color, with live preview.
  • Writes like the official app: one block per change and an explicit save to flash.
  • RGB effects: rainbow, chase, pulse, gradient, hit-reactive and “ripple” (rings expanding from the pad you hit), each with its own parameters.

Takeaways

  1. A hypothesis is worth nothing until it is verified on hardware. Models that fit on the computer failed on the device until validation was byte for byte.
  2. Test the layer people actually use. At first I validated the engine from the console, not the UI; the UI had a different bug.
  3. Well-designed captures save experiments. One capture with varied data solved what dozens of random trials could not.