uv run pytest tests/ -v # all tests
uv run pytest tests/test_sequencer.py -v # fast unit tests only
uv run pytest tests/test_ui.py -v # Playwright UI tests (starts server)Start the server: uv run uvicorn server:app --reload
Open: http://127.0.0.1:8000
examples/example.json — 128 BPM, two patterns:
- drums (ch9, 16 steps): kick on 0,4,8,12 / snare on 2,6,10,14 / hihat on all 16 (vel 60)
- bass (ch0, 16 steps): notes on 0,4,8,12
Load with: load examples/example.json or via the Load button.
- [FRESH] — restart server, open a clean browser tab (or clear localStorage)
- [EXAMPLE] — load
examples/example.jsonfirst
-
Do: Click the Play button in the transport bar
-
Observe: Button changes to "Stop", playhead animates across both pattern grids
-
Do: Click Stop
-
Observe: Button changes to "Play", playhead disappears
-
Do: Type
playin the command input, press Enter -
Observe: Playback starts, button reflects playing state
-
Do: Type
stop -
Observe: Playback stops
- Do: With empty command input focused, press Space
- Observe: Toggles play/stop
- Do: Press Ctrl+Space from anywhere on the page
- Observe: Toggles play/stop
-
Do: Type
bpm 140 -
Observe: BPM display in transport bar updates to 140
-
Do: Change BPM via the number input in the transport bar
-
Observe: Log shows BPM change confirmation
- Do: Type
new mybeat 16 9(16 steps, channel 9/drums) - Observe: Pattern appears in both detail view and overview sidebar
- Do: Type
list - Observe: Log shows all patterns with step count and channel
- Do: Type
delete mybeat - Observe: Pattern removed from detail view and overview
- Do: Create a pattern (
new test 16 0), then typerename test renamed - Observe: Pattern name updates everywhere
- Do: Type
copy drums drums2 - Observe: New pattern appears with same data as original
- Do: Type
len drums 32 - Observe: Grid expands to 32 columns
- Do: Type
new beat 16 9, thenput beat 0,4,8,12 kick 100 - Observe: Grid cells fill at steps 0, 4, 8, 12 on the kick row
- Do: Type
put beat 0-7 hihat 80 - Observe: Steps 0 through 7 fill on hihat row
- Do: Type
put beat 0-15:2 snare 90 - Observe: Even steps fill on snare row
- Do: Type
clear beat 0,4 - Observe: Those steps empty across all notes
- Do: Type
remove beat 0-3 kick - Observe: Kick notes removed from steps 0-3, other notes untouched
- Do: Type
new euc 16 9, theneuclid euc 5 16 hihat 100 - Observe: 5 hits distributed across 16 steps in a Euclidean pattern
- Do: Type
new melody 16 0, thenarp melody 0-15 C4,E4,G4 100 - Observe: Notes cycle through C4, E4, G4 across steps
- Do: Type
vel drums 0-15:2 hihat 120 - Observe: Log confirms "Set velocity 120 on 8 hit(s)" (example has hihat on all 16 steps)
- Do: Type
vel drums 0-15 cowbell 120 - Observe: Log shows "No cowbell hits found on those steps. Use 'put' to place notes first."
- Do: Type
vel drums 0-15:2 hihat 120(even steps loud), thenvel drums 1-15:2 hihat 30(odd steps quiet) - Observe: Even-step hihat cells are visibly brighter than odd-step cells
- Do: Type
vel drums 0 hihat 1thenvel drums 2 hihat 127 - Observe: Step 0 cell is dim but still visible (~35% opacity). Step 2 cell is full brightness
- Do: Start playback with mixed-velocity pattern from above
- Observe: Playhead highlight still shows correctly on both bright and dim cells
- Do: Type
show drums - Observe: ASCII grid printed in command log
-
Do: Type
fold drums -
Observe: Pattern grid collapses in detail view
-
Do: Type
unfold drums -
Observe: Pattern grid expands back
- Do: Type
select bass - Observe: Bass pattern highlights in overview sidebar, detail view scrolls to it
- Do: Type
new pad 16 1to get 3 patterns, then typeup/down - Observe: Detail view scrolls through patterns
- Do: Type
mute drums, start playback - Observe: Drums silent, bass still plays, visual indicator in overview
- Do: Type
unmute drums - Observe: Drums play again
- Do: Type
solo drums - Observe: Only drums play, bass silenced
- Do: Type
mute drums hihat, start playback - Observe: Hihat muted within drums, kick and snare still play
- Do: Type
shift drums 2 - Observe: All notes shift 2 steps to the right (kick moves from 0,4,8,12 to 2,6,10,14)
- Do: Type
reverse drums - Observe: Step order flips
- Do: Type
humanize drums 10 - Observe: Velocities randomized slightly (check via
show drums)
- Do: Type
swing drums 50, start playback - Observe: Off-beats get timing delay during playback
- Do: Type
swing drums 60 kick - Observe: Log confirms swing set on kick only
- Do: Type
swing drums 0-15 kick - Observe: Error: "Swing amount must be 0-100, not a step range" with usage hint
- Do: Type
midito toggle monitor, start playback - Observe: MIDI monitor panel appears showing note-on events with note names, velocities
- Do: Type
auto drums cc74 0:0 15:127, start playback - Observe: CC automation shows in MIDI monitor during playback
- Do: Type
panic - Observe: All notes off sent (verify in MIDI monitor or DAW)
- Do: Type
ports - Observe: Available MIDI ports listed in log
- Do: Type
taprepeatedly at a steady rhythm - Observe: BPM adjusts to match tap interval
- Do: Click Save in transport bar
- Observe: Browser downloads
session.json
- Do: Type a few commands to populate the log, then click Load and select the
session.jsonfrom 9.1 - Observe: Both patterns restore with correct notes, velocities, 128 BPM. Command log is cleared (only shows load success message)
-
Do: Type
save test_session.json -
Observe: File saved to server
-
Do: Type a few commands, then type
load test_session.json -
Observe: Session restores, command log is cleared
- Do: Load example, remap a drum (
drummap hihat G#1), then restart the server (autosave triggers on shutdown) - Observe: On restart,
show drumsshows hihat on G#1 row (not F#1) - Observe:
vel drums 0-15 hihat 120correctly finds and updates hihat hits (not "0 hits")
- Do: Type
load examples/example.json, thendrummap hihat G#1, thensave remap_test.json - Do: Restart server, delete
.autosave.json, thenload remap_test.json - Observe:
vel drums 0-15 hihat 120correctly finds and updates hihat hits - Observe: Grid shows hihat notes on the correct row matching the remapped note
- Do: Type
put drums 0-15 cowbell 100, then typeundo - Observe: Cowbell notes removed, grid returns to previous state
- Do: After undo, type
redo - Observe: Cowbell notes reappear
- Do: Type
macro fourfloor put ${pat} 0,4,8,12 kick 100; put ${pat} 2,6,10,14 hihat 80 - Observe: Macro created confirmation
- Do: Type
new drums 16 9, thenfourfloor pat=drums - Observe: Both put commands execute, notes appear in grid
- Do: Type
macro edit fourfloor - Observe: Macro editor panel opens with current commands
- Do: Use the width dropdown (Compact/Normal/Wide/Full)
- Observe: Main content area resizes accordingly
- Do: Press Ctrl+1 through Ctrl+4
- Observe: Width cycles through presets
- Do: Press Ctrl+
or typetab` - Observe: Switches between Command Log and AI Chat tabs
- Do: Click
?button or typehelp - Observe: Cheatsheet panel toggles
- Do: Type several commands, then press Up/Down arrows in command input
- Observe: Previous commands cycle through
-
Do: Type
puthen press Tab -
Observe: Completes to
put -
Do: Type
put drthen press Tab -
Observe: Completes pattern name
drums
- Do: Type
putand a space - Observe: Hint bar shows expected arguments
- Do: Verify drums and bass appear in overview
- Observe: Both patterns shown with density bars
- Do: Click
bassin the overview - Observe: Detail view scrolls to bass, selection highlights
- Do: Type
clear bass 0,4 - Observe: Density visualization updates in overview (fewer filled segments)
- Do: Type
octave yamaha(oroct -1) - Observe: Note labels in bass grid update (e.g. C2 becomes C1)
- Do: Type
drums - Observe: Drum name to MIDI note mapping displayed
- Do: Verify the drums pattern grid
- Observe: Rows show drum names (kick, snare, hihat) instead of note numbers
- Do: Type
describe - Observe: Compact overview showing drums (ch9, 16 steps) and bass (ch0, 16 steps)
- Do: Type
dump - Observe: Full JSON state printed to log including both patterns