anupars (อนุภา(ส), meaning "tiny following light" in Thai), a Rust-based reimagining of anu a musical sequencer driven by regular expressions, designed to operate on resource-constrained devices, and performance-oriented.
Warning
This project is a work in progress. Features and APIs are subject to change anytime.
- A MIDI-capable application or device connected
- Go to the Releases page and download the archive for your platform.
- Extract and make the binary executable:
# macOS only, remove quarantine set by Gatekeeper on downloaded binaries
xattr -d com.apple.quarantine ./anupars
chmod +x ./anupars
./anupars# Windows — just double-click anupars.exe, or run from PowerShell:
.\anupars.exeRequires the Rust toolchain (stable).
git clone https://github.com/karnpapon/anupars.git
cd anupars
cargo run- Open the menubar with
Ctrl+b, the menubar will be shown at the top of terminal screen. Click "anupars" > "Generate Text", enter a length for the generated text, then hit(obsoleted for v0.2.0)Enter, this fills the grid editor with content (you can also provide your own text inInsert FilebelowGenerate Textmenu).- Click
anupars>MIDIensure you select the right MIDI output. - Type a regex, the matching cells are highlighted on the grid in real time.
- Press
Escto switch between the regex input and the grid editor section (there's a little indicator to tell which section is currently selected). - the playhead will be initialises at position (0, 0) top-left (since it initialized with len=1 so you might trying to look for it).
- Move the playhead with
h / j / k / l(vim-style) or click directly on the canvas. - Scale playhead with
Shiftmodifier key, along with move key (h / j / k / l) or just mouse dragging. - Leap playhead similar to how
Orcaleap, you can use modifier keyAlt(orOptionin osx) combine with move key and scale key. - Press
Spaceto start playback, the playhead steps through cells that match your pattern (try increasing beat division by}if you want to make it faster). - Change tempo with
>(faster) and<(slower). - to enable mode use
Ctrlalong with available mode chars (a,n,u,e,s,y,zyou can easily see theses being displayed on the top of the screen) let's start withsweepmode usingCtrl+sthe active mode with change from lowercase to uppercase mode the crosshair appears and it'll trigger all the trigger point along y-axis (the velocity is based-on the trigger point distance to the playhead (in y-axis), the far from playhead, the lower velocity), all modes is togglable all default to off initially. - the top spatial keyboard layout (inspired by Music Mouse - Laurie Spiegel) will hint you what note is being triggered, where
#represent black-key━represent white-key- long-vertical represent natural C
- the number in long-vertical key is an octave
- a left keyboard (vertical layout) is also available — press
~to toggle between top keyboard ([ ∧ ]) and left keyboard ([ ∨ ]). When the left keyboard is active, notes vary along the y-axis (each row is a pitch) instead of the x-axis. - try changing scale by
Shift+Plus(scale up) orShift+Underscore(scale down); use(/)to change the left keyboard scale directly - changing root note by
=(root note up) or-(down); use9/0to change the left keyboard root note directly
That's all you need for a first session. Explore Features and Keybindings whenever you're ready to go deeper.
-
Tiny Binary Size
- Optimized for minimal footprint (~855 KB) - release builds use
opt-level = "z",lto, and symbol stripping, producing a small self-contained binary suitable for resource-constrained devices.
- Optimized for minimal footprint (~855 KB) - release builds use
-
MIDI In/Out Selector
- Choose from available MIDI devices for flexible routing to synths, DAWs, or hardware.
-
MIDI In (Clock Sync)
- When MIDI input clock messages are enabled (default:
disabled), the app will listen for incoming MIDIStart/Stop/Continue/Clockmessages and synchronize playback to an external master clock. - Note: enabling MIDI input clock-sync disables the spacebar
play/pausecontrol to avoid conflicting local and external transport control. Disable MIDI input if you want to use the spacebar locally.
- When MIDI input clock messages are enabled (default:
-
MIDI Out Clock Features
- Implements standard MIDI clock transport messages for external device:
StartSends MIDI Start (0xFA) to begin playback from the start.StopSends MIDI Stop (0xFC) to halt playback.TickSends MIDI Clock (0xF8) for timing synchronization (24 per quarter note (PPQN)).ContinueSends MIDI Continue (0xFB) to resume playback from the current position.Song Position Pointer(SPP) Sends MIDI Song Position Pointer (0xF2) to set the playback position in beats.
- Implements standard MIDI clock transport messages for external device:
-
Dual Spatial Keyboard Layout
- Two independent on-screen keyboards inspired by Laurie Spiegel's Music Mouse, enabling expressive, algorithmic play.
- Top keyboard (horizontal layout): notes vary along the x-axis — each column is a distinct pitch. The keyboard strip is drawn across the top of the grid.
- Left keyboard (vertical layout): notes vary along the y-axis — each row is a distinct pitch. The keyboard strip is drawn along the left edge of the grid.
- Press
~to toggle between the two keyboards. The active keyboard is shown as[ ∧ ](top) or[ ∨ ](left) at the top-left of the canvas. - Scale mode and root note changes (
Shift++,Shift+_,=,-) apply to whichever keyboard is currently active. - The left keyboard also has dedicated shortcuts that always target it regardless of the active keyboard:
(/)cycle scale mode,9/0adjust root note. - Sweep mode always uses the top keyboard regardless of the active selection.
-
Separated Scale Change for Vertical/Horizontal Steps
- Independently assign musical scales for vertical (Y-axis) and horizontal (X-axis) movement, allowing complex modal and harmonic explorations.
-
Movement
- Instantly change running direction of the sequencer, creating evolving or retrograde patterns at the touch.
- Forward
- Reverse
- Pendulum
- Random
- The active movement is shown in the
MVE:status bar as[f]rdp(brackets mark the current mode).
- Instantly change running direction of the sequencer, creating evolving or retrograde patterns at the touch.
-
Sweep Movement Mode
- An independent movement direction for the sweep crosshair, decoupled from the normal playhead movement. Press
!to toggle (requires Sweep mode to be active first). - When enabled, the sweep crosshair
<x>moves according to its own direction while the playhead[x]continues on its own. Both indicators are shown together inMVE:, e.g.[f]<r>dpmeans normal=Forward, sweep=Reverse. - Initializes to the current normal movement when activated.
- Sweep movement directions:
- Forward (
Ctrl+f): crosshair moves left→right (0→W-1) each step. - Reverse (
Ctrl+r): crosshair moves right→left (W-1→0) each step. - Random (
Ctrl+d): crosshair jumps to a deterministic pseudo-random column each step. - Pendulum (
Ctrl+p): crosshair oscillates W-1→0→W-1→… (reversed pendulum).
- Forward (
- Press
!again to disable; the crosshair returns to following the playhead.
- An independent movement direction for the sweep crosshair, decoupled from the normal playhead movement. Press
-
Arpeggiator Mode
- When enabled, the sequencer steps only through positions matching the current regex, producing arpeggiator-like melodic patterns from your rules.
-
Accumulation Mode (Semi Self-Configuration)
- Activate accumulation mode to let the system semi-autonomously reconfigure itself via Queue System, stacking and evolving patterns for emergent musical results.
-
Drone Mode
- A sustained-note layer that runs independently of the sequencer playback. Toggle with
Ctrl+o. - When active, a vertical drone line appears at the left edge of the playhead area. Every row with a regex match at that x-column is held simultaneously, producing a chord that sustains until the line moves or the mode is toggled off.
- No sound on enable — toggling drone mode on only shows the line; no MIDI is triggered until you explicitly move the line with
iorp. This avoids unwanted notes during live performance. - Notes use the left keyboard scale (
scale_mode_left/scale_root_left) with y-axis velocity (higher rows = louder). Cells inside the playhead area are excluded to avoid overlap with the sequencer trigger. - Move the drone line with
i(left) /p(right), held notes fade out with a short tail before the new position fires, enabling smooth chord transitions. - Cycle the MIDI output channel with
I(decrease) /P(increase), wrapping through channels 1–16. When a non-default channel is selected it is shown in the status bar asO<ch>(e.g.O<3>).
- A sustained-note layer that runs independently of the sequencer playback. Toggle with
-
Modes (shown in status bar; uppercase = active)
a: Arpeggiator (see above)n: Drain Queueu: Accumulation (see above)e: Event Operator, enables event operator triggering from keyboard.s: Sweep, sweeps through positions across the playhead range.o: Drone, sustains matched notes at a movable vertical line (see Drone Mode above).y: Dynamic Length, playhead length adjusts dynamically.z: Freeze, locks the active position and retriggers MIDI at that cell each DIV tick (it's quantized to the beat).
-
Sweep Tilt (TLT) Controls the angle of the sweep crosshair. this mode should be used with "Sweep" mode, it moves like a "Bishop" movement in chess. Each triggered cell fires on the MIDI channel matching its visual column band Cycles through three modes displayed in the status bar:
|Vertical (default): crosshair sweeps straight down, same column for every row.\DiagDown: crosshair shifts one column right per row below the playhead, left per row above (like a\diagonal)./DiagUp: crosshair shifts one column left per row below the playhead, right per row above (like a/diagonal).
-
Sweep Row Mode Filters which rows the sweep crosshair draws on and triggers MIDI from. requires Sweep mode to be active. The active sub-mode is shown inline in the mode status bar after the
Scharacter (e.g.S<O>). Cycles through four modes with|:- Normal (default): every row is active — identical to previous sweep behaviour.
OOdd rows only (rows 1, 3, 5 …): even rows are silenced and not rendered.EEven rows only (rows 0, 2, 4 …): odd rows are silenced and not rendered.RRandom: each row is independently gated by a deterministic hash of its index, producing a fixed sparse pattern.
-
Scale Selection
- Scale root selectable across all 12 chromatic pitches (C–B).
# Scale Notes 1 Chromatic2 Major3 Minor4 Harmonic Minor5 Melodic Minor6 Dorian7 Phrygian8 Lydian9 Mixolydian10 Locrian11 Major Pentatonic12 Minor Pentatonic13 Blues14 Whole Tone15 Diminished16 Thai 7-TETmicrotonal
Inpspired by Event Loop, the queue is a first-in-first-out (FIFO) dispatch mechanism, events accumulate, wait, and are consumed one at a time, each driving a state transition in the sequencer. every jump or timbral gesture is the result of something that was previously enqueued.
it comprised of
- Event Queue (EVQ) holds pending event operators to be fused into the next push, current available ops are
c/>CHORDchord event triggers a triad.h/>HOLDNhold event sustains the note.r/>RTCHTratheting (re-triggering) the note.
- Queue Operators spatially mapped on keyboard, current available ops are
PPush, push current playhead position (or front event) onto OPQ.SSwap, swap the top two items in OPQ.OPop, execute and remove the front item from OPQ.DDuplicate, duplicate the top item in OPQ.
| Key | Action | Description |
|---|---|---|
h |
Move Left | Move playhead 1 step left |
j |
Move Down | Move playhead 1 step down |
k |
Move Up | Move playhead 1 step up |
l |
Move Right | Move playhead 1 step right |
Ctrl+h |
Aim Left (preview) | Hold to preview a left jump, press any movement key to commit |
Ctrl+j |
Aim Down (preview) | Hold to preview a down jump, press any movement key commit |
Ctrl+k |
Aim Up (preview) | Hold to preview an up jump, press any movement key commit |
Ctrl+l |
Aim Right (preview) | Hold to preview a right jump, press any movement key commit |
Option+h / Alt+h |
Leap Left | Jump 5 steps left |
Option+j / Alt+j |
Leap Down | Jump 5 steps down |
Option+k / Alt+k |
Leap Up | Jump 5 steps up |
Option+l / Alt+l |
Leap Right | Jump 5 steps right |
Note (macOS): Option+hjkl produces Unicode characters (
˙,∆,˚,¬) which are automatically mapped.
| Key | Action | Description |
|---|---|---|
Shift+h / H |
Scale Left | Decrease area width by 1 |
Shift+j / J |
Scale Down | Decrease area height by 1 |
Shift+k / K |
Scale Up | Increase area height by 1 |
Shift+l / L |
Scale Right | Increase area width by 1 |
Shift+Option+h* |
Scale Left (Large) | Decrease area width by 8 |
Shift+Option+j* |
Scale Down (Large) | Decrease area height by 8 |
Shift+Option+k* |
Scale Up (Large) | Increase area height by 8 |
Shift+Option+l* |
Scale Right (Large) | Increase area width by 8 |
Note (macOS only): Shift+Option combinations produce Unicode characters (
Ó,Ô, ``,Ò) for large scale adjustments.
| Key | Action | Description |
|---|---|---|
1 |
1×1 Grid | Single cell (no splits) |
2 |
2×1 Grid | 2 vertical splits |
3 |
3×1 Grid | 3 vertical splits |
4 |
4×1 Grid | 4 vertical splits |
5 |
4×2 Grid | 4×2 grid (8 cells) |
6 |
4×3 Grid | 4×3 grid (12 cells) |
7 |
4×4 Grid | 4×4 grid (16 cells) |
| Key | Action | Description |
|---|---|---|
Space |
Toggle Play/Pause | Start or stop playback |
Ctrl+f |
Toggle Forward | Enable forward playback mode |
Ctrl+r |
Toggle Reverse | Enable reverse playback mode |
Ctrl+d |
Toggle Random | Enable random playback mode |
Ctrl+p |
Toggle Pendulum | Enable pendulum playback mode |
| Key | Action | Description |
|---|---|---|
Ctrl+a |
Toggle Arpeggiator | Enable/disable arpeggiator mode |
Ctrl+u |
Toggle Accumulation | Enable/disable queue accumulation |
Ctrl+e |
Toggle Event Operator | Enable/disable event operators |
Ctrl+n |
Toggle Drain Queue | Enable/disable queue draining |
Ctrl+s |
Toggle Sweep | Enable/disable sweep mode |
! |
Toggle Sweep Movement | Enable/disable independent sweep crosshair direction (requires Sweep mode), initializes to current normal movement |
Shift+| |
Cycle Sweep Row Mode (SWP) | Cycle sweep row filter: Nrm → Odd → Even → Rnd |
Ctrl+t |
Cycle Tilt (TLT) | Cycle sweep crosshair angle: | → / → \ |
Ctrl+o |
Toggle Drone | Enable/disable drone mode |
Ctrl+y |
Toggle Dynamic Length | Enable/disable dynamic length mode |
Ctrl+z |
Toggle Freeze | Lock active position; retrigger matched cell at current DIV rate |
| Key | Action | Description |
|---|---|---|
Ctrl+o |
Toggle Drone | Enable/disable drone mode; line starts at left edge of playhead area |
i |
Move Drone Left | Shift drone line one column left (within playhead area) |
p |
Move Drone Right | Shift drone line one column right (within playhead area) |
I |
Cycle Drone Channel Down | Previous MIDI channel (1–16, wraps); shown as O<ch> in status bar |
P |
Cycle Drone Channel Up | Next MIDI channel (1–16, wraps); shown as O<ch> in status bar |
| Key | Action | Description |
|---|---|---|
> |
Increase BPM | Speed up tempo |
< |
Decrease BPM | Slow down tempo |
} |
Increase Ratio | Increase timing ratio |
{ |
Decrease Ratio | Decrease timing ratio |
| Key | Action | Description |
|---|---|---|
Shift++ / Shift+Plus |
Cycle Scale Mode Up | Next musical scale (applies to active keyboard) |
Shift+_ / Shift+Underscore |
Cycle Scale Mode Down | Previous musical scale (applies to active keyboard) |
= / Equal |
Increase Root Note | Next root note (applies to active keyboard) |
- / Minus |
Decrease Root Note | Previous root note (applies to active keyboard) |
~ |
Toggle Spatial Keyboard | Switch between top keyboard [ ∧ ] (x-axis, horizontal) and left keyboard [ ∨ ] (y-axis, vertical) |
| Key | Action | Description |
|---|---|---|
Ctrl+b |
Show Menubar | Display/access menu |
Esc |
Toggle Input Mode | Switch between regex input and canvas |
q |
Quit | Exit application |
| Action | Description |
|---|---|
| Click | Set playhead position to clicked location |
| Click+Drag | Adjust playhead area size by dragging |
- macOS: Option key (⌥) is used for leap and large scale adjustments
- Linux/Windows: Alt key behaves similarly to Option on macOS for leap functions
- Cross-platform: Vim-style hjkl keys work consistently across all platforms
- Arrow keys have been removed in favor of vim-style navigation
- Platform-specific key combinations use
#[cfg(target_os = "macos")]compilation flags - Minimum playhead area size is enforced (1×1) to prevent overflow errors
- Un coup de dés jamais n'abolira le hasard (A Throw of the Dice Will Never Abolish Chance) — Stéphane Mallarmé (1897) English translation by Peter G. Doyle, via Wikisource
- Docker must be installed before proceeding
- Execute:
sh ./build - finger-crossed
Supported Platforms:
- Desktop: Linux, macOS, Windows (x86_64, ARM64)
- Embedded: Raspberry Pi 4B (aarch64-unknown-linux-gnu)
cargo run
cargo build --release
