Welcome to Cagire
Cagire is a live-codable step sequencer. Each sequencer step is defined by a Forth script that gets evaluated at the right time. Forth is a minimal, fun and rewarding programming language. It has almost no syntax but provides infinite fun. It rewards exploration, creativity and curiosity.
This documentation is both a tutorial and a reference. All the code examples in the documentation are interactive. You can run them! Use n and p (next/previous) to navigate through the examples. Press Enter to evaluate an example! Try to evaluate the following example using n, p and Enter:
saw sound
c4 note
0.5 decay
.
What is live coding?
Live coding is a technique where you write code in real-time to create audiovisual performances. Most often, it is practiced in front of an audience. Live coding is a way to experiment with code, to share things and thoughts openly, to think through code. It can be technical, poetic, weird, preferably all at once. Live coding can be used to create music, visual art, and other forms of media with a strong emphasis on improvisation. Learn more about live coding on toplap.org or livecoding.fr. Live coding is an autotelic activity: the act of doing it is its own reward. There are no errors, only fun.
About
Cagire is mainly developed by BuboBubo (Raphaël Maurice Forment, raphaelforment.fr). It is a free and open-source project licensed under the AGPL-3.0 License. You are free to contribute to the project by contributing to the codebase or by sharing feedback. Help and feedback are welcome!
Credits
- Doux (audio engine) is a Rust port of Dough, originally written in C by Felix Roos.
Navigation
Press ? on any view to see its keybindings. The most important shortcuts are always displayed in the footer bar. Press Esc to close the keybindings panel. These shortcuts work on every view:
| Key | Action |
|---|
Space | Toggle play / stop |
q | Quit |
s | Save project |
l | Load project |
? | Show keybindings for view |
Views
Cagire's interface is organized as a 3x2 grid of six views. Jump to any view with its F-key or Ctrl+Arrow keys:
F1 Dict F2 Patterns F3 Options
F4 Help F5 Sequencer F6 Engine
| Key | View | Description |
|---|
F1 | Dict | Forth dictionary — learn about the language |
F2 | Patterns | Manage your current session / project |
F3 | Options | Configuration settings for the application |
F4 | Help | Help and tutorials — learn about the tool |
F5 | Sequencer | The main view, where you edit sequences and play music |
F6 | Engine | Configuration settings for the audio engine |
Use Ctrl+Arrow keys to move between adjacent views. A minimap will briefly appear to show your position in the grid. You can also click on the view name at the bottom left or in the top left corner of the header bar to open the switch view panel.
Ctrl+Left / Ctrl+Right — move horizontally (wraps around)Ctrl+Up / Ctrl+Down — move vertically (does not wrap)
Secrets
There is a hidden seventh view: the Periodic Script. Press F11 to open it. The periodic script is a free-running Forth script evaluated at every step, independent of any pattern. It is useful for drones, global effects, control logic, and experimentation. See the Periodic Script tutorial for details.
Big Picture
What exactly is Cagire? What purpose does it serve?
Cagire is a small and simple software that allows you to create music live while programming short scripts. At heart, it is really nothing more than a classic step sequencer, the kind you can buy in a music store. It is deliberately kept small and simple in form, but it goes rather deep if you take the time to discover the audio engine and all its capabilities. Adding the Forth language to program steps allows you to create patterns and behaviors of any complexity. Forth also makes it easy to extend and to customize Cagire while keeping the core mechanisms and the logic simple.
Cagire is not complex, it is just very peculiar. It has been created as a hybrid between a step sequencer and a programming environment. It allows you to create music live and to extend and customize it using the power of Forth. It has been designed to be fast and responsive, low-tech in the sense that you can run it on any decent computer. You can think of it as a musical instrument. You learn it by getting into the flow and practicing. What you ultimately do with it is up to you: improvisation, composition, etc. Cagire is also made to be autonomous, self-contained, and self-sustaining: it contains all the necessary components to make music without relying on external software or hardware.
Scripts, Not Notes
A traditional step sequencer would offer the musician a grid where each step represents a note or a single musical event. Cagire replaces notes and/or events in favour of Forth scripts. When the sequencer reaches a step to play, it runs the script associated with it. A script can do whatever it is programmed to do: play a note, trigger a sample, apply effects, generate randomness, or all of the above. Scripts can share code and data with each other. Everything else works like a regular step sequencer: you can toggle, copy, paste, and rearrange steps freely.
0.0 8.0 rand at
sine sound
200 2000 rand 100 4000 rand
4 slide freq 0.6 verb 2 vib
0.125 vibmod 0.2 chorus
0.4 0.6 rand gain
.
What Does a Script Look Like?
A Forth script is generally kind of small, and it solves a simple problem: playing a chord, tweaking some parameters, etc. The more focused it is, the better. Using Forth doesn't feel like programming at all. It feels more like juggling with words and numbers or writing bad computer poetry. Here is a program that plays a middle C note for two steps using a sine wave:
c4 note sine sound 2 decay .
Read it backwards and you will understand what it does:
. — play a sound.2 decay — the sound takes two steps to die.sine sound — the sound is a sine wave.c4 note — the pitch is C4 (middle C).
There is pretty much no syntax to learn, just three rules:
- There are
words and numbers.- A
word is anything that is not a space or a number (can include symbols). - A
number is anything that is not a space or a word.
- They are separated by spaces.
- Everything piles up on the stack.
The stack is what makes Forth tick. Think of it as a pile of things. c4 puts a pitch on the pile. note picks it up. sine chooses a waveform. sound assembles everything into a voice. . plays it. Each word picks up what the previous ones left behind and leaves something for the next. Scripts can be simple one-liners or complex programs with conditionals, loops, and randomness. Cagire requires you to understand what the stack is. The good thing is that it will take five minutes for you to make sense of it. See the Forth section for details.
The Audio Engine
Cagire includes a complete synthesis and sampling engine. No external software is required to play music. It comes with oscillators, sample players, effects, filters, and more. Here are a few examples:
;; sawtooth wave + lowpass filter with envelope + chorus + reverb
100 199 freq saw sound 250 8000 0.01 0.3 0.5 0.3 env lpf 0.2 chorus 0.8 verb 2 gate .
;; sine wave + vibrato + bit crushing
0.25 vibmod 8 vib sine sound 6 crush 0.8 gain .
;; white noise + sine wave + envelope = percussion
white sine sound 100 freq 0.5 decay 2 gate .
;; random robot noises: sine + randomized freq + ring modulation
10 1000 rand freq sine sound 1 100 rand rm 0.5 1.0 rand rmdepth .
By creating words, registering synth definitions and effects, you will form a vocabulary that can be used to create complex sounds and music. The audio engine is quite capable, and you won't ever run out of new things to try!
Project Organization
A step sequencer can generally keep multiple patterns in memory, organized in banks. By switching from pattern to pattern, from bank to bank, you can create complex compositions and entire live performances. Cagire works exactly the same and offers banks and patterns. Cagire can also run multiple patterns at the same time. Each pattern can contain from 1 to 1024 steps. Every project is organized following a hierarchical structure:
- 32 Banks: a (named) container holding patterns.
- 32 Patterns: each pattern can contain from
1 to 1024 steps. - 1024 Steps: each step can be used to program anything you have in mind.
A single project / session can store a lot, probably more than you'll need for a gig or an album. That's up to you to decide if you prefer to keep things simple or to store your entire life in Forth.
Timing and Synchronization
Everything in Cagire is measured in beats. A beat is the pulse of the music. How fast that pulse goes is controlled by a single number, the BPM (beats per minute). At 120 BPM, one beat lasts half a second. Each step in a pattern is a sixteenth note: four steps per beat. At 120 BPM, that is one step every 125 milliseconds. Change the BPM and every step follows. Patterns can run at different speeds. A pattern set to 2x fires its steps twice as fast. A pattern at 0.5x takes twice as long. The speed is a multiplier on the step rate, not a separate tempo — all patterns still share the same beat.
Cagire uses Ableton Link for synchronization. All devices on the same network share a common tempo automatically. If you change the BPM in Cagire, other apps follow. If they change it, Cagire follows. Most commercial music software supports Link.
Live Coding
The sequencer runs while you edit. When you change a step's script, the new version takes effect the next time that step fires. There is no compile step, no "run" button — just write, listen, and adjust. This is the core loop: you hear a pattern playing, open a step, change a word, close it, and immediately hear the difference. Forth's brevity helps — swapping sine for saw or adding 0.3 verb at the end is a single edit that reshapes the sound. Cagire is not a write-then-run environment. It is closer to a conversation: you propose something, the sequencer plays it back, and you refine from there.
Banks & Patterns
Cagire organizes all your patterns and data following a strict hierarchy:
- Projects contain Banks.
- Banks contain Patterns.
- Patterns contain Steps.
If strict organization isn't your style, don't worry, you can ignore banks entirely and just work in a single pattern. You can also escape the strict metric using sub-step timing and randomness.
Structure
Project
└── 32 Banks
└── 32 Patterns (per bank)
└── 1024 Steps (per pattern)
A single project gives you 32 banks, each holding 32 patterns. You get 1024 patterns in each project, ~1.048.000 steps. This means that you can create a staggering amount of music. Don't hesitate to create copies, variations, and explore the pattern system thoroughly. The more you add, the more surprising it becomes.
Patterns
Each pattern is an independent sequence of steps with its own properties:
| Property | Description | Default |
|---|
| Length | Steps before the pattern loops (1-1024) | 16 |
| Speed | Playback rate (1/8x to 8x) | 1x |
| Quantization | When the pattern launches | Bar |
| Follow Up | What happens when the pattern finishes an iteration | Loop |
Press e in the patterns view to edit these settings. After editing properties, you will have to hit the c key to launch these changes. More about that later!
Follow Up
The follow-up action determines what happens when a pattern reaches the end of its steps:
- Loop — the pattern repeats indefinitely. This is the default behavior.
- Stop — the pattern plays once and stops.
- Chain — the pattern plays once, then starts another pattern. Use
Left/Right to set the target bank and pattern in the edit view.
Patterns View
Access the patterns view with F2 (or Ctrl+Up from the sequencer). The view shows all banks and patterns in a grid. Indicators show pattern state:
> Currently playing+ Armed to play- Armed to stopM MutedS Soloed
It is quite essential for you to understand the arm / launch system in order to use patterns. Please read the next section carefully!
Keybindings
| Key | Action |
|---|
Arrows | Navigate banks and patterns |
Enter | Select and return to sequencer |
p | Arm pattern to play/stop |
c | Launch armed changes |
m / x | Arm mute / solo toggle |
e | Edit pattern properties |
r | Rename bank or pattern |
Ctrl+c / Ctrl+v | Copy / Paste |
Delete | Reset to empty pattern |
Esc | Cancel armed changes |
Arm / Launch
In Cagire, changes to playback happen in two steps. First you arm: you mark what you want to happen. Then you launch: you apply all armed changes at once. Nothing changes until you launch. It is simpler than it sounds.
Say you want patterns 04 and 05 to start playing together. You arm both (p on each), then launch (c). Both start at the same time. Want to stop them later? Arm them again, launch again. That's it.
This two-step process exists for good reasons:
- Multiple changes at once: queue several patterns to start/stop, launch them together.
- Clean timing: all changes land on beat or bar boundaries, never mid-step.
- Safe preparation: set up the next section while the current one keeps playing.
Arm changes, then launch
Arming is an essential feature to understand to be effective when doing live performances:
- Open the Patterns view (
F2 or Ctrl+Up from sequencer) - Navigate to a pattern you wish to change/play
- Press
p to arm it. The pending change is going to be displayed:+ (armed to play)- (armed to stop)m (armed to mute)s (armed to solo)- etc.
- Repeat for other patterns you want to change
- Press
c to launch all changes - Or press
Esc to cancel
You can also arm mute/solo changes:
- Press
m to arm a mute toggle - Press
x to arm a solo toggle - Press
Shift+m to clear all mutes - Press
Shift+x to clear all solos
A pattern might not start immediately depending on its quantization setting. It might wait for the next beat/bar boundary.
Status Indicators
| Indicator | Meaning |
|---|
> | Currently playing |
+ | Armed to play |
- | Armed to stop |
M | Muted |
S | Soloed |
A pattern can show combined indicators, e.g. > (playing) and - (armed to stop), or >M (playing and muted).
Armed patterns blink to make pending changes impossible to miss.
Quantization
Launched changes don't execute immediately. They wait for a quantization boundary:
| Setting | Behavior |
|---|
| Immediate | Next sequencer tick |
| Beat | Next beat |
| 1 Bar | Next bar (default) |
| 2/4/8 Bars | Next 2, 4, or 8-bar boundary |
Edit quantization in pattern properties (press e on a pattern).
Patterns always start at a beat-aligned position (phase-lock), staying in sync with other running patterns.
The Sequencer Grid
The sequencer grid (F5) is where you spend most of your time in Cagire. It shows the step sequencer and lets you edit each step using the code editor. This is the first view you see when you open the application. Optional widgets — oscilloscope, spectrum analyzer, goniometer, prelude preview, and step preview — can be toggled on for visual feedback while you work.
Navigation
Use arrow keys to move between steps. Shift+arrows selects multiple steps, and Esc clears any selection. The grid wraps around at pattern boundaries. Press : to jump directly to a step by number.
Alt+Up / Alt+Down — Previous / next patternAlt+Left / Alt+Right — Previous / next bank
Editing Steps
Enter — Open the script editort — Make a step active / inactiver — Rename a stepDel — Delete selected steps
Mirrored Steps
Imagine a drum pattern where four steps play the same kick script. You tweak the sound on one of them — now you have to find and edit all four. Mirrored steps solve this: one step is the source, the others are mirrors that always reflect its script. Edit the source once, every mirror follows.
On the grid, mirrors are easy to spot. They show an arrow prefix like →05, meaning "I mirror step 05." Steps that share a source also share a background color, so clusters of linked steps are visible at a glance.
To create mirrors: copy a step with Ctrl+C, then paste with Ctrl+B instead of Ctrl+V. The pasted steps become mirrors of the original. Pressing Enter on a mirror jumps to its source and opens the editor there. If you want to break the link and make a mirror independent again, press Ctrl+H to harden it back into a regular copy.
Copy & Paste
Ctrl+C — Copy selected stepsCtrl+V — Paste as independent copiesCtrl+B — Paste as mirrored stepsCtrl+D — Duplicate selectionCtrl+H — Harden mirrors (convert to independent copies)
Preludes
Each bank has its own prelude — a Forth script for definitions and setup that travels with the bank when shared. There is also a project-wide prelude for global configuration.
p — Open current bank's prelude editorP — Open project prelude editord — Evaluate all preludes (project + all banks)
Pattern Controls
Each pattern has its own length and speed. Length sets how many steps it cycles through. Speed is a multiplier on the global tempo.
< / > — Decrease / increase pattern length[ / ] — Decrease / increase pattern speedL — Set length directlyS — Set speed directly
Playback
Playback starts and stops globally across all unmuted patterns. The highlighted cell on the grid marks the currently playing step.
Space — Toggle play / stop+ / - — Adjust tempoT — Set tempo directlyCtrl+R — Execute current step once (one-shot)
Mute & Solo
Mute silences a pattern; solo silences everything except it. Both work while playing.
m — Mute current patternx — Solo current patternShift+m — Clear all mutesShift+x — Clear all solos
Project
s — Save projectl — Load projectq — Quit
Tools
A few utilities accessible from the grid.
e — Euclidean rhythm distribution? — Show keybindings helpo — Cycle layoutTab — Toggle sample browser panel
Editing a Step
Each step in Cagire contains a Forth script. When the sequencer reaches that step, it runs the script to produce sound. This is where you write your music. Press Enter when hovering over any step to open the code editor. The editor appears as a modal overlay with the step number in the title bar. If the step is a mirrored step (shown with an arrow like →05), pressing Enter navigates to the source step instead.
Writing Scripts
Scripts are written in Forth. Type words and numbers separated by spaces. The simplest script that makes sound is:
;; a very simple sound
sine sound .
Add parameters before words to modify them:
;; the same sound with more parameters
c4 note 0.75 decay sine sound .
Writing long lines can become tedious. Instead, break your code into multiple lines for clarity:
;; the same sound on multiple lines
c4 note
0.75 decay
sine sound
0.4 verb
.
Forth has no special rule about what a line should look like and space has no meaning.
Adding comments to your code
You can comment a line using ;;. This is not very common for people that are used to Forth. There are no multiline comments.
Saving
Esc — Save, compile, and close the editor.Ctrl+E — Save and compile without closing (evaluate in place).
When you save, the script is compiled and sent to the sequencer. If there's an error, a message flashes briefly at the bottom of the screen. Esc has layered behavior: if text is selected, it cancels the selection first. If completions are showing, it dismisses them. Otherwise it saves and closes.
Completion
As you type, the editor suggests matching Forth words. The completion popup appears once you've typed two or more characters of a word, showing candidates that match your input along with their stack signature and description.
Tab — Accept the selected suggestion.Ctrl+N / Ctrl+P — Navigate between suggestions.Esc — Dismiss the list.
Completion helps you discover words without memorizing them all. Type a few letters and browse what's available. For example, typing ver will suggest verb (reverb), typing chor will show chorus-related words.
Sample Finder
Press Ctrl+B to open the sample finder. This provides fuzzy search over your loaded sample folders, making it easy to insert sample names without remembering their exact spelling.
- Type to filter by name
Tab or Enter — Insert the selected sample name.Ctrl+N / Ctrl+P — Navigate matches.Esc — Dismiss.
Search
Press Ctrl+F to open the search bar. Type your query, then navigate matches:
Ctrl+N — Jump to next match.Ctrl+P — Jump to previous match.Enter — Confirm and close search.Esc — Cancel search.
Script preview
Press Ctrl+R to execute the script immediately as a one-shot, without waiting for the sequencer to reach the step. A green flash indicates success, red indicates an error. This is super useful for sound design. It also works when hovering on a step with the editor closed.
The Audio Engine
The Engine page (F6) is where you configure audio hardware, manage MIDI connections, set up Ableton Link, and manage your sample library. The left column holds six configuration sections — press Tab to move between them, Shift+Tab to go back. The right column is a read-only monitoring panel with VU meters, status metrics, and an oscilloscope.
Devices
Two columns show available output and input devices. Press Left or Right to switch between them. Browse with Up/Down and press Enter to select a device. Use PageUp/PageDown to scroll long device lists. Press D to refresh the device list if you plugged something in after launching Cagire.
Settings
Four audio parameters are adjustable with Left/Right:
| Setting | Range | Description |
|---|
| Channels | 1–64 | Number of output channels |
| Buffer | 64–4096 | Audio buffer size in bytes |
| Voices | 1–128 | Maximum polyphony (simultaneous sounds) |
| Nudge | -100 to +100 ms | Timing offset to compensate for latency |
After changing the buffer size or channel count, press Shift+r to restart the audio engine for changes to take effect.
Link
Ableton Link synchronizes tempo across devices and applications on the same network. Three settings are adjustable with Left/Right:
- Enabled — Turn Link on or off. A status badge next to the header shows DISABLED, LISTENING, or CONNECTED.
- Start/Stop Sync — Whether play/stop commands are shared with other Link peers.
- Quantum — Number of beats per phrase, used for phase alignment.
Below the settings, three read-only session values update in real time: Tempo, Beat, and Phase.
MIDI Outputs
Four output slots (0–3). Browse with Up/Down, cycle available devices with Left/Right. A slot shows "(not connected)" until you assign a device.
MIDI Inputs
Same layout as outputs — four input slots (0–3) with the same navigation.
Samples
This section shows how many sample directories are registered and how many files have been indexed. Browse existing paths with Up/Down. Press A to open a file browser and add a new sample directory. Press D to remove the selected path. Cagire indexes audio files (wav, mp3, ogg, flac, aac, m4a) from all registered paths.
Sample directories must be added here before you can use the sample browser or reference samples in your scripts.
Audio Controls
A few keys work from anywhere on the Engine page:
h — Hush. Silence all audio immediately.p — Panic. Hard stop, clears all active voices, stop all patterns.t — Test tone. Plays a brief sine wave to verify audio output.r — Reset the peak voice counter.Shift+r — Restart the audio engine.
Monitoring
The right column displays a live overview of the engine state. Everything here is read-only.
- VU Meters — Left and right channel levels with horizontal bars and dB readouts. Green below -12 dB, yellow approaching 0 dB, red above.
- Status — CPU load (with bar graph), active voices and peak count, scheduled events, schedule depth, nudge offset, sample rate, audio host, and Link peers (when connected).
- Scope — An oscilloscope showing the current audio output waveform.
Options
The Options page (F3) gathers display and onboarding settings in one place. Navigate with Up/Down or Tab, change values with Left/Right. All changes are saved automatically. A description line appears below the focused option to explain what it does.
Display
| Option | Values | Description |
|---|
| Theme | (cycle) | Color scheme for the entire interface |
| Hue rotation | 0–360° | Shift all theme colors by a hue angle (±5° per step) |
| Refresh rate | 60 / 30 / 15 fps | Lower values reduce CPU usage |
| Runtime highlight | on / off | Highlight executed code spans during playback |
| Show scope | on / off | Oscilloscope on the main view |
| Show spectrum | on / off | Spectrum analyzer on the main view |
| Show lissajous | on / off | XY stereo phase scope |
| Gain boost | 1x – 16x | Amplify scope and lissajous waveforms |
| Normalize | on / off | Auto-scale visualizations to fill the display |
| Completion | on / off | Word completion popup in the editor |
| Show preview | on / off | Step script preview on the sequencer grid |
| Performance mode | on / off | Hide header and footer bars |
| Font | 6x13 – 10x20 | Bitmap font size (plugin mode only) |
| Zoom | 50% – 200% | Interface zoom factor (plugin mode only) |
| Window | (presets) | Window size presets (plugin mode only) |
Onboarding
- Reset guides — Re-enable all dismissed guide popups.
- Demo on startup — Load a rotating demo song on fresh startup.
Saving & Loading
Press s to save a project or l to load one. These keys work from any view. Both open a file browser where you navigate your filesystem, pick a location, and confirm.
The File Browser
The browser shows a path input at the top and a directory listing below. Hidden files are filtered out.
Up / Down — Browse entriesRight — Enter a directoryLeft — Go up to the parent directoryTab — Autocomplete the current pathEnter — Confirm selectionEsc — Cancel
You can also type directly into the path input. Characters are appended to the current path, and the listing updates as you type. Press Backspace to delete.
Saving
When saving, type a filename and press Enter. Parent directories are created automatically if they don't exist. Projects are stored as .cagire files.
Loading
When loading, browse to a .cagire file and press Enter. The project replaces the current session entirely.
The Sample Browser
Press Tab on the sequencer grid to open the sample browser. It appears as a side panel showing a tree of all your sample directories and files. Press Tab again to close it. Before using the browser, you need to register at least one sample directory on the Engine page (F6). Cagire indexes audio files (.wav, .mp3, .ogg, .flac, .aac, .m4a) from all registered paths.
Browsing
The browser displays folders and files in a tree structure.
Up / Down — Move through the listRight — Expand a folder or play a fileLeft — Collapse a folderEnter — Play the selected filePageUp / PageDown — Scroll quickly
When you play a sample, it sounds immediately. This makes it easy to audition sounds before writing them into a script.
Search
Press / to activate the search filter. Type to narrow results by name. Press Esc to clear the filter and see everything again.
Using Samples in Scripts
Sample folder names become words you can use in your Forth scripts. If you have a folder called kick, you can write:
kick sound .
See the Samples section in the Audio Engine documentation for details on how sample playback works.
About Forth
Forth is a stack-based programming language created by Charles H. Moore in the early 1970s. It was designed with simplicity, directness, and interactive exploration in mind. Forth has been used for scientific work and embedded systems: it controlled telescopes and even ran on hardware aboard space missions. It evolved into many implementations targeting various architectures, but none of them really caught on. Nonetheless, the ideas behind Forth continue to attract people from very different, often unrelated fields. Today, Forth languages are used by hackers and artists for their unconventional nature. Forth is simple, direct, and beautiful to implement. Forth is an elegant, minimal language, easy to understand, extend, and tailor to a specific task. The Forth we use in Cagire is specialized in making live music. It is used as a DSL: a Domain Specific Language.
TLDR: Forth is a really nice language to play music with.
Why Forth?
Most programming languages rely on a complex syntax of variables, expressions and statements like x = 3 + 4 or do_something(()=>bob(4)). Forth works differently. It has almost no syntax at all. Instead, you push values onto a stack and apply words that transform them:
3 4 + print
The program above leaves the number 7 on the stack. There are no variables, no parentheses, no syntax to remember. You just end up with words and numbers separated by spaces. For live coding music, this directness is quite exciting. All you do is think in terms of transformations and add things to the stack: take a note, shift it up, add reverb, play it.
The Stack
The stack is where values live. When you type a number, it goes on the stack. When you type a word, it usually takes values off and puts new ones back.
3 ;; stack: 3
4 ;; stack: 3 4
+ ;; stack: 7
print
The stack is last-in, first-out. The most recent value is always on top. This means that it's often better to read Forth programs from right to left, bottom to top.
Words
Everything in Forth is either a number or a word. Words are like functions but conceptually simpler. They have no arguments or return values in the traditional sense. They just manipulate the stack directly.
dup ;; duplicate the top value
drop ;; discard the top value
swap ;; swap the top two values
Words compose naturally on the stack. To double a number:
;; 3 3 +
3 dup + print
Forth has a large vocabulary, so Cagire includes a Dictionary directly in the application. You can also create your own words. They will work just like existing words. The only difference is that these words will not be included in the dictionary. There are good reasons to create new words on-the-fly:
- To make synth definitions.
- To abstract some piece of code that you use frequently.
- To share data and processes between different steps.
Values
Four basic types of values can live on the stack:
- Integers:
42, -7, 0 - Floats:
0.5, 3.14, -1.0 - Strings:
"kick", "hello" - Quotations:
( dup + ) (code as data)
Floats can omit the leading zero: .25 is the same as 0.25, and -.5 is -0.5.
Parentheses are used to "quote" a section of a program. The code inside does not run immediately — it is pushed onto the stack as a value. A quotation only runs when a consuming word decides to execute it. This is how conditionals and loops work:
( 60 note 0.3 verb ) 1 ?
Here ? pops the quotation and the condition. The code inside runs only when the condition is truthy. Words like ?, !?, times, cycle, choose, ifelse, every, chance, and apply all consume quotations this way.
Because parentheses defer execution, wrapping code in ( ... ) without a consuming word means it never runs. Quotations are transparent to sound and parameter words — they stay on the stack untouched. This is a useful trick for temporarily disabling part of a step:
( 0.5 gain ) ;; this quotation is ignored
"kick" sound
0.3 decay
.
Any word that is not recognized as a built-in or a user definition becomes a string on the stack. This means kick snd and "kick" snd are equivalent. You only need quotes when the string contains spaces or when it conflicts with an existing word name.
The Command Register
Traditional Forth programs print text to a terminal. Cagire's Forth builds sound commands instead. This happens through an internal accumulator called the command register. The command register has two parts:
- a sound name (what instrument to play)
- a list of parameters (how to play it)
Three kinds of words interact with it:
kick sound ;; sets the sound name
0.5 gain ;; adds a parameter
. ;; emits the command and clears the register
The word sound (or its shorthand snd) sets what sound to play. Parameter words like gain, freq, decay, or verb add key-value pairs to the register. Nothing happens until you emit with . (dot). At that moment, the register is packaged into a command and sent to the audio engine.
This design lets you build sounds incrementally:
"sine" sound
c4 note
0.5 gain
0.3 decay
0.4 verb
.
Each line adds something to the register. The final . triggers the sound. You can also write it all on one line:
"sine" snd c4 note 0.5 gain 0.3 decay 0.4 verb .
The order of parameters does not matter. You can even emit multiple times in a single step. If you need to discard the register without emitting, use clear:
"kick" snd 0.5 gain clear ;; nothing plays, register is emptied
"hat" snd . ;; only the hat plays
This is useful when conditionals might cancel a sound before it emits.
The Dictionary
Cagire includes a built-in dictionary of all the internal Forth words. Press Ctrl+Up to reach the Dict view.
Using the Dictionary
The dictionary shows every available word organized by category:
- Stack: Manipulation words like
dup, swap, drop. - Arithmetic: Math operations.
- Sound: Sound sources and emission.
- Filter, Envelope, Effects: Sound shaping.
- MIDI: External MIDI control (
chan, cc, emit, clock, etc.). - Context: Sequencer state like
step, beat, tempo. - And many more...
This documentation will not teach you how to use all words. It would be counterproductive to do so and it would defeat the purpose of having a dictionary. The syntax is very uniform and you can quickly learn a new word when necessary. We encourage you to explore as you play, this is the best way to learn. The tutorial will remain focused on various topics that require you to apply knowledge to a given task or specific context.
Navigation
| Key | Action |
|---|
Tab | Switch between categories and words |
↑/↓ or j/k | Navigate items |
PgUp/PgDn | Page through lists |
/ or Ctrl+F | Search |
Esc | Clear search |
Each word entry shows:
- Name and aliases
- Stack effect:
( before -- after ) - Description: What the word does
- Example: How to use it
Press / to search across all words. The search matches word names, aliases, and descriptions. Press Esc to clear and return to browsing. Use the dictionary while writing scripts to check stack effects and study their behavior. Some words also come with shorter aliases (e.g., sound → snd). You will learn aliases quite naturally, because aliases are usually reserved for very common words.
The Stack
In most languages, you store values in variables and pass them to functions. Forth has a single shared workspace instead: the stack. You put values on it, words take them off and put results back. Everything flows through the stack. There is no assignment, no named arguments — just a sequence of values waiting to be consumed. Learning to think in terms of the stack is the one skill that makes everything else in Forth click.
Pushing Values
When you type a number or a string, it goes on top of the stack:
| Input | Stack (top on right) |
|---|
3 | 3 |
4 | 3 4 |
5 | 3 4 5 |
The stack grows to the right. The rightmost value is the top.
Words Consume and Produce
Words take values from the top and push results back. The + word pops two numbers and pushes their sum:
This is why Forth uses postfix notation: operands come first, then the operator.
Stack Notation
Documentation describes what words do using stack effect notation:
( before -- after )
The word + has the effect ( a b -- sum ). It takes two values and leaves one. The word dup has the effect ( a -- a a ). It takes one value and leaves two.
Thinking in Stack
The key to Forth is learning to visualize the stack as you write. Consider this program:
| Input | Stack | What happens |
|---|
3 | 3 | Push 3 |
4 | 3 4 | Push 4 |
+ | 7 | Add them |
2 | 7 2 | Push 2 |
* | 14 | Multiply |
This computes (3 + 4) * 2. The parentheses are implicit in the order of operations. You can use line breaks and whitespace to keep organized, and the editor can show the stack state as you type — press Ctrl+S to toggle it.
Rearranging Values
Sometimes you need values in a different order. Stack manipulation words like dup, swap, drop, and over let you shuffle things around. You will find them in the dictionary. Here is a common pattern. You want to use a value twice:
The word dup duplicates the top, so 3 dup + doubles the number.
Another pattern. You have two values but need them swapped:
| Input | Stack |
|---|
3 | 3 |
4 | 3 4 |
swap | 4 3 |
- | 1 |
Without swap, 3 4 - would compute 3 - 4 = -1. With swap, you get 4 - 3 = 1.
Stack Errors
Two things can go wrong with the stack:
- Stack underflow happens when a word needs more values than the stack has. If you write
+ with only one number on the stack, there is nothing to add. The script stops with an error.
3 + ;; error: stack underflow
The fix is simple: make sure you push enough values before calling a word. Check the stack effect in the dictionary if you are unsure.
- Leftover values are the opposite problem: values remain on the stack after your script finishes. This is less critical but indicates sloppy code. If your script leaves unused values behind, you probably made a mistake somewhere.
3 4 5 + ;; 3 is still on the stack, unconsumed
The 3 was never used. Either it should not be there, or you forgot a word that consumes it.
Creating Words
One of Forth's most powerful features is the ability to define new words. A word definition gives a name to a sequence of operations. Once defined, you can use the new word just like any built-in word.
The Syntax
Use : to start a definition and ; to end it:
: double dup + ;
This creates a word called double that duplicates the top value and adds it to itself. Now you can use it:
3 double print ;; leaves 6 on the stack
The definition is simple: everything between : and ; becomes the body of the word.
Definitions Are Shared
When you define a word in one step, it becomes available to all other steps. This is how you share code across your pattern. Define your synths, rhythms, and utilities once, then use them everywhere.
Step 0:
: bass "saw" snd 0.8 gain 800 lpf ;
Step 4:
c2 note bass .
Step 8:
g2 note bass .
The bass word carries the sound design. Each step just adds a note and plays.
Redefining Words
You can redefine any word, including built-in ones:
: dup drop ;
Now dup does the opposite of what it used to do. This is powerful but dangerous. Redefining core words can break things in subtle ways.
You can even redefine numbers:
: 2 4 ;
Now 2 pushes 4 onto the stack. The number two no longer exists in your session. This is a classic Forth demonstration: nothing is sacred, everything can be redefined.
Removing Words
forget removes a user-defined word from the dictionary:
: double dup + ;
3 double ;; 6
"double" forget
3 double ;; error: unknown word
This only affects words you defined with : ... ;. Built-in words cannot be forgotten.
Practical Uses
Synth definitions save you from repeating sound design:
: pad "sine" snd 0.3 gain 2 attack 0.5 verb ;
Transpositions and musical helpers:
: octup 12 + ;
: octdn 12 - ;
Words That Emit
A word can contain . to emit sounds directly:
: kick "kick" snd . ;
: hat "hat" snd 0.4 gain . ;
Then a step becomes trivial:
kick hat
Two sounds, two words, no clutter.
Stack Effects
When you create a word, think about what it expects on the stack and what it leaves behind. The word double expects one number and leaves one number. The word kick expects nothing and leaves nothing (it emits a sound as a side effect). Well-designed words have clear stack effects. This makes them easy to combine.
Control Flow
Control flow in Cagire's Forth comes in two families. The first is compiled syntax — if/then and case — which the compiler handles directly as branch instructions. The second is quotation words — ?, !?, ifelse, select, apply — which pop ( ... ) quotations from the stack and decide whether to run them. Probability and periodic execution (chance, every, bjork) are covered in the Randomness tutorial.
Branching with if / else / then
Push a condition, then if. Everything between if and then runs only when the condition is truthy:
;; degrade sound if true
coin if
7 crush
then
sine sound
c4 note
1 decay
.
The crush is applied on half the hits. The sound always plays. Add else for a two-way split:
coin if
c5 note
else
c3 note
then
saw sound
0.3 verb
0.5 decay
0.6 gain
.
These are compiled directly into branch instructions — they will not appear in the dictionary. This is a "low level" way to use conditionals in Cagire.
Matching with case
For matching a value against several options. Cleaner than a chain of ifs when you have more than two branches:
1 8 rand 4 mod case
0 of c3 note endof
1 of e3 note endof
2 of g3 note endof
3 of a3 note endof
endcase
tri s
2 fm 0.99 fmh
0.6 gain 0.2 chorus
1 decay
800 lpf
.
A different root note each time the pattern loops. The last line before endcase is the default — it runs when no of matched:
iter 3 mod case
0 of 0.9 gain endof
0.4 gain
endcase
saw s
.5 decay
c4 note
.
Like if/then, case is compiled syntax and does not appear in the dictionary.
Quotation Words
The remaining control flow words operate on quotations — ( ... ) blocks sitting on the stack. Each word pops one or more quotations and decides whether or how to execute them.
? and !?
? executes a quotation if the condition is truthy:
( 0.4 verb 6 crush ) coin ?
tri sound 2 fm 0.5 fmh
c3 note 0.5 gain 2 decay
.
Reverb on half the hits. !? is the opposite — executes when falsy:
( 0.5 delay 0.9 delayfeedback ) coin !?
saw sound
c4 note
500 lpf
0.5 decay
0.5 gain
.
Quiet on half the hits. These pair well with chance and fill from the Randomness tutorial.
ifelse
Two quotations, one condition. The true branch comes first:
( c3 note ) ( c5 note ) coin ifelse
saw sound 0.3 verb
0.5 decay 0.6 gain
.
Reads naturally: "c3 or c5, depending on the coin."
( 0.8 gain ) ( 0.3 gain ) fill ifelse
tri snd c4 note 0.2 decay .
Loud during fills, quiet otherwise.
select
Choose the nth quotation from a list. The index is 0-based:
( c4 ) ( e4 ) ( g4 ) ( b4 ) 0 3 rand select
note sine snd 0.5 decay .
Four notes of a major seventh chord picked randomly. Note that this is unnecessarily complex :)
apply
When you have a quotation and want to execute it unconditionally:
( dup + ) apply
Pops the quotation and runs it. Simpler than ? when there is no condition to check.
More!
For probability gates, periodic execution, and euclidean rhythms, see the Randomness tutorial. For generators and ranges, see the Generators tutorial.
Preludes
Cagire has two levels of prelude: a project prelude shared by all banks, and bank preludes that travel with each bank.
Bank Prelude
Each bank can carry its own prelude script. Press p to open the current bank's prelude editor. Press Esc to save, evaluate, and close.
Bank preludes make banks self-contained. When you share a bank, its prelude travels with it — recipients get all the definitions they need without merging anything into their own project.
: bass pulse sound 0.8 gain 400 8000 0.01 0.3 0.5 0.3 env lpf 0.6 width . ;
: pad sine sound 0.5 gain 2 spread 1.5 attack 0.4 verb . ;
Every step in that bank can now use bass and pad. Share the bank and the recipient gets these definitions automatically.
Project Prelude
The project prelude is a global script shared across all banks. Press P (Shift+p) to open it. Use it for truly project-wide definitions, variables, and settings that every bank should see.
c2 !root
0 !mode
42 seed
Evaluation Order
When preludes are evaluated (on playback start, project load, or pressing d):
- Project prelude runs first
- Bank 0 prelude runs next (if non-empty)
- Bank 1 prelude, then Bank 2, ... up to Bank 31
Only non-empty bank preludes are evaluated. Last-evaluated wins for name collisions — a bank prelude can override a project-level definition.
Keybindings
| Key | Action |
|---|
p | Open current bank's prelude editor |
P | Open project prelude editor |
d | Re-evaluate all preludes (project + all banks) |
Naming Your Sounds
The most common use of a bank prelude is to define words for your instruments. Without a prelude, every step that plays a bass has to spell out the full sound design:
pulse sound c2 note 0.8 gain 400 8000 0.01 0.3 0.5 0.3 env lpf 0.6 width .
In the bank prelude, define it once:
: bass pulse sound 0.8 gain 400 8000 0.01 0.3 0.5 0.3 env lpf 0.6 width . ;
Now every step just writes c2 note bass. Change the sound in one place, every step follows.
Building a Vocabulary
;; instruments
: bass pulse sound 0.8 gain 400 8000 0.01 0.3 0.5 0.3 env lpf 0.6 width . ;
: pad sine sound 0.5 gain 2 spread 1.5 attack 0.4 verb . ;
: lead tri sound 0.6 gain 5000 lpf 2 decay . ;
;; musical helpers
: octup 12 + ;
: octdn 12 - ;
: quiet 0.3 gain ;
: loud 0.9 gain ;
Steps become expressive and short. The prelude carries the design decisions; steps carry the composition.
Setting Initial State
The project prelude is the right place for global state:
c2 !root
0 !mode
42 seed
Every step can then read @root and @mode. And 42 seed makes randomness reproducible.
When Preludes Run
Preludes evaluate at three moments:
- When you press Space to start playback
- When you load a project
- When you press d manually
They run once at these moments, not on every step. If you edit a prelude while playing, press d to push changes into the running session.
What Not to Put Here
Preludes have no access to sequencer state. Words like step, beat, iter, and phase are meaningless here because no step is playing yet. Use preludes for definitions and setup, not for logic that depends on timing. Preludes also should not emit sounds — any . calls here would fire before the sequencer clock is running.
Cagire's Forth VS Classic Forth
Cagire's Forth is not a classic Forth. It borrows the core ideas (stack-based evaluation, postfix notation, word definitions) but adds modern features and domain-specific extensions. If you know traditional Forth, here are the differences.
Comments
Classic Forth uses parentheses for comments:
( this is a comment )
In Cagire, parentheses create quotations, so comments use double semicolons instead:
;; this is a comment
Everything after ;; until the end of the line is ignored.
Conditionals
Classic Forth uses IF ... ELSE ... THEN:
x 0 > IF 1 ELSE -1 THEN
Cagire supports this syntax but also provides quotation-based conditionals:
( 1 ) ( -1 ) x 0 > ifelse
The words ? and !? execute a quotation based on a condition:
( "kick" snd . ) coin ? ;; execute if coin is 1
( "snare" snd . ) coin !? ;; execute if coin is 0
Strings
Classic Forth has limited string support. You print strings with .":
." Hello World"
Cagire has first-class strings:
"hello"
This pushes a string value onto the stack. Strings are used for sound names, sample names, and variable keys. You often do not need quotes at all. Any unrecognized word becomes a string automatically:
kick snd . ;; "kick" is not a word, so it becomes the string "kick"
myweirdname ;; pushes "myweirdname" onto the stack
This makes scripts cleaner. You only need quotes when the string contains spaces or conflicts with a real word.
Variables
Classic Forth declares variables explicitly:
VARIABLE x
10 x !
x @
Cagire uses prefix syntax:
10 !x ;; store 10 in x
@x ;; fetch x (returns 0 if undefined)
10 ,x ;; store 10 in x, keep on stack
No declaration needed. Variables spring into existence when you store to them. ,x stores and keeps the value on the stack.
Floating Point
Classic Forth (in its original form) has no floating point. Numbers are integers. Floating point was added later as an optional extension with separate words. Cagire has native floating point:
3.14159
0.5 0.3 + ;; 0.8
Integers and floats mix freely. Division always produces a float.
Loops
Classic Forth has DO ... LOOP:
10 0 DO I . LOOP
Cagire uses a quotation-based loop with times:
4 ( @i . ) times ;; prints 0 1 2 3
The loop counter is stored in the variable i, accessed with @i. This fits Cagire's style where control flow uses quotations.
4 ( @i 4 / at hat snd . ) times ;; hat at 0, 0.25, 0.5, 0.75
4 ( c4 @i + note sine snd . ) times ;; ascending notes
For generating sequences without side effects, use .. or gen:
1 5 .. ;; pushes 1 2 3 4 5
( dup * ) 4 gen ;; pushes 0 1 4 9 (squares)
The Command Register
This is completely unique to Cagire. Traditional Forth programs print text. Cagire programs build sound commands.
The command register accumulates a sound name and parameters:
"sine" sound ;; set sound
440 freq ;; add parameter
0.5 gain ;; add parameter
. ;; emit and clear
Nothing is sent to the audio engine until you emit with .. This is unlike any classic Forth.
Context Words
Cagire provides words that read the current sequencer state:
step ;; current step index (0-127)
beat ;; current beat position
iter ;; pattern iteration count
tempo ;; current BPM
phase ;; position in bar (0-1)
These have no equivalent in classic Forth. They connect your script to the sequencer's timeline.
Probability
Classic Forth is deterministic. Cagire has built-in randomness:
( "snare" snd . ) 50 prob ;; 50% chance
( "clap" snd . ) 0.25 chance ;; 25% chance
( "hat" snd . ) often ;; 75% chance
( "rim" snd . ) sometimes ;; 50% chance
( "tom" snd . ) rarely ;; 25% chance
These words take a quotation and execute it probabilistically.
Periodic Execution
Execute a quotation on specific iterations:
( "snare" snd . ) 4 every ;; every 4th pattern iteration
( "hat" snd . ) 3 8 bjork ;; Euclidean: 3 hits across 8 step runs
( "hat" snd . ) 5 8 pbjork ;; Euclidean: 5 hits across 8 pattern iterations
every checks the pattern iteration count. On iteration 0, 4, 8, 12... the quotation runs. On all other iterations it is skipped.
bjork and pbjork use Bjorklund's algorithm to distribute k hits as evenly as possible across n positions. bjork counts by step runs, pbjork counts by pattern iterations. Classic Euclidean rhythms: tresillo (3,8), cinquillo (5,8), son clave (5,16).
Polyphonic Parameters
Parameter words like note, freq, and gain consume the entire stack. If you push multiple values before a param word, you get polyphony:
60 64 67 note sine snd . ;; emits 3 voices with notes 60, 64, 67
This works for any param and for the sound word itself:
440 880 freq sine tri snd . ;; 2 voices: sine at 440, tri at 880
When params have different lengths, shorter lists cycle:
60 64 67 note ;; 3 notes
0.5 1.0 gain ;; 2 gains (cycles: 0.5, 1.0, 0.5)
sine snd . ;; emits 3 voices
Polyphony multiplies with at deltas:
0 0.5 at ;; 2 time points
60 64 note ;; 2 notes
sine snd . ;; emits 4 voices (2 notes × 2 times)
Summary
Cagire's Forth is a domain-specific language for music. It keeps Forth's elegance (stack, postfix, definitions) but adapts it for live coding.
Introduction
Cagire includes an audio engine called Doux. No external software is needed to make sound. Doux is an opinionated, semi-modular synthesizer. It was designed for live coding environments and works by receiving command strings that describe sounds. Despite its fixed architecture,Doux is extremely versatile and will likely cover most of the audio needs of a live coder.
How It Works
When you write a Forth script and emit (.), the script produces a command string. This command travels to the audio engine, which interprets it and creates a voice. The voice plays until its envelope finishes or until it is killed by another voice. You can also spawn infinite voices, but you will need to manage their lifecycle manually, otherwise they will never stop.
saw snd c4 note 0.8 gain 0.3 verb .
Voices
Each emit (.) creates or manages a voice by sending parameters. Voices are independent sound generators with their own oscillator, envelope, and effects. The engine can run many voices at once (up to 128, default 32). When you exceed the voice limit, the oldest voice is stolen (a process called round robin scheduling). You can monitor voice usage on the Engine page:
- Active voices: how many are playing right now.
- Peak voices: the maximum reached since last reset.
Press r on the Engine page to reset the peak counter.
Parameters
After selecting a sound source, you add parameters. Each parameter word takes a value from the stack and stores it in the command register:
saw snd
c4 note ;; pitch
0.5 gain ;; volume
0.1 attack ;; envelope attack time
2000 lpf ;; lowpass filter at 2kHz
0.3 verb ;; reverb mix
.
Parameters can appear in any order. They accumulate until you emit. You can clear the register using the clear word.
Global Parameters
Use all to apply parameters globally. Global parameters persist across all patterns and steps until cleared with noall. They work both prospectively (before sounds) and retroactively (after sounds):
;; Prospective: set params before emitting
500 lpf 0.5 verb all
kick snd 60 note . ;; gets lpf=500 verb=0.5
hat snd 70 note . ;; gets lpf=500 verb=0.5
;; Retroactive: patch already-emitted sounds
kick snd 60 note .
hat snd 70 note .
500 lpf 0.5 verb all ;; both outputs get lpf and verb
Per-sound parameters override global ones:
500 lpf all
kick snd 2000 lpf . ;; lpf=2000 (per-sound wins)
hat snd . ;; lpf=500 (global)
Use noall to clear global parameters:
500 lpf all
kick snd . ;; gets lpf
noall
hat snd . ;; no lpf
Controlling Existing Voices
You can emit without a sound name. In this case, no new voice is created. Instead, the parameters are sent to control an existing voice. Use voice with an ID to target a specific voice:
0 voice 500 freq . ;; change frequency on voice 0
This is useful for modulating long-running or infinite voices. Set up a drone on one step with a known voice ID, then tweak its parameters from other steps.
Hush and Panic
Two emergency controls exist on the Engine page:
h - Hush: gracefully fade out all voicesp - Panic: immediately kill all voices
Use hush when things get too loud. Use panic when things go wrong.
Settings
The audio engine can be configured through the Engine page or via command-line arguments. Settings are saved and restored between sessions.
Engine Page
Press Ctrl+Right until you reach the Engine page. Here you can see the engine status and adjust settings.
Display
The right side of the page shows visualizations:
- Scope: oscilloscope showing the audio waveform
- Spectrum: 32-band frequency analyzer
Settings
Navigate with arrow keys, adjust values with left/right:
- Output Device: where sound goes (speakers, headphones, interface).
- Input Device: what audio input source to use (microphone, line-in, etc.).
- Channels: number of output channels (2 for stereo).
- Buffer Size: audio buffer in samples (64-4096).
- Max Voices: polyphony limit (1-128, default 32).
Buffer Size
Smaller buffers mean lower latency but higher CPU load. Larger buffers are safer but feel sluggish.
| Buffer | Latency at 44.1kHz |
|---|
| 64 | ~1.5ms |
| 128 | ~3ms |
| 256 | ~6ms |
| 512 | ~12ms |
| 1024 | ~23ms |
Start with 512. Lower it if you need tighter timing. Raise it if you hear glitches.
Samples
The engine indexes audio files from your sample directories. Add directories with A, remove with D. The sample count shows how many files are indexed.
Supported Formats
- WAV (.wav)
- MP3 (.mp3)
- OGG Vorbis (.ogg)
- FLAC (.flac)
- AIFF (.aiff, .aif)
- AAC (.aac)
- M4A (.m4a)
Lazy Loading
Samples are not loaded into memory at startup. They are decoded on demand when first played. This means you can have thousands of samples indexed without using much RAM until you actually use them.
Folder Organization
The scanner looks at top-level files and one level of subdirectories:
samples/
├── kick.wav -> "kick"
├── snare.wav -> "snare"
├── hats/
│ ├── closed.wav -> "hats" n 0
│ ├── open.wav -> "hats" n 1
│ └── pedal.wav -> "hats" n 2
└── breaks/
├── amen.wav -> "breaks" n 0
└── think.wav -> "breaks" n 1
Top-level files are named by their filename (without extension). Files inside folders are sorted alphabetically and numbered starting from 0.
Playing Samples
Reference samples by name:
kick snd . ;; play kick.wav
snare snd 0.5 gain . ;; play snare at half volume
For samples in folders, use n to select which one:
hats snd 0 n . ;; play hats/closed.wav (index 0)
hats snd 1 n . ;; play hats/open.wav (index 1)
hats snd 2 n . ;; play hats/pedal.wav (index 2)
The index wraps around. If you have 3 samples and request 5 n, you get index 2 (because 5 % 3 = 2).
Sample Variations with Bank
The bank parameter lets you organize variations:
samples/
├── kick.wav -> default
├── kick_a.wav -> bank "a"
├── kick_b.wav -> bank "b"
└── kick_hard.wav -> bank "hard"
kick snd . ;; plays kick.wav
kick snd a bank . ;; plays kick_a.wav
kick snd hard bank . ;; plays kick_hard.wav
If the banked version does not exist, it falls back to the default.
Troubleshooting
- No sound: Check output device selection.
- Try the test sound (
t) on Engine page).
- Glitches/crackling: Increase buffer size, restart the Engine.
- High CPU: Reduce max voices. Disable scope/spectrum. Increase buffer size.
- Samples not found: Check sample directories on Engine page. Filenames are case-sensitive on some systems.
Sources
The audio engine provides a variety of sound sources. Use the sound word (or snd for short) to select one.
Basic Oscillators
| Name | Description |
|---|
sine | Pure sinusoid, smooth and mellow |
tri | Triangle wave, warmer than sine, naturally band-limited |
saw | Bright sawtooth with anti-aliasing, rich in harmonics |
zaw | Raw sawtooth without anti-aliasing, lo-fi character |
pulse, square | Variable-width pulse wave with anti-aliasing |
pulze, zquare | Raw pulse without anti-aliasing, 8-bit feel |
pulse and pulze respond to the pw parameter (0.0-1.0) for pulse width. At 0.5 you get a square wave.
Phase Shaping
All oscillators support phase shaping for timbral variation:
| Parameter | Range | Effect |
|---|
size | 0-256 | Phase quantization (lo-fi, chiptune). |
mult | 0.25-16 | Phase multiplier (harmonic overtones). |
warp | -1 to 1 | Power curve asymmetry. |
mirror | 0-1 | Phase reflection point. |
These are super useful to get the most out of your oscillators.
Sub Oscillator
Add a sub oscillator layer to any basic oscillator:
| Parameter | Range | Effect |
|---|
sub | 0-1 | Mix level |
suboct | 1-3 | Octaves below main. |
subwave | tri/sine/square | Sub waveform. |
Noise
| Name | Description |
|---|
white | Equal energy across all frequencies, bright and hissy. |
pink | -3dB/octave rolloff, equal energy per octave, natural. |
brown | -6dB/octave rolloff, deep rumbling, random walk. |
Noise sources ignore pitch. Use filters to shape the spectrum.
Live Input
| Name | Description |
|---|
live, livein, mic | Live audio input from microphone or line-in |
All filter and effect parameters apply to the input signal.
Additive
| Name | Description |
|---|
add | Stacks 1-32 sine partials with spectral tilt, even/odd morph, harmonic stretching, phase shaping. |
| Parameter | Controls |
|---|
harmonics | Harmonic content / structure. |
timbre | Brightness / tonal color. |
morph | Smooth transitions between variations. |
partials | Number of active harmonics (1-32). |
Percussion
Native drum synthesis with timbral morphing. All share wave (waveform: 0=sine, 0.5=tri, 1=saw), morph, harmonics, and timbre parameters.
| Name | Description |
|---|
kick | Bass drum. |
snare | Snare drum with tone/noise balance. |
hat | Hi-hat. |
tom | Tom drum. |
rim | Rimshot. |
cowbell | Cowbell. |
cymbal | Cymbal. |
Samples
The sample source plays audio files from disk with pitch tracking.
Loading Samples
There are two ways to load samples:
- From the app: Navigate to the Engine view and find the Samples section. Press
A to open a file browser, then select a folder containing your samples. Press D to remove the last added path.
- From the command line: Use the
-s flag when launching Cagire:
cagire -s ~/samples -s ~/more-samples
The engine scans these directories and builds a registry of available samples. Samples load in the background without blocking audio. Supported file formats are .wav, .mp3, .ogg, .flac and .aiff.
Folder Organization
samples/
├── kick/ → "kick"
│ └── kick.wav
├── snare/ → "snare"
│ └── snare.wav
└── hats/
├── closed.wav → "hats" n 0
├── open.wav → "hats" n 1
└── pedal.wav → "hats" n 2
Folders at the root of your sample directory become sample banks named after the folder. Each file within a folder gets an index. Files are sorted alphabetically and assigned indices starting from 0.
Playing Samples
kick sound . ( play kick sample )
hats sound 2 n . ( play third hat sample )
snare sound 0.5 speed . ( play snare at half speed )
Parameters
| Parameter | Range | Description |
|---|
n | 0+ | Sample index within a folder (wraps around) |
begin | 0-1 | Playback start position |
end | 0-1 | Playback end position |
slice | 1+ | Divide sample into N equal slices |
pick | 0+ | Select which slice to play (0-indexed, wraps) |
speed | any | Playback speed multiplier |
stretch | 0+ | Time-stretch factor (pitch-independent) |
freq | Hz | Base frequency for pitch tracking |
fit | seconds | Stretch/compress sample to fit duration |
cut | 0+ | Choke group |
Slicing with Begin/End
The begin and end parameters define what portion of the sample plays. Values are normalized from 0 (start) to 1 (end).
kick sound 0.25 begin 0.75 end . ( play middle half )
kick sound 0.5 begin . ( play second half )
kick sound 0.5 end . ( play first half )
If begin is greater than end, they swap automatically.
Slice and Pick
For evenly-spaced slicing, slice divides the sample into N equal parts and pick selects which one (0-indexed, wraps around).
break sound 8 slice 3 pick . ( play the 4th eighth of the sample )
break sound 16 slice step pick . ( scan through 16 slices by step )
Combine with fit to time-stretch each slice to a target duration. fit accounts for the sliced range automatically.
break sound 4 slice 2 pick 1 loop . ( quarter of the sample, fitted to 1 beat )
Speed and Pitch
The speed parameter affects both tempo and pitch. A speed of 2 plays twice as fast and an octave higher.
snare sound 2 speed . ( double speed, octave up )
snare sound 0.5 speed . ( half speed, octave down )
snare sound -1 speed . ( play in reverse )
For pitched playback, use freq or note names. The sample's base frequency defaults to middle C (261.626 Hz).
kick sound 440 freq . ( play at A4 )
kick sound c4 . ( play at C4 )
Negative speed will reverse the sample and play it backwards.
crow sound -1 speed . ( play backwards at nominal speed )
crow sound -4 speed . ( play backwards, 4 times faster )
Time Stretching
The stretch parameter changes sample duration without affecting pitch, using a phase vocoder algorithm. This contrasts with speed, which changes both tempo and pitch together.
kick sound 2 stretch . ( twice as long, same pitch )
kick sound 0.5 stretch . ( half as long, same pitch )
kick sound 0 stretch . ( freeze — holds at current position )
Combine with slice and pick for pitch-locked breakbeat manipulation:
break sound 8 slice step pick 2 stretch . ( sliced break, stretched x2, original pitch )
Reverse playback is not available with stretch — use speed for that.
Fitting to Duration
The fit parameter stretches or compresses a sample to match a target duration in seconds. This adjusts speed automatically.
kick sound 0.25 fit . ( fit kick into quarter second )
snare sound beat fit . ( fit snare to one beat )
Choke Groups
The cut parameter assigns a sample to a choke group. When a new sample with the same cut value plays, it kills any currently playing samples in that group.
hihat_closed sound 1 cut . ( choke group 1 )
hihat_open sound 1 cut . ( kills closed hat, starts open )
This is essential for realistic hi-hat behavior where open and closed hats shouldn't overlap.
Bank Variations
Add _suffix to sample names to create variations that share the same base name.
samples/
├── kick.wav
├── kick_hard.wav
├── kick_soft.wav
Select variations with the bank parameter:
kick sound . ( plays kick.wav )
kick sound hard bank . ( plays kick_hard.wav )
kick sound soft bank . ( plays kick_soft.wav )
Wavetables
Any sample can be played as a wavetable. When you use the scan parameter, the sample automatically becomes a pitched oscillator that can morph between cycles.
What is a Wavetable?
A wavetable is a series of single-cycle waveforms stored end-to-end in an audio file. Each cycle is a complete waveform that starts and ends at zero crossing, allowing it to loop seamlessly at any pitch.
Sample: [cycle 0][cycle 1][cycle 2][cycle 3]
↑ ↑
scan 0 scan 1
The oscillator reads through one cycle at audio rate (determining pitch), while scan selects which cycle to play. Values between cycles crossfade smoothly, creating timbral morphing.
Basic Usage
Just add scan to any sample and it becomes a wavetable:
pad 0 scan . ( play pad as wavetable, first cycle )
pad 0.5 scan . ( blend to middle cycles )
pad 440 freq 0 scan . ( play at A4 )
Without scan, the sample plays normally. With scan, it becomes a looping wavetable oscillator.
Parameters
| Parameter | Range | Description |
|---|
scan | 0-1 | Position in wavetable (0 = first cycle, 1 = last) |
wtlen | samples | Cycle length in samples (0 = entire sample) |
Cycle Length
The wtlen parameter tells the engine how many samples make up one cycle. This must match how the wavetable was created, otherwise you'll hear the wrong pitch or glitchy artifacts.
pad 0 scan 2048 wtlen . ( Serum-style 2048-sample cycles )
pad 0 scan 1024 wtlen . ( 1024-sample cycles )
Common cycle lengths are powers of two: 256, 512, 1024, 2048. Serum uses 2048 samples per cycle. The number of cycles in a wavetable is the total sample length divided by wtlen. If wtlen is 0 (default), the entire sample is treated as one cycle. The sample still loops as a pitched oscillator, but scan has no morphing effect since there's only one cycle.
Scanning
The scan parameter selects which cycle to play:
pad 0 scan . ( first cycle only )
pad 0.5 scan . ( blend between middle cycles )
pad 1 scan . ( last cycle only )
Scan Modulation
Use audio-rate modulation words to automate the scan position:
pad 0 1 2 lfo scan . ( sine LFO, full range, 2 Hz )
pad 0 0.5 1 tlfo scan . ( triangle LFO, half range, 1 Hz )
pad 0 1 0.5 jit scan . ( random scan every 0.5 steps )
Creating Wavetables
A proper wavetable file:
- Contains multiple single-cycle waveforms of identical length
- Each cycle starts and ends at zero crossing for seamless looping
- Uses power-of-two cycle lengths (256, 512, 1024, 2048)
- Has cycles that morph smoothly from one to the next
You can find wavetable packs online or create your own in tools like Serum, WaveEdit, or Audacity (using zero-crossing snap). Single-cycle waveforms also work. With wtlen set to 0, a single-cycle sample becomes a basic pitched oscillator.
Filters
Filters attenuate frequencies above or below a cutoff point.
Lowpass Filter
The lowpass filter (lpf) attenuates frequencies above the cutoff.
saw 1000 lpf . ( cut above 1000 Hz )
saw 500 lpf 0.8 lpq . ( with resonance )
| Parameter | Range | Description |
|---|
lpf | Hz | Cutoff frequency |
lpq | 0-1 | Resonance (peak at cutoff) |
Highpass Filter
The highpass filter (hpf) attenuates frequencies below the cutoff.
kick 200 hpf . ( cut below 200 Hz )
pad 400 hpf 0.3 hpq . ( with resonance )
| Parameter | Range | Description |
|---|
hpf | Hz | Cutoff frequency |
hpq | 0-1 | Resonance |
Bandpass Filter
The bandpass filter (bpf) attenuates frequencies outside a band around the center frequency.
noise 1000 bpf 0.7 bpq . ( narrow band around 1000 Hz )
| Parameter | Range | Description |
|---|
bpf | Hz | Center frequency |
bpq | 0-1 | Resonance (narrower band) |
Filter Slope
The ftype parameter sets the filter slope (rolloff steepness).
| Value | Slope |
|---|
1 | 12 dB/octave |
2 | 24 dB/octave (default) |
3 | 48 dB/octave |
saw 800 lpf 3 ftype . ( 48 dB/oct lowpass )
Filter Envelope Modulation
Use the env word to apply a DAHDSR envelope to any filter cutoff:
saw 200 8000 0.01 0.3 0.5 0.3 env lpf . ( cutoff sweeps from 200 to 8000 Hz )
The same works for highpass and bandpass: env hpf, env bpf.
Ladder Filters
Ladder filters use a different algorithm (Moog-style) with self-oscillation at high resonance.
saw 800 llpf 0.7 llpq . ( ladder lowpass )
saw 300 lhpf 0.5 lhpq . ( ladder highpass )
saw 1000 lbpf 0.8 lbpq . ( ladder bandpass )
| Parameter | Range | Description |
|---|
llpf | Hz | Ladder lowpass cutoff |
llpq | 0-1 | Ladder lowpass resonance |
lhpf | Hz | Ladder highpass cutoff |
lhpq | 0-1 | Ladder highpass resonance |
lbpf | Hz | Ladder bandpass cutoff |
lbpq | 0-1 | Ladder bandpass resonance |
Ladder filter cutoffs can also be modulated with env, lfo, slide, etc.
EQ
The 3-band EQ applies shelf and peak filters at fixed frequencies.
kick 3 eqlo -2 eqhi . ( +3 dB at 200 Hz, -2 dB at 5000 Hz )
snare 2 eqmid . ( +2 dB at 1000 Hz )
| Parameter | Frequency | Type |
|---|
eqlo | 200 Hz | Low shelf (dB) |
eqmid | 1000 Hz | Peak (dB) |
eqhi | 5000 Hz | High shelf (dB) |
Tilt EQ
Tilt EQ applies a high shelf at 800 Hz with up to ±6 dB gain.
pad -0.5 tilt . ( -3 dB above 800 Hz )
hat 0.5 tilt . ( +3 dB above 800 Hz )
| Parameter | Range | Description |
|---|
tilt | -1 to 1 | High shelf gain (-1 = -6 dB, 0 = flat, 1 = +6 dB) |
Modulation
Modulation effects vary parameters over time using LFOs or envelopes.
Vibrato
Vibrato modulates pitch with an LFO.
saw 5 vib 0.5 vibmod . ( 5 Hz, 0.5 semitone depth )
| Parameter | Range | Description |
|---|
vib | Hz | LFO rate |
vibmod | semitones | Modulation depth |
vibshape | shape | LFO waveform (sine, tri, saw, square) |
FM Synthesis
FM modulates the carrier frequency with a modulator oscillator.
sine 440 freq 2 fm 2 fmh . ( modulator at 2× carrier frequency )
| Parameter | Range | Description |
|---|
fm | 0+ | Modulation index |
fmh | ratio | Harmonic ratio (modulator / carrier) |
fmshape | shape | Modulator waveform |
Use env to apply a DAHDSR envelope to FM depth: 0 5 0.01 0.1 0.3 0.5 env fm.
Amplitude Modulation
AM multiplies the signal by an LFO.
pad 4 am 0.5 amdepth . ( 4 Hz tremolo )
| Parameter | Range | Description |
|---|
am | Hz | LFO rate |
amdepth | 0-1 | Modulation depth |
amshape | shape | LFO waveform |
Ring Modulation
Ring modulation multiplies two signals, producing sum and difference frequencies.
saw 150 rm 0.8 rmdepth . ( ring mod at 150 Hz )
| Parameter | Range | Description |
|---|
rm | Hz | Modulator frequency |
rmdepth | 0-1 | Modulation depth |
rmshape | shape | Modulator waveform |
Phaser
Phaser sweeps notches through the frequency spectrum using allpass filters.
pad 0.5 phaser 0.6 phaserdepth .
| Parameter | Range | Description |
|---|
phaser | Hz | Sweep rate |
phaserdepth | 0-1 | Sweep depth |
phasersweep | cents | Sweep range |
phasercenter | Hz | Center frequency |
Flanger
Flanger mixes the signal with a short modulated delay (0.5-10ms).
pad 0.3 flanger 0.7 flangerdepth 0.5 flangerfeedback .
| Parameter | Range | Description |
|---|
flanger | Hz | Modulation rate |
flangerdepth | 0-1 | Modulation depth |
flangerfeedback | 0-0.95 | Feedback amount |
Chorus
Chorus uses multiple modulated delay lines with 120° phase offset for stereo width.
pad 1 chorus 0.4 chorusdepth 20 chorusdelay .
| Parameter | Range | Description |
|---|
chorus | Hz | Modulation rate |
chorusdepth | 0-1 | Modulation depth |
chorusdelay | ms | Base delay time |
Distortion
Distortion effects add harmonics by nonlinearly shaping the waveform.
Saturation
Soft saturation using the transfer function x / (1 + k|x|).
saw 2 distort .
saw 8 distort 0.5 distortvol . ( with volume compensation )
| Parameter | Range | Description |
|---|
distort | 0+ | Saturation amount |
distortvol | 0-1 | Output volume |
Wavefolding
Wavefolding reflects the signal when it exceeds ±1, using sin(x × amount × π/2).
sine 4 fold .
| Parameter | Range | Description |
|---|
fold | 0+ | Fold amount |
Wavewrapping
Wavewrapping applies modulo to wrap the signal into the -1 to 1 range.
saw 3 wrap .
| Parameter | Range | Description |
|---|
wrap | 0+ | Number of wraps |
Bit Crushing
Bit crushing quantizes the signal to fewer amplitude levels.
snare 6 crush . ( 6-bit = 32 levels )
| Parameter | Range | Description |
|---|
crush | 1-16 | Bit depth |
Sample Rate Reduction
Sample rate reduction holds each sample for multiple output samples.
hat 4 coarse . ( 1/4 effective sample rate )
| Parameter | Range | Description |
|---|
coarse | 1+ | Reduction factor (1 = bypass) |
Space & Time
Spatial effects position sounds in the stereo field and add depth through delays and reverbs.
Pan
Pan positions a sound in the stereo field.
hat -0.5 pan . ( slightly left )
perc 1 pan . ( hard right )
kick 0 pan . ( center )
| Parameter | Range | Description |
|---|
pan | -1 to 1 | Stereo position (-1 = left, 0 = center, 1 = right) |
Width
Width controls the stereo spread using mid-side processing.
pad 1.5 width . ( wider stereo )
pad 0 width . ( mono )
| Parameter | Range | Description |
|---|
width | 0+ | Stereo width (0 = mono, 1 = unchanged, 2 = exaggerated) |
Haas Effect
The Haas effect delays one channel slightly, creating a sense of stereo width and spatial placement.
snare 15 haas . ( 15ms delay on right channel )
| Parameter | Range | Description |
|---|
haas | ms | Delay time (1-10ms = subtle width, 10-35ms = distinct echo) |
Delay
Delay is a send effect that creates echoes. The delay parameter sets how much signal is sent to the delay bus.
snare 0.3 delay 0.25 delaytime 0.5 delayfeedback .
| Parameter | Range | Description |
|---|
delay | 0-1 | Send level |
delaytime | seconds | Delay time |
delayfeedback | 0-0.95 | Feedback amount |
delaytype | type | Delay algorithm |
Delay Types
| Type | Description |
|---|
standard | Clean digital repeats |
pingpong | Bounces between left and right |
tape | Each repeat gets darker (analog warmth) |
multitap | 4 taps with swing control via feedback |
snare 0.4 delay pingpong delaytype .
pad 0.3 delay tape delaytype .
Reverb
Reverb is a send effect that simulates acoustic spaces. The verb parameter sets the send level.
snare 0.2 verb 2 verbdecay .
pad 0.4 verb 4 verbdecay 0.7 verbdamp .
| Parameter | Range | Description |
|---|
verb | 0-1 | Send level |
verbdecay | seconds | Reverb tail length |
verbdamp | 0-1 | High frequency damping |
verbpredelay | ms | Initial delay before reverb |
verbdiff | 0-1 | Diffusion (smears transients) |
verbtype | type | Reverb algorithm |
Reverb Types
| Type | Description |
|---|
dattorro | Plate reverb, bright and metallic shimmer |
fdn | Hall reverb, dense and smooth |
snare 0.3 verb dattorro verbtype . ( plate )
pad 0.5 verb fdn verbtype . ( hall )
Comb Filter
The comb filter creates resonant pitched delays, useful for Karplus-Strong string synthesis and metallic tones.
white 0.5 comb 220 combfreq 0.9 combfeedback . ( plucked string )
| Parameter | Range | Description |
|---|
comb | 0-1 | Send level |
combfreq | Hz | Resonant frequency |
combfeedback | 0-0.99 | Feedback (higher = longer decay) |
combdamp | 0-1 | High frequency damping |
Higher feedback creates longer, ringing tones. Add damping for more natural string-like decay.
Audio-Rate Modulation
Any parameter can be modulated continuously using modulation words. Instead of a fixed value, these words produce a modulation string that the engine interprets as a moving signal.
All time values are in steps, just like attack, decay, and release. At 120 BPM with speed 1, one step is 0.125 seconds. Writing 4 lfo means a 4-step period.
LFOs
Oscillate a parameter between two values.
saw snd 200 4000 4 lfo lpf . ( sweep filter over 4 steps )
saw snd 0.3 0.7 2 tlfo pan . ( triangle pan over 2 steps )
| Word | Shape | Output |
|---|
lfo | Sine | min~max:period |
tlfo | Triangle | min~max:periodt |
wlfo | Sawtooth | min~max:periodw |
qlfo | Square | min~max:periodq |
Stack effect: ( min max period -- str )
Slides
Transition from one value to another over a duration.
saw snd 0 1 0.5 slide gain . ( fade in over half a step )
saw snd 200 4000 8 sslide lpf . ( smooth sweep over 8 steps )
| Word | Curve | Output |
|---|
slide | Linear | start>end:dur |
expslide | Exponential | start>end:dure |
sslide | Smooth (S-curve) | start>end:durs |
Stack effect: ( start end dur -- str )
Random
Randomize a parameter within a range, retriggering at a given period.
saw snd 200 4000 2 jit lpf . ( new random value every 2 steps )
saw snd 200 4000 2 sjit lpf . ( same but smoothly interpolated )
saw snd 200 4000 1 drunk lpf . ( random walk, each step )
| Word | Behavior | Output |
|---|
jit | Sample & hold | min?max:period |
sjit | Smooth interpolation | min?max:periods |
drunk | Random walk | min?max:periodd |
Stack effect: ( min max period -- str )
Envelope Modulation
Apply an envelope to any parameter. The env word is the complete form: it sweeps from min to max following a full attack, decay, sustain, release shape. All times are in steps.
saw snd 200 8000 0.01 0.1 0.5 0.3 env lpf .
Stack effect: ( min max attack decay sustain release -- str )
This is the building block. From it, three shorthands drop the parameters you don't need:
| Word | Stack | What it does |
|---|
env | ( min max a d s r -- str ) | Full envelope (attack, decay, sustain, release) |
eadr | ( min max a d r -- str ) | No sustain (sustain = 0) |
ead | ( min max a d -- str ) | Percussive (sustain = 0, release = 0) |
eadsr is an alias for env.
saw snd 200 8000 0.01 0.3 ead lpf . ( percussive filter pluck )
saw snd 0 5 0.01 0.1 0.3 eadr fm . ( FM depth with release tail )
saw snd 200 8000 0.01 0.1 0.5 0.3 env lpf . ( full ADSR on filter )
These work on any parameter — lpf, fm, gain, pan, freq, anything that accepts a value.
Low Pass Gate
The lpg word couples the amplitude envelope with a lowpass filter. Set your amp envelope first with ad or adsr, then lpg mirrors it to lpf.
saw snd 0.01 0.1 ad 200 8000 1 lpg . ( percussive LPG )
saw snd 0.01 0.1 0.5 0.3 adsr 200 4000 1 lpg . ( sustained LPG )
Stack effect: ( min max depth -- )
min/max — filter frequency range in Hzdepth — 0 to 1, scales the filter range (1 = full, 0.5 = halfway)
saw snd 0.01 0.5 ad 200 8000 0.3 lpg . ( subtle LPG, filter barely opens )
lpg reads attack, decay, sustain, and release from the current sound. If none are set, it defaults to a short percussive shape.
Combining
Modulation words return strings, so they compose naturally with the rest of the language. Use them anywhere a parameter value is expected.
saw snd
200 4000 4 lfo lpf
0.3 0.7 8 tlfo pan
0 1 0.01 0.1 ead gain
.
Words & Sounds
Word definitions let you abstract sound design into reusable units.
Defining Sounds
: lead "saw" snd 0.3 gain 1200 lpf ;
Use it with different notes:
c4 note lead .
e4 note lead .
Self-Contained Words
Include the emit to make the word play directly:
: kk "kick" snd 1 decay . ;
: hh "hihat" snd 0.5 gain 0.5 decay . ;
Steps become simple:
kk
0.5 at hh
Effect Presets
: dark 800 lpf 0.6 lpq ;
: wet 0.7 verb 8 verbdiff ;
c4 note saw snd dark wet .
MIDI
Cagire speaks MIDI. You can send notes, control changes, and other messages to external synthesizers, drum machines, and DAWs. You can also read incoming control change values from MIDI controllers and use them to modulate your scripts.
Device Slots
Cagire provides four input slots and four output slots, numbered 0 through 3. Each slot can connect to one MIDI device. By default, slot 0 is used for both input and output.
Configuration
Configure your MIDI devices in the Options view. Select input and output devices for each slot. Changes take effect immediately.
MIDI vs Audio
The audio engine (Doux) and MIDI are independent systems. Use . to emit audio commands, use m. to emit MIDI messages. You can use both in the same script:
saw snd c4 note 0.5 gain . ;; audio
60 note 100 velocity m. ;; MIDI
MIDI is useful when you want to sequence external gear, layer Cagire with hardware synths, or integrate into a larger studio setup. The audio engine is self-contained and needs no external equipment.
Clock and Transport
Cagire can send MIDI clock and transport messages to synchronize external gear. Use mclock to send a single clock pulse, and mstart, mstop, mcont for transport control. MIDI clock requires 24 pulses per quarter note, so you need to call mclock at the appropriate rate for your tempo.
MIDI Output
Send MIDI messages using the m. word. Build up parameters on the stack, then emit. The system determines message type based on which parameters you set.
Note Messages
The default message type is a note. Set note and velocity, then emit:
60 note 100 velocity m. ;; middle C, velocity 100
c4 note 80 velocity m. ;; same pitch, lower velocity
| Parameter | Stack | Range | Description |
|---|
note | (n --) | 0-127 | MIDI note number |
velocity | (n --) | 0-127 | Note velocity |
chan | (n --) | 1-16 | MIDI channel |
dur | (f --) | steps | Note duration |
dev | (n --) | 0-3 | Output device slot |
Duration (dur) is measured in steps. If not set, the note plays until the next step. Channel defaults to 1, device defaults to 0.
Control Change
Set both ccnum (controller number) and ccout (value) to send a CC message:
74 ccnum 64 ccout m. ;; CC 74, value 64
1 ccnum 127 ccout m. ;; mod wheel full
| Parameter | Stack | Range | Description |
|---|
ccnum | (n --) | 0-127 | Controller number |
ccout | (n --) | 0-127 | Controller value |
Pitch Bend
Set bend to send pitch bend. The range is -1.0 (full down) to 1.0 (full up), with 0.0 as center:
0.5 bend m. ;; bend up halfway
-1.0 bend m. ;; full bend down
Channel Pressure
Set pressure to send channel aftertouch:
64 pressure m. ;; medium pressure
Program Change
Set program to send a program change message:
0 program m. ;; select program 0
127 program m. ;; select program 127
Message Priority
When multiple message types are set, only one is sent per emit. Priority order:
- Control Change (if
ccnum AND ccout set) - Pitch Bend
- Channel Pressure
- Program Change
- Note (default)
To send multiple message types, use multiple emits:
74 ccnum 100 ccout m. ;; CC first
60 note 100 velocity m. ;; then note
Real-Time Messages
Transport and clock messages for external synchronization:
| Word | Description |
|---|
mclock | Send MIDI clock pulse |
mstart | Send MIDI start |
mstop | Send MIDI stop |
mcont | Send MIDI continue |
These ignore all parameters and send immediately.
MIDI Input
Read incoming MIDI control change values with the ccval word. This lets you use hardware controllers to modulate parameters in your scripts.
Reading CC Values
The ccval word takes a CC number and channel from the stack, and returns the last received value:
1 1 ccval ;; read CC 1 (mod wheel) on channel 1
Stack effect: (cc chan -- val)
The returned value is 0-127. If no message has been received for that CC/channel combination, the value is 0.
Device Selection
Use dev to select which input device slot to read from:
1 dev 1 1 ccval ;; read from device slot 1
Device defaults to 0 if not specified.
Practical Examples
Map a controller knob to filter cutoff:
74 1 ccval 127 / 200 2740 range lpf
Use mod wheel for vibrato depth:
1 1 ccval 127 / 0 0.5 range vibdepth
Crossfade between two sounds:
1 1 ccval 127 / ;; normalize to 0.0-1.0
dup saw snd swap gain .
1 swap - tri snd swap gain .
Scaling Values
CC values are integers 0-127. Normalize to 0.0-1.0 first, then use range to scale:
;; normalize to 0.0-1.0
74 1 ccval 127 /
;; scale to custom range (e.g., 200-4000)
74 1 ccval 127 / 200 4000 range
;; bipolar range (-1.0 to 1.0)
74 1 ccval 127 / -1 1 range
The range word takes a normalized value (0.0-1.0) and scales it to your target range: (val min max -- scaled).
Latency
CC values are sampled at the start of each step. Changes during a step take effect on the next step. For smoothest results, turn knobs slowly or use higher step rates.
Randomness
Music needs surprise. A pattern that plays identically every time gets boring fast. Cagire has a rich set of words for injecting randomness and controlled variation into your sequences.
Random Numbers
coin pushes 0 or 1 with equal probability:
;; sometimes, reverb
sine sound
( 0.5 verb ) coin ?
1 decay
.
rand takes a range and returns a random value. If both bounds are integers, the result is an integer. If either is a float, you get a float:
60 72 rand note sine snd 0.5 decay . ;; random MIDI note from 60 to 72
0.3 0.9 rand gain sine snd 0.5 decay . ;; random gain between 0.3 and 0.9
exprand and logrand give you weighted distributions. exprand is biased toward the low end, logrand toward the high end:
200.0 8000.0 exprand freq sine snd 0.5 decay . ;; mostly low frequencies
200.0 8000.0 logrand freq sine snd 0.5 decay . ;; mostly high frequencies
These are useful for parameters where perception is logarithmic, like frequency and duration.
Conditional Execution
The probability words take a quotation and execute it with some chance. chance takes a float from 0.0 to 1.0, prob takes a percentage from 0 to 100:
( hat snd . ) 0.25 chance ;; 25% chance
( kick snd . ) 75 prob ;; 75% chance
Named probability words save you from remembering numbers:
| Word | Probability |
|---|
always | 100% |
almostAlways | 90% |
often | 75% |
sometimes | 50% |
rarely | 25% |
almostNever | 10% |
never | 0% |
( hat snd . ) often ;; 75%
( snare snd . ) sometimes ;; 50%
( clap snd . ) rarely ;; 25%
always and never are useful when you want to temporarily mute or unmute a voice without deleting code. Change sometimes to never to silence it, always to bring it back.
Use ? and !? with coin for quick coin-flip decisions:
( hat snd . ) coin ? ;; execute if coin is 1
( rim snd . ) coin !? ;; execute if coin is 0
Selection
choose picks randomly from n items on the stack:
kick snare hat 3 choose snd . ;; random drum hit
60 64 67 72 4 choose note sine snd 0.5 decay . ;; random note from a set
When a chosen item is a quotation, it gets executed:
( 0.1 decay ) ( 0.5 decay ) ( 0.9 decay ) 3 choose
sine snd .
wchoose lets you assign weights to each option. Push value/weight pairs:
kick 0.5 snare 0.3 hat 0.2 3 wchoose snd .
Kick plays 50% of the time, snare 30%, hat 20%. Weights don't need to sum to 1 -- they're normalized automatically.
shuffle randomizes the order of n items on the stack:
60 64 67 72 4 shuffle ;; stack now has the same 4 values in random order
Combined with note, this gives you a random permutation of a chord every time the step runs.
Periodic Execution
every runs a quotation once every n pattern iterations:
( crash snd . ) 4 every ;; crash cymbal every 4th iteration
except is the inverse -- it runs a quotation on all iterations *except* every nth:
( 2 distort ) 4 except ;; distort on all iterations except every 4th
every+ and except+ take an extra offset argument to shift the phase:
( snare snd . ) 4 2 every+ ;; fires at iter 2, 6, 10, 14...
( snare snd . ) 4 2 except+ ;; skips at iter 2, 6, 10, 14...
Without the offset, every fires at 0, 4, 8... The offset shifts that by 2, so it fires at 2, 6, 10... This lets you interleave patterns that share the same period:
( kick snd . ) 4 every ;; kick at 0, 4, 8...
( snare snd . ) 4 2 every+ ;; snare at 2, 6, 10...
bjork and pbjork use Bjorklund's algorithm to distribute k hits across n positions as evenly as possible. Classic Euclidean rhythms:
( hat snd . ) 3 8 bjork ;; tresillo: x..x..x. (by step runs)
( hat snd . ) 5 8 pbjork ;; cinquillo: x.xx.xx. (by pattern iterations)
bjork counts by step runs (how many times this particular step has played). pbjork counts by pattern iterations. Some classic patterns:
| k | n | Name |
|---|
| 3 | 8 | tresillo |
| 5 | 8 | cinquillo |
| 5 | 16 | bossa nova |
| 7 | 16 | samba |
Seeding
By default, every run produces different random values. Use seed to make randomness reproducible:
42 seed
60 72 rand note sine snd . ;; always the same "random" note
The seed is set at the start of the script. Same seed, same sequence. Useful when you want a specific random pattern to repeat.
Combining Words
The real power comes from mixing techniques. A hi-hat pattern with ghost notes:
hat snd
( 0.3 0.6 rand gain ) ( 0.8 gain ) 2 cycle
.
Full volume on even runs, random quiet on odd runs.
A bass line that changes every 4 bars:
( c2 note ) ( e2 note ) ( g2 note ) ( a2 note ) 4 pcycle
( 0.5 decay ) often
sine snd .
Layered percussion with different densities:
( kick snd . ) always
( snare snd . ) 2 every
( hat snd . ) 5 8 bjork
( rim snd . ) rarely
A melodic step with weighted note selection and random timbre:
c4 0.4 e4 0.3 g4 0.2 b4 0.1 4 wchoose note
0.3 0.7 rand decay
1.0 4.0 exprand harmonics
add snd .
The root note plays most often. Higher chord tones are rarer. Decay and harmonics vary continuously.
Notes & Harmony
This tutorial covers everything pitch-related: notes, intervals, chords, voicings, transposition, scales, and diatonic harmony. Each section builds on the previous one.
Notes
A note name followed by an octave number compiles to a MIDI integer:
c4 note sine snd .
That plays middle C (MIDI 60). a4 is concert A (69), e3 is 52. Sharps use s or #, flats use b:
fs4 note 0.5 decay saw snd .
eb4 note 0.8 decay tri snd .
fs4 and f#4 both mean F sharp 4 (MIDI 66). bb3 is B flat 3 (58). Octave range is -1 to 9.
Notes are just integers. They work anywhere an integer works — you can do arithmetic on them, store them in variables, pass them to any word that expects a number.
Intervals
An interval duplicates the top of the stack and adds semitones. Stack two intervals to build a chord by hand:
c4 M3 P5 note 1.5 decay sine snd .
That builds a C major triad from scratch: C4 (60), then a major third above (64), then a perfect fifth above the root (67). Three notes on the stack, all played together.
a3 m3 P5 note 1.2 decay saw snd .
A minor triad: A3, C4, E4.
Simple intervals (within one octave):
| Interval | Semitones | Name |
|---|
P1 / unison | 0 | Perfect unison |
m2 | 1 | Minor 2nd |
M2 | 2 | Major 2nd |
m3 | 3 | Minor 3rd |
M3 | 4 | Major 3rd |
P4 | 5 | Perfect 4th |
aug4 / dim5 / tritone | 6 | Tritone |
P5 | 7 | Perfect 5th |
m6 | 8 | Minor 6th |
M6 | 9 | Major 6th |
m7 | 10 | Minor 7th |
M7 | 11 | Major 7th |
P8 | 12 | Octave |
Compound intervals (beyond one octave):
| Interval | Semitones |
|---|
m9 | 13 |
M9 | 14 |
m10 | 15 |
M10 | 16 |
P11 | 17 |
aug11 | 18 |
P12 | 19 |
m13 | 20 |
M13 | 21 |
m14 | 22 |
M14 | 23 |
P15 | 24 |
Custom voicings with wide intervals:
c3 P5 P8 M10 note 1.5 decay sine snd .
C3, G3, C4, E4 — an open-voiced C major spread across two octaves.
Chords
Chord words replace a root note with all the chord tones. They're shortcuts for what intervals do manually:
c4 maj note 1.5 decay sine snd .
That's the same C major triad, but in one word instead of M3 P5. A few more:
d3 min7 note 1.5 decay saw snd .
e3 dom9 note 1.2 decay saw snd .
a3 sus2 note 1.5 decay tri snd .
Common triads:
| Word | Intervals |
|---|
maj | 0 4 7 |
m | 0 3 7 |
dim | 0 3 6 |
aug | 0 4 8 |
sus2 | 0 2 7 |
sus4 | 0 5 7 |
pwr | 0 7 |
Common seventh chords:
| Word | Intervals |
|---|
maj7 | 0 4 7 11 |
min7 | 0 3 7 10 |
dom7 | 0 4 7 10 |
dim7 | 0 3 6 9 |
m7b5 | 0 3 6 10 |
minmaj7 | 0 3 7 11 |
aug7 | 0 4 8 10 |
augmaj7 | 0 4 8 11 |
7sus4 | 0 5 7 10 |
Extended, add, altered, and other chord types are listed in the Reference section at the end.
Voicings
Four words reshape chord voicings without changing the harmony.
inv moves the bottom note up an octave (inversion):
c4 maj inv note 1.5 decay sine snd .
The root C goes up, giving E4 G4 C5 — first inversion. Apply it twice for second inversion:
c4 maj inv inv note 1.5 decay sine snd .
G4 C5 E5. dinv does the opposite — moves the top note down an octave:
c4 maj dinv note 1.5 decay sine snd .
G3 C4 E4. The fifth drops below the root.
drop2 and drop3 are jazz voicing techniques for four-note chords. drop2 takes the second-from-top note and drops it an octave:
c4 maj7 drop2 note 1.2 decay saw snd .
From C4 E4 G4 B4, the G drops to G3: G3 C4 E4 B4. drop3 drops the third-from-top:
c4 maj7 drop3 note 1.2 decay saw snd .
E drops to E3: E3 C4 G4 B4. These create wider, more open voicings common in jazz guitar and piano.
Transposition
tp shifts every note on the stack by N semitones:
c4 maj 3 tp note 1.5 decay sine snd .
C major transposed up 3 semitones becomes Eb major. Works with any number of notes:
c4 min7 -2 tp note 1.5 decay saw snd .
Shifts the whole chord down 2 semitones (Bb minor 7).
oct shifts a single note by octaves:
c4 1 oct note 0.3 decay sine snd .
C5 (one octave up). Useful for bass lines:
0 2 4 5 7 5 4 2 8 cycle minor note
-2 oct 0.8 gain sine snd .
Scales
Scale words convert a degree index into a MIDI note. By default the root is C4 (MIDI 60):
0 major note 0.5 decay sine snd .
Degree 0 of the major scale: C4. Degrees wrap with octave transposition — degree 7 gives C5 (72), degree -1 gives B3 (59).
Walk through a scale with cycle:
0 1 2 3 4 5 6 7 8 cycle minor note 0.5 decay sine snd .
Random notes from a scale:
0 7 rand pentatonic note 0.8 decay saw snd .
Setting the key
By default scales are rooted at C4. Use key! to change the tonal center:
g3 key! 0 major note 0.5 decay sine snd .
Now degree 0 is G3 (55) instead of C4. The key persists across steps until changed again:
a3 key! 0 3 5 7 3 cycle minor note 0.8 decay tri snd .
A minor melody starting from A3.
Common modes:
| Word | Pattern |
|---|
major | 0 2 4 5 7 9 11 |
minor | 0 2 3 5 7 8 10 |
dorian | 0 2 3 5 7 9 10 |
phrygian | 0 1 3 5 7 8 10 |
lydian | 0 2 4 6 7 9 11 |
mixolydian | 0 2 4 5 7 9 10 |
pentatonic | 0 2 4 7 9 |
minpent | 0 3 5 7 10 |
blues | 0 3 5 6 7 10 |
harmonicminor | 0 2 3 5 7 8 11 |
melodicminor | 0 2 3 5 7 9 11 |
Jazz, symmetric, and modal variant scales are listed in the Reference section.
Diatonic Harmony
triad and seventh build chords from scale degrees. Instead of specifying a chord type, you get whatever chord the scale produces at that degree:
0 major triad note 1.5 decay sine snd .
Degree 0 of the major scale, stacked in thirds: C E G — a major triad. The scale determines the chord quality automatically. Degree 1 gives D F A (minor), degree 4 gives G B D (major):
4 major triad note 1.5 decay sine snd .
seventh adds a fourth note:
0 major seventh note 1.2 decay saw snd .
C E G B — Cmaj7. Degree 1 gives Dm7, degree 4 gives G7 (dominant). The diatonic context determines everything.
Combine with key! to play diatonic chords in any key:
g3 key! 0 major triad note 1.5 decay sine snd .
G major triad rooted at G3.
A I-vi-IV-V chord progression using pcycle:
( 0 major seventh ) ( 5 major seventh )
( 3 major seventh ) ( 4 major seventh ) 4 pcycle
note 1.2 decay saw snd .
Combine with voicings for smoother voice leading:
( 0 major seventh ) ( 5 major seventh inv )
( 3 major seventh ) ( 4 major seventh drop2 ) 4 pcycle
note 1.5 decay saw snd .
Arpeggiate diatonic chords using arp (see the *Timing with at* tutorial for details on arp):
0 major seventh arp note 0.5 decay sine snd .
Frequency Conversion
mtof converts a MIDI note to frequency in Hz. ftom does the reverse:
c4 mtof freq sine snd .
Useful when a synth parameter expects Hz rather than MIDI.
Reference
All Chords
Triads:
| Word | Intervals |
|---|
maj | 0 4 7 |
m | 0 3 7 |
dim | 0 3 6 |
aug | 0 4 8 |
sus2 | 0 2 7 |
sus4 | 0 5 7 |
pwr | 0 7 |
Seventh chords:
| Word | Intervals |
|---|
maj7 | 0 4 7 11 |
min7 | 0 3 7 10 |
dom7 | 0 4 7 10 |
dim7 | 0 3 6 9 |
m7b5 | 0 3 6 10 |
minmaj7 | 0 3 7 11 |
aug7 | 0 4 8 10 |
augmaj7 | 0 4 8 11 |
7sus4 | 0 5 7 10 |
Sixth chords:
| Word | Intervals |
|---|
maj6 | 0 4 7 9 |
min6 | 0 3 7 9 |
maj69 | 0 4 7 9 14 |
min69 | 0 3 7 9 14 |
Extended chords:
| Word | Intervals |
|---|
dom9 | 0 4 7 10 14 |
maj9 | 0 4 7 11 14 |
min9 | 0 3 7 10 14 |
9sus4 | 0 5 7 10 14 |
dom11 | 0 4 7 10 14 17 |
maj11 | 0 4 7 11 14 17 |
min11 | 0 3 7 10 14 17 |
dom13 | 0 4 7 10 14 21 |
maj13 | 0 4 7 11 14 21 |
min13 | 0 3 7 10 14 21 |
Add chords:
| Word | Intervals |
|---|
add9 | 0 4 7 14 |
add11 | 0 4 7 17 |
madd9 | 0 3 7 14 |
Altered dominants:
| Word | Intervals |
|---|
dom7b9 | 0 4 7 10 13 |
dom7s9 | 0 4 7 10 15 |
dom7b5 | 0 4 6 10 |
dom7s5 | 0 4 8 10 |
dom7s11 | 0 4 7 10 18 |
All Scales
Modes:
| Word | Pattern |
|---|
major | 0 2 4 5 7 9 11 |
minor / aeolian | 0 2 3 5 7 8 10 |
dorian | 0 2 3 5 7 9 10 |
phrygian | 0 1 3 5 7 8 10 |
lydian | 0 2 4 6 7 9 11 |
mixolydian | 0 2 4 5 7 9 10 |
locrian | 0 1 3 5 6 8 10 |
Pentatonic and blues:
| Word | Pattern |
|---|
pentatonic | 0 2 4 7 9 |
minpent | 0 3 5 7 10 |
blues | 0 3 5 6 7 10 |
Harmonic and melodic minor:
| Word | Pattern |
|---|
harmonicminor | 0 2 3 5 7 8 11 |
melodicminor | 0 2 3 5 7 9 11 |
Chromatic and whole tone:
| Word | Pattern |
|---|
chromatic | 0 1 2 3 4 5 6 7 8 9 10 11 |
wholetone | 0 2 4 6 8 10 |
Jazz / Bebop:
| Word | Pattern |
|---|
bebop | 0 2 4 5 7 9 10 11 |
bebopmaj | 0 2 4 5 7 8 9 11 |
bebopmin | 0 2 3 5 7 8 9 10 |
altered | 0 1 3 4 6 8 10 |
lyddom | 0 2 4 6 7 9 10 |
Symmetric:
| Word | Pattern |
|---|
halfwhole | 0 1 3 4 6 7 9 10 |
wholehalf | 0 2 3 5 6 8 9 11 |
augmented | 0 3 4 7 8 11 |
tritone | 0 1 4 6 7 10 |
prometheus | 0 2 4 6 9 10 |
Modal variants (from melodic minor):
| Word | Pattern |
|---|
dorianb2 | 0 1 3 5 7 9 10 |
lydianaug | 0 2 4 6 8 9 11 |
mixb6 | 0 2 4 5 7 8 10 |
locrian2 | 0 2 3 5 6 8 10 |
Generators & Sequences
Sequences of values drive music: arpeggios, parameter sweeps, rhythmic patterns. Cagire has dedicated words for building sequences on the stack, transforming them, and collapsing them to single values.
Ranges
.. pushes an integer range onto the stack. Both endpoints are inclusive. If start exceeds end, it counts down:
1 5 .. ;; 1 2 3 4 5
5 1 .. ;; 5 4 3 2 1
0 7 .. ;; 0 1 2 3 4 5 6 7
., adds a step parameter. Works with floats:
0 1 0.25 ., ;; 0 0.25 0.5 0.75 1
0 10 2 ., ;; 0 2 4 6 8 10
1 0 0.5 ., ;; 1 0.5 0 (descending)
geom.. builds a geometric sequence. Takes start, ratio, and count:
1 2 4 geom.. ;; 1 2 4 8
100 0.5 4 geom.. ;; 100 50 25 12.5
Build a harmonic series:
110 2 5 geom.. 5 rev freq
That gives you 110, 220, 440, 880, 1760 (reversed), ready to feed into freq.
Computed Sequences
gen executes a quotation n times and collects all results. The quotation must push exactly one value per call:
( 1 6 rand ) 4 gen ;; 4 random values between 1 and 6
( coin ) 8 gen ;; 8 random 0s and 1s
Contrast with times, which executes for side effects and does not collect. times sets @i to the current index:
4 ( @i ) times ;; 0 1 2 3 (pushes @i each iteration)
4 ( @i 60 + note sine snd . ) times ;; plays 4 notes, collects nothing
The distinction: gen is for building data. times is for doing things.
Euclidean Patterns
euclid distributes k hits evenly across n positions and pushes the hit indices:
3 8 euclid ;; 0 3 5
4 8 euclid ;; 0 2 4 6
5 8 euclid ;; 0 1 3 5 6
euclidrot adds a rotation parameter that shifts the pattern:
3 8 0 euclidrot ;; 0 3 5 (no rotation)
3 8 1 euclidrot ;; 1 4 6
3 8 2 euclidrot ;; 1 4 6
These give you raw indices as data on the stack. This is different from bjork and pbjork (covered in the Randomness tutorial), which execute a quotation on matching steps. euclid gives you numbers to work with; bjork triggers actions.
Transforming Sequences
Four words reshape values already on the stack. All take n (the count of items to operate on) from the top:
rev reverses order:
1 2 3 4 4 rev ;; 4 3 2 1
c4 e4 g4 3 rev ;; g4 e4 c4 (descending arpeggio)
shuffle randomizes order:
c4 e4 g4 b4 4 shuffle ;; random permutation each time
sort and rsort for ascending and descending:
3 1 4 1 5 5 sort ;; 1 1 3 4 5
3 1 4 1 5 5 rsort ;; 5 4 3 1 1
Reducing Sequences
sum and prod collapse n values into one:
1 2 3 4 4 sum ;; 10
1 2 3 4 4 prod ;; 24
Useful for computing averages or accumulating values:
( 1 6 rand ) 4 gen 4 sum ;; sum of 4 dice rolls
Replication
dupn (alias !) duplicates a value n times:
440 3 dupn ;; 440 440 440
c4 4 dupn ;; c4 c4 c4 c4
Build a drone chord -- same note, different octaves:
c3 note 0.5 gain sine snd .
c3 note 12 + 0.5 gain sine snd .
c3 note 24 + 0.3 gain sine snd .
Or replicate a value for batch processing:
0.5 4 dupn 4 sum ;; 2.0
The generator words produce raw material. The transform words shape it. Together they let you express complex musical ideas in a few words.
Timing with at
Every step has a duration. By default, sounds emit at the very start of that duration. at changes *when* within the step sounds fire -- giving you sub-step rhythmic control without adding more steps.
The Basics
at drains the entire stack and stores the values as timing offsets. Each value is a fraction of the step duration: 0 = start, 0.5 = halfway, 1.0 = next step boundary.
0.5 at kick snd . ;; kick at the midpoint
Push multiple values before calling at to get multiple emits from a single .:
0 0.5 at kick snd .
Two kicks: one at start, one at midpoint.
0 0.25 0.5 0.75 at hat snd .
Four hats, evenly spaced.
The deltas persist across multiple . calls until clear or a new at:
0 0.5 at
kick snd . ;; 2 kicks
hat snd . ;; 2 hats (same timing)
clear
snare snd . ;; 1 snare (deltas cleared)
Cross-product: at Without arp
Without arp, deltas multiply with polyphonic voices. If you have 3 notes and 2 deltas, you get 6 emits -- every note at every delta:
0 0.5 at
c4 e4 g4 note 1.5 decay sine snd .
6 emits: 3 notes x 2 deltas. A chord played twice per step.
1:1 Pairing: at With arp
arp changes the behavior. Instead of cross-product, deltas and arp values pair up 1:1. Each delta gets one note from the arpeggio:
0 0.33 0.66 at
c4 e4 g4 arp note 0.5 decay sine snd .
C4 at 0, E4 at 0.33, G4 at 0.66.
If the lists differ in length, the shorter one wraps around:
0 0.25 0.5 0.75 at
c4 e4 arp note 0.3 decay sine snd .
C4, E4, C4, E4 — the shorter list wraps to fill 4 time points.
This is THE key distinction. Without arp: every note at every time. With arp: one note per time slot.
Generating Deltas
You rarely type deltas by hand. Use generators:
Evenly spaced via .,:
0 1 0.25 ., at hat snd . ;; 0 0.25 0.5 0.75 1.0
Euclidean distribution via euclid:
3 8 euclid at hat snd . ;; 3 hats at positions 0, 3, 5
Random timing via gen:
( 0.0 1.0 rand ) 4 gen at hat snd . ;; 4 hats at random positions
Geometric spacing via geom..:
0.0 2.0 4 geom.. at hat snd . ;; exponentially spaced
Gating at
Wrap at expressions in quotations for conditional timing:
( 0 0.25 0.5 0.75 at ) 2 every
hat snd .
16th-note hats every other bar.
( 0 0.5 at ) 0.5 chance
kick snd .
50% chance of double-hit.
When the quotation doesn't execute, no deltas are set -- you get the default single emit at beat start.
Using Variables
Variables let you name values and share data between steps. They are global -- any step can read what another step wrote.
Store and Fetch
!name stores the top of the stack into a variable. @name fetches it back. Variables spring into existence when you first store to them. Fetching a variable that was never stored returns 0.
10 !x ;; store 10 in x
@x ;; pushes 10
@y ;; pushes 0 (never stored)
Store and Keep
,name stores just like !name but keeps the value on the stack. Useful when you want to name something and keep using it:
440 ,freq sine snd . ;; stores 440 in freq AND passes it to the pipeline
Without ,, you'd need dup:
440 dup !freq sine snd . ;; equivalent, but noisier
Sharing Between Steps
Variables are shared across all steps. One step can store a value that another reads:
;; step 0: pick a root note
c4 iter 7 mod + !root
;; step 4: read it
@root 7 + note sine snd .
Every time the pattern loops, step 0 picks a new root. Step 4 always harmonizes with it.
Accumulators
Fetch, modify, store back. A classic pattern for evolving values:
@n 1 + !n ;; increment n each time this step runs
@n 12 mod note sine snd . ;; cycle through 12 notes
Reset on some condition:
@n 1 + !n
( 0 !n ) @n 16 > ? ;; reset after 16
When Changes Take Effect
Within a single step, you can read back what you just wrote. But variable changes only become visible to other steps after the current step finishes executing. If step 0 writes 10 !x and step 1 reads @x, step 1 sees the value from the previous iteration of step 0, not from the current one running in parallel.
Naming Sounds
Store a sound name in a variable, reuse it across steps:
;; step 0: choose the sound
"sine" !synth
;; step 1, 2, 3...
c4 note @synth snd .
Change one step, all steps follow.
Recording
Live recording captures the master output into a sample. One word to start, the same word to stop. The result is a first-class sample you can play, slice, and layer.
rec
rec takes a name from the stack and toggles recording. Everything the engine outputs goes into a buffer until you call rec again:
"drums" rec ;; start recording
Play something -- a pattern, a live input, anything that makes sound. When you're done:
"drums" rec ;; stop recording, register sample
The recording is now available as a sample:
drums snd .
Playback
Recorded samples are ordinary samples. Everything you can do with a loaded sample works here:
drums snd 0.5 speed . ;; half speed
drums snd 0.25 begin 0.5 end . ;; slice the middle quarter
drums snd 800 lpf 0.3 verb . ;; filter and reverb
drums snd -1 speed . ;; reverse
Overdub
overdub (or dub) layers new audio on top of an existing recording. It wraps at the buffer boundary, so the loop length stays fixed:
"drums" overdub ;; start layering onto drums
Play new material over the existing content. Stop with the same call:
"drums" overdub ;; stop, register updated sample
If the target name doesn't exist yet, overdub falls back to a fresh recording.
Building Layers
Record a foundation, then overdub to build up:
;; 1. record a kick pattern
"loop" rec
;; ... play kick pattern ...
"loop" rec
;; 2. overdub hats
"loop" dub
;; ... play hat pattern ...
"loop" dub
;; 3. overdub a melody
"loop" dub
;; ... play melody ...
"loop" dub
;; 4. play the result
loop snd .
Each overdub pass adds to what's already there. The buffer wraps, so longer passes layer cyclically over the original length.
Slicing a Recording
Once you have a recording, carve it up:
loop snd 0.0 begin 0.25 end . ;; first quarter
loop snd 0.25 begin 0.5 end . ;; second quarter
loop snd 0.5 begin 0.75 end . ;; third quarter
loop snd 0.75 begin 1.0 end . ;; last quarter
Combine with randomness for variation:
loop snd
0.0 0.25 0.5 0.75 4 choose begin
0.5 speed
.
Orbit Recording
orec records a single orbit instead of the master output. Useful for capturing one layer (e.g. drums on orbit 0) without bleeding other orbits in:
"drums" 0 orec ;; start recording orbit 0
Play patterns routed to orbit 0. When done:
"drums" 0 orec ;; stop, register sample
odub overdubs onto a single orbit:
"drums" 0 odub ;; overdub orbit 0 onto "drums"
rec and overdub still record the master output as before.
Constraints
Recordings have a 60-second maximum. Recording is native only -- not available in the WASM build.
Soundfonts
General MIDI playback via SF2 soundfont files. Native only -- not available in the WASM build.
Setup
Drop an .sf2 file into one of your samples directories. The engine finds and loads it automatically when Cagire starts. Only one soundfont is active at a time.
Playing
Use gm as the sound source. The n parameter selects a program by name or number (0-127):
gm snd piano n . ;; acoustic piano
gm snd strings n c4 note . ;; strings playing middle C
gm snd 0 n e4 note . ;; program 0 (piano) playing E4
Drums
Drums live on a separate bank. Use drums or percussion as the n value. Each MIDI note triggers a different instrument:
gm snd drums n 36 note . ;; kick
gm snd drums n 38 note . ;; snare
gm snd drums n 42 note . ;; closed hi-hat
gm snd percussion n 49 note . ;; crash cymbal
Envelope
The soundfont embeds ADSR envelope data per preset. The engine applies it automatically. Override any parameter explicitly:
gm snd piano n 0.01 attack 0.3 decay .
gm snd strings n 0.5 attack 2.0 release .
If you set attack, decay, sustain, or release, your value wins. Unspecified parameters keep the soundfont default.
Effects
All standard engine parameters work on GM voices. Filter, distort, spatialize:
gm snd bass n 800 lpf 0.3 verb .
gm snd epiano n 0.5 delay 1.5 distort .
gm snd choir n 0.8 pan 2000 hpf .
Preset Names
| # | Name | # | Name | # | Name | # | Name |
|---|
| 0 | piano | 24 | guitar | 48 | strings | 72 | piccolo |
| 1 | brightpiano | 25 | steelguitar | 49 | slowstrings | 73 | flute |
| 4 | epiano | 26 | jazzguitar | 52 | choir | 74 | recorder |
| 6 | harpsichord | 27 | cleangt | 56 | trumpet | 75 | panflute |
| 7 | clavinet | 29 | overdrive | 57 | trombone | 79 | whistle |
| 8 | celesta | 30 | distgt | 58 | tuba | 80 | ocarina |
| 9 | glockenspiel | 33 | bass | 60 | horn | 81 | lead |
| 10 | musicbox | 34 | pickbass | 61 | brass | 82 | sawlead |
| 11 | vibraphone | 35 | fretless | 64 | sopranosax | 89 | pad |
| 12 | marimba | 36 | slapbass | 65 | altosax | 90 | warmpad |
| 13 | xylophone | 38 | synthbass | 66 | tenorsax | 91 | polysynth |
| 14 | bells | 40 | violin | 67 | barisax | 104 | sitar |
| 16 | organ | 41 | viola | 68 | oboe | 105 | banjo |
| 19 | churchorgan | 42 | cello | 70 | bassoon | 108 | kalimba |
| 21 | accordion | 43 | contrabass | 71 | clarinet | 114 | steeldrum |
| 22 | harmonica | 45 | pizzicato | | | | |
| | 46 | harp | | | | |
| | 47 | timpani | | | | |
Drums are on a separate bank: use drums or percussion as the n value.
Examples
A simple GM drum pattern across four steps:
;; step 1: kick
gm snd drums n 36 note .
;; step 2: closed hat
gm snd drums n 42 note 0.6 gain .
;; step 3: snare
gm snd drums n 38 note .
;; step 4: closed hat
gm snd drums n 42 note 0.6 gain .
Layer piano chords with randomness:
gm snd piano n
c4 e4 g4 3 choose note
0.3 0.8 rand gain
0.1 0.4 rand verb
.
A bass line with envelope override:
gm snd bass n
c2 e2 g2 a2 4 cycle note
0.01 attack 0.2 decay 0.0 sustain
.
Sharing
Export and import patterns or entire banks via the clipboard. Data is serialized into a compact text string you can paste anywhere -- a chat message, a text file, a note.
Export
Press g to copy the current pattern to the clipboard as a share string. The string starts with cgr: followed by compressed data.
On the patterns page, g exports a pattern or a bank depending on which column the cursor is in. Bank strings start with cgrb:.
Import
Press G to import from the clipboard. The share string is decoded and the result replaces the current pattern.
On the patterns page, importing into the banks column replaces an entire bank. Because this is destructive, a confirmation prompt appears first.
Undo
Both pattern and bank imports are covered by undo. Press u to revert an import.