Content
# Drummy 🥁
An open-source AI drummer you can talk to and jam with.
Say *"hey, give me a beat like Brick House"* and a groove starts playing through real
TR-808 / LinnDrum / MPC60 samples in your browser — or a real **acoustic kit**
(the Salamander Drumkit), where velocity picks the actual soft/medium/hard recording,
ghost notes are real ghost-note captures, and the open hat blooms from semi-open
sizzle to a full wash as you lean in. Say *"more hi-hat, make it swing"* and the
change lands at the next bar boundary, click-free.
## Why this exists
This is a side project, and the goal is **not to replace drummers**. The goal is to
**unblock a musician mid-composition**.
If you're a guitarist working out a riff, the moment you stop to click through a
drum-machine UI or finger-tap a pad pattern, you've left the flow. Here you just
*say what you want* — "give me a lazy half-time shuffle", "more ghost notes",
"open the hat on the 3rd sixteenth of every beat" — and keep playing while the beat
reshapes itself under you. It's the difference between programming a drum machine and
directing a session drummer who never gets tired of "one more time, but busier."
When it's time to record the real thing, hire the real drummer — they'll play it
better than this ever will. This just keeps you moving until then.
There's no app and no UI to learn: it's an [MCP](https://modelcontextprotocol.io) server,
so any MCP client — Claude Code, Claude Desktop — is the chat surface. The LLM authors
the groove from its own song and music-theory knowledge; this server compiles it to
[Strudel](https://strudel.cc) code and streams it to a local browser page that loops it.
## Quickstart (clone → jamming)
Requirements: macOS (Linux likely works but is untested), Node ≥ 20, a current
Chrome/Safari, internet at play time
(Strudel + samples load from CDNs), and an MCP client.
```bash
git clone https://github.com/rh-adrian/drummy.git
cd drummy
npm install
npm run build
```
Register with Claude Code (`-s user` makes it available in every session):
```bash
claude mcp add -s user drummy -- node "$PWD/dist/index.js"
```
For Claude Desktop or other MCP clients, copy [.mcp.json.example](.mcp.json.example)
into your client's MCP config and fix the absolute path.
Then jam:
1. Start a new session in your MCP client (the server starts with it).
2. Open **http://localhost:4747** and click **▶ Start** — one click is required by
browser autoplay policy. Leave the tab open: it shows the groove as a live step
grid (one row per drum with its full name and a volume fader, a drummer's count
header — Bar 1 · 1 e & a … — cells lit by velocity, a playhead sweeping in time) with
tempo, kit, and connection status, plus a play/pause button and the compiled
Strudel code behind a "show code" toggle. **Click any cell to cycle it: off →
normal → STRESS → ghost → off**; each row also has a volume fader, an **M**
mute, and a **✕** clear button, with **↩ undo** (your last ~20 page edits) and
a **BPM input** in the transport. Everything lands in the same groove the LLM
works on (`trackGain`/`muted` show up to it as `[vol N%]`/`[muted]` and survive
its edits), so "make it swing" after your tweak keeps your tweak. Pausing is
respected: your own edits update the grid silently; only the LLM playing
something new resumes audio. Grooves can carry up to 6 pre-authored **fills**
that substitute at loop ends on a non-periodic random schedule (a "fills"
toggle appears when the groove has them).
3. Ask for a beat: *"give me a beat like Brick House"*, *"a dusty 90 BPM boom-bap
groove"*, *"four-on-the-floor house at 124"*.
4. Iterate: *"busier hats"*, *"add ghost notes on the snare"*, *"halftime it"*,
*"try it on the 909"*, *"play it on the acoustic kit"*, *"slow it down to 95"*.
No MCP client handy? `npm run dev:jam` plays a canned groove through the same page.
## The tools (what the LLM drives)
| Tool | What it does |
|------|--------------|
| `play_groove` | Start (or replace) the looping groove — drum-tab grid notation or groove JSON |
| `update_groove` | Partial edit of the playing groove — only the changed fields are sent |
| `set_tempo` | Change BPM smoothly without restarting the loop or losing bar phase |
| `set_kit` | Same pattern, different kit's samples (drum machines or the acoustic kit) |
| `list_kits` | Available banks with sound descriptions + which drums each provides |
| `stop` | Silence; a later `play_groove` starts fresh |
| `now_playing` | Current state: groove, tempo, kit, playing/stopped, page connected |
The tool descriptions teach the model the drummer vocabulary (drum abbreviations,
ghost-note velocity conventions, swing semantics, genre sketches), so any frontier
model can drive it with no external docs.
A groove is a bar grid — `resolution` steps per bar × `bars`. The preferred way to
write one is **grid notation** (v1.4): drum-tab rows, one character per step, letters
carry velocity — `X` accent (1.0), `x` normal (0.7), `o` soft (0.5), `g` ghost (0.25),
`-`/`.` rest, `|` cosmetic beat separator:
```jsonc
{
"tempoBpm": 100,
"swing": 0.12, // 0–0.6, fraction of a step; delays every odd step
"bank": "RolandTR808", // see list_kits
"bars": 1, // 1–4
"resolution": 16, // steps per bar: 8, 12, 16, 24, 32 (12/24 = triplet feels)
"grid": {
"hh": "X-x-|x-o-|X-x-|x-o-", // accents vary — uniform velocity sounds robotic
"sd": "----|X--g|-g--|X---", // backbeat + ghost notes
"bd": "X---|--x-|----|-x--"
}
}
```
Columns line up across rows, so how the kick, snare, and hats interlock is visible in
the notation itself — the representation LLMs reason over drum grooves best
([arXiv:2505.08203](https://arxiv.org/abs/2505.08203)). Tool responses render the
playing groove back in the same notation (including your grid click-edits).
The JSON `voices` form (`hits` with exact `vel` and per-hit `micro` push/drag) remains
first-class for fine control beyond the four letters — use one or the other per call:
```jsonc
"voices": [
{ "drum": "sd", "hits": [ { "step": 4 }, { "step": 12, "micro": 0.03 }, // lazy backbeat
{ "step": 6, "vel": 0.25 } ] } // ghost note
]
```
## How it works
```
MCP client (Claude Code / Desktop) — the chat surface
→ MCP server (stdio) src/server/tools.ts drummer-vocabulary tools
→ groove schema + compiler src/core/* groove JSON → Strudel code
→ HTTP+WS sidecar src/server/sidecar.ts serves the page, pushes code
→ browser page page/ @strudel/web via CDN, loops it
```
Everything runs in one process launched by your MCP client. The page connects back over
a websocket, applies incoming patterns at the **next bar boundary** (Strudel's clock
phase is preserved, so swaps are click-free and never lose their place), heartbeats its
status, and auto-reconnects. Real-time control is a non-goal — a change landing at the
top of the next bar (~2–4 s) is how you'd count a band in anyway.
## Configuration
| Env var | Default | Meaning |
|---------|---------|---------|
| `AI_DRUMMER_PORT` | `4747` | Port for the page + websocket sidecar |
Set it in the MCP registration, e.g.
`claude mcp add -s user drummy -e AI_DRUMMER_PORT=5757 -- node "$PWD/dist/index.js"`.
## Troubleshooting
- **No sound** — you must click **▶ Start** on the page once per load (browser
autoplay policy). The tab must stay open; audio keeps playing in a background tab.
Also check the page's own play/pause button (next to the tempo) isn't showing ▶ —
it pauses locally without the LLM knowing to restart.
- **"No drummer page is connected"** — the tool result already says it: open
http://localhost:4747 and click Start, then ask again.
- **"port 4747 is already in use"** — another Drummy session (another Claude
window?) owns the port. One jam session per port: close the other session, or give
this one its own `AI_DRUMMER_PORT`.
- **No internet** — playback needs it: the page loads `@strudel/web` from unpkg and
samples from Strudel's CDN at runtime (see Licensing). Offline mode would require
vendoring samples, which is blocked on license verification ([SAMPLES.md](SAMPLES.md)).
- **First hit of a freshly-switched kit sounds clipped** — can happen once on very slow
connections while samples stream in; the next bar is fine.
- **Page shows connected but tools say it isn't** — a tab left hidden *and stopped* for
5+ minutes gets its timers throttled by Chrome, which stalls the heartbeat. Focus or
reload the tab.
## Development
```bash
npm test # vitest — schema, compiler, sidecar, MCP tools (in-memory transport)
npm run dev:jam # sidecar + canned groove, no MCP client needed
npm run e2e # full stack: real stdio server + headless Chrome + audio assertions
npm run capture # DSP verification harness: onset grid, tempo, ghost/accent RMS, swing
```
`e2e` and `capture` drive a headless Chrome via Playwright — run
`npx playwright install chromium` once first. They record the page's actual audio
output to WAV and assert on it (onsets on the grid, measured tempo, ghost notes quieter
than accents, bar-boundary swap continuity, post-stop silence): audio correctness is CI-checkable,
not ear-checked. Development is spec-driven; [specs/drummer-v1/SPEC.md](specs/drummer-v1/SPEC.md)
is the source of truth for requirements, decisions, and progress.
Background reading: [RESEARCH.md](RESEARCH.md) (landscape analysis and why symbolic
beats neural audio here), [IDEAS.md](IDEAS.md) (parked future expansions, e.g.
mouthed-bassline → bass MIDI).
## Licensing
This repo is **MIT** — all code here is ours and freely licensed.
Things it deliberately does **not** contain:
- **Strudel** (`@strudel/web`, AGPL-3.0-or-later) — loaded unmodified from unpkg by
*your browser* at page runtime. It is not bundled, linked, imported, or redistributed
by this project; the server only ever emits Strudel *source code as text*. AGPL
obligations attach to Strudel itself (which is already open), not to this repo.
- **Drum samples** (tidal-drum-machines) — streamed from Strudel's CDN by the browser.
The individual WAV licenses are unverified upstream, so they stay out of the repo
entirely. Sources and license status are tracked in [SAMPLES.md](SAMPLES.md).
- **The acoustic kit** (Salamander Drumkit by Rytmenpinne, CC BY-SA 3.0) — streamed
by the browser from a pinned GitHub mirror; only an index of file names
([page/acoustic.json](page/acoustic.json)) lives here. Attribution and details in
[SAMPLES.md](SAMPLES.md).
The trade-off is deliberate: internet required at play time, in exchange for a repo
that is pure MIT with zero license ambiguity.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available