Program Structure
| Component | Syntax | Purpose |
| Lua block | {...} | Define atoms, timing, effects |
| Atom stream | a=b=c=d | Sequence of sounds + rests |
| PAL action | /cmd/args/ | Patterns, tracks, playback |
| Comment | -- text | Ignored to end of line |
Atoms & Naming
| Form | Example | Notes |
| Single char | a–z | Defined in Lua: {a=saw("A4")} |
| Uppercase | A–Z | Loud variant of lowercase (redefinable) |
| Complex name | $kick | Lua: {$kick=env(...)}, stream: $kick |
| Volume (0-9) | a5, A3 | Number overrides case-based volume |
Notation Operators
| Op | Cells | Effect |
= | 1 | Rest (silence) |
_ | 2 | Long rest |
__ | 4 | Extra-long rest |
=xN | N | N cells of rest: =x16 |
ax4 | 4 | Sustain atom for 4 cells |
! or space | — | Hard cut (no morph) |
| newline | — | Hard cut |
. | — | Staccato (short note) |
~ | — | Tremolo |
| \ | — | Pitch bend up / down (+2st each, cumulative) |
^ | — | Break sustain |
| tab | — | Ignored (use for indentation) |
Transitions
| Type | Syntax | Result |
| Sustain | aaaa | One voice held 4 beats |
| Morph | abcd | Smooth glide a→b→c→d |
| Hard cut | a!b / a b | Separate attacks, no glide |
| Rest | a=b=c | Attacks with silence gaps |
Timing
{bpm=120; div=16} -- ms/cell = 240000 / (bpm × div)
| div | Note | @120 BPM | Cells/bar |
| 4 | Quarter | 500ms | 4 |
| 8 | Eighth | 250ms | 8 |
| 16 | 16th | 125ms | 16 |
| 32 | 32nd | 62.5ms | 32 |
Legacy: rate=8 = 8 beats/sec = 125ms/cell
BPM LFO Modulation
bpm = 90 + lfo_smooth_random(0.3, 15, "free") -- drift 75-105 BPM
bpm = 120 + lfo_sine(0.2, 10, "free") -- breathing 110-130
Tempo LFO: virtual clock stretches/compresses event timing. Keep depth ±10-20 BPM. Use "free" mode.
Oscillators
Frequency accepts note strings or Hz: saw("A4"), saw("C4+7"), saw(440)
| Function | Sound | Parameters |
sine(f) | Pure tone, smooth | f: Hz or note string ("A4", "C4+7") |
saw(f) | Rich harmonics, buzzy | f: Hz or note string |
square(f) | Hollow, clarinet-like | f: Hz or note string |
tri(f) | Soft, flute-like | f: Hz or note string |
pulse(f, pw) | Variable duty cycle | f: Hz/note; pw: 0.0–1.0 duty (default 0.5, LFO-modulatable) |
osc(type, f) | Generic oscillator | type: "sine", "saw", "square", "tri", etc.; f: Hz/note |
Note offsets: "C4+7" = G4, "A4-12" = A3. Sharps/flats: "F#3", "Bb4"
Noise Generators
| Function | Spectrum |
noise() / white_noise() | Flat (equal energy) |
pink_noise() | −3 dB/oct (natural) |
brown_noise() / red_noise() | −6 dB/oct (deep rumble) |
blue_noise() | +3 dB/oct (bright) |
violet_noise() | +6 dB/oct (very bright) |
Filters
| Function | Type | Parameters |
lpf(cutoff, src) | Low-pass | cutoff: Hz (LFO-modulatable) |
hpf(cutoff, src) | High-pass | cutoff: Hz |
bpf(cutoff, q, src) | Band-pass | cutoff: Hz; q: quality factor (0.1–30, higher = narrower) |
notch(cutoff, q, src) | Band-stop | cutoff: Hz; q: quality factor |
allpass(cutoff, q, src) | All-pass (phase shift) | cutoff: Hz; q: quality factor |
peak(cutoff, q, dB, src) | Parametric EQ bell | cutoff: Hz; q: bandwidth; dB: gain (+/−) |
Optional last param: key_follow (−1.0 to 1.0). 1.0 = cutoff tracks pitch.
Formant (Vowel) Filter
formant("ah", src) -- single vowel
formant("oh", 3, src) -- with tract shift (semitones)
Vowels: "ah" "eh" "ee" "oh" "oo" "ae" "uh" "er" "aw" "mm" "nn" "ng"
Envelopes
env(attack_ms, decay_ms, sustain_level, release_ms, source)
env(attack_ms, decay_ms, sustain_level, release_ms, source, curve)
curve (optional): shapes all ADSR segments. 1.0=linear (default), <1.0=convex (fast start, slow tail), >1.0=concave (slow start, fast end; approximates exponential decay). Typical: 0.5–3.0.
| Preset | A | D | S | R | Curve |
| Instant | 1 | 50 | 0.8 | 100 | |
| Pluck | 2 | 150 | 0 | 100 | |
| Pad | 500 | 800 | 0.8 | 1000 | |
| Organ | 5 | 0 | 1.0 | 50 | |
| Bell | 1 | 1500 | 0 | 2000 | 2.5 |
| Kick | 1 | 80 | 0 | 50 | |
| Snare | 1 | 60 | 0 | 40 | |
| Hi-hat | 1 | 20 | 0 | 10 | |
Mixing & Gain
mix(src1, src2, ...) -- combine sources (auto-normalizes)
gain(0.5, src) -- apply volume (0.0-1.0, LFO-modulatable)
compress(ratio, thresh_db, src) -- per-atom compressor (ratio: e.g. 4; thresh: dB e.g. -12)
Simultaneity Groups (Chords)
(abc) -- three voices triggered at once (1 cell)
(abc)x4 -- sustain chord for 4 cells
(a)x4 -- sustain single atom for 4 cells
($pat)x4 -- if $pat is a PAL pattern: repeat 4× sequentially
Distortion
| Function | Effect | Parameters |
soft_clip(drive, src) | Warm saturation | drive: 0.0–1.0 |
hard_clip(drive, src) | Aggressive clipping | drive: 0.0–1.0 |
distort(drive, type, src) | Generic distortion | drive: 0.0–1.0; type: "soft_clip", "hard_clip", etc. |
bitcrush(bits, [rate], src) | Lo-fi bit reduction | bits: 2–16; rate: 0.0–1.0 sample rate (optional, default 1.0) |
wavefold(amount, src) | Wave folding | amount: fold threshold |
Time-Based Effects
delay(ms, feedback, src) -- ms: delay time; feedback: 0.0-1.0
delay(ms, feedback, mix, src) -- mix: wet/dry 0.0-1.0
reverb(size, src) -- size: room 0.0-1.0
reverb(size, damping, wet, width, src) -- damping/wet/width: 0.0-1.0
chorus(depth, src) -- depth: 0.0-1.0
chorus(mix, delay_ms, depth_ms, rate, src)
flanger(depth, src) -- depth: 0.0-1.0
flanger(mix, depth_ms, rate, fb, src) -- fb: feedback 0.0-1.0
Master Effects
master_gain(0.25) -- output volume (SET FIRST!)
master_reverb(size, damping) -- size/damping: 0.0-1.0
master_delay(ms, feedback, mix) -- ms: time; feedback/mix: 0.0-1.0
master_limiter(threshold) -- threshold: dB (LFO-modulatable)
master_compress(ratio, threshold) -- both params LFO-modulatable
master_lpf(cutoff) -- cutoff: Hz
master_hpf(cutoff) -- cutoff: Hz
master_bpf(cutoff, q) -- cutoff: Hz; q: quality
master_notch(cutoff, q) -- cutoff: Hz; q: quality
master_peak(cutoff, q, gain_db) -- parametric EQ
master_lpf_bypass() -- disable default master LPF
master_chain("filter","delay","reverb") -- processing order
master_parallel(true) -- parallel filter+reverb
Group Effects (Bus Processing)
group("drums", {"k", "s", "h"}) -- define group
group_reverb("drums", size, damp) -- size/damp: 0.0-1.0
group_lpf("drums", cutoff) -- cutoff: Hz
group_hpf("drums", cutoff) -- cutoff: Hz
group_bpf("drums", cutoff, q) -- cutoff: Hz; q: quality
group_notch("drums", cutoff, q) -- cutoff: Hz; q: quality
group_peak("drums", cutoff, q, gain_db) -- parametric EQ
group_compress("drums", ratio, thresh) -- ratio: e.g. 4; thresh: dB
LFOs (Low Frequency Oscillators)
name = lfo_sine(rate, depth, "mode")
a = sine(440 + 20 * name) -- modulate frequency
Waveforms
| Function | Shape |
lfo_sine | Smooth sine |
lfo_square | Gating / stepped |
lfo_saw | Rising ramp |
lfo_rsaw | Falling ramp |
lfo_tri | Triangle (linear) |
lfo_random | Sample & hold |
lfo_smooth_random | Smooth random drift |
lfo_exp | Exponential (sidechain) |
Unipolar: append _uni for 0-to-depth range (e.g., lfo_sine_uni)
Trigger Modes
| Mode | Behavior |
"retrigger" | Reset phase per voice |
"free" | Continuous (global phase) |
"sync" | Tempo-synced (rate = cycles/beat) |
"oneshot" | One cycle, then hold |
"key" | Phase seeded by MIDI note |
Shape Options
lfo_square(4,1,"sync", {pulsewidth=0.25})
lfo_exp(4,1,"sync", {direction="rise", curve_amount=3})
lfo_smooth_random(0.1,1,"free", {smoothness=0.2}) -- slow drift
lfo_tri(2,1,"retrigger", {skew=0.2})
Params: pulsewidth, curve, skew, slew, smoothness (0=smooth, 1=instant), octaves, direction, curve_amount, phase
Envelope-Wrapped LFO
vib_raw = lfo_sine(6, 1.0, "retrigger")
vib = env_lfo(0, 200, 1.0, 50, vib_raw) -- fades in over 200ms
Common LFO Recipes
-- Vibrato
vib = lfo_sine(5,1,"retrigger"); a = sine(440+20*vib)
-- Filter wobble (tempo-synced)
w = lfo_sine(2,1,"sync"); a = lpf(300+1200*w, saw(55))
-- Sidechain pump
p = lfo_exp(4,1,"sync",{direction="rise",curve_amount=3})
a = gain(0.2+0.7*p, saw(110))
-- Tremolo
t = lfo_sine(4,0.3,"retrigger"); a = gain(0.5+0.3*t, sine(440))
-- Tempo drift (BPM modulation)
bpm = 90 + lfo_smooth_random(0.3,15,"free")
Expressions (Per-Note Overrides)
`atom:pitch:velocity:duration:modifier=value`
| Example | Meaning |
`a:C4` | Atom a at C4 |
`a::100` | Default pitch, velocity 100 |
`a:::2` | Duration 2 units |
`a:G3:100:1:vib=4` | G3, vel 100, 1 unit, 4Hz vibrato |
Pitch: note (C4, F#3), offset (+7, -5), Hz (440hz)
Modifiers: vib, pan, att, rel, bend, cut
PAL — Playback Actions Language
Patterns & Tracks
/pa/$kick/k===k===k===k===/ -- define pattern
/tr/@drums/+$kick/ -- track with pattern ref (+)
/tr/@drums/*$kick/ -- track with pattern copy (*)
/tr/@lead/a=b=c=d/ -- track with inline atoms
Playback & Control
| Action | Effect |
/pp/ /pp/@t/ | Play all / specific track |
/ps/ /ps/@t/ | Pause all / specific |
/st/ /st/@t/ | Stop all / specific (rewind) |
/lp/on/ /lp/off/ | Enable / disable looping |
/tp/120/ | Set tempo |
/qt/16/ | Set quantize grid |
/mu/@t/ | Mute track |
/so/@t/ | Solo track |
Arpeggio Builder
/ar/$arp/u/ceg/ -- up: c!e!g
/ar/$arp/d/ceg/ -- down: g!e!c
/ar/$arp/ud/ceg/ -- up-down (ping-pong)
/ar/$arp/r/ceg/ -- random order
/ar/$arp/u/ceg/=/ -- rested: c=e=g
/ar/$arp/u/ceg// -- morph: ceg
FM Synthesis
a = fm({carrier=440, modulator=880, index=2.5})
a = fm({carrier=440, modulator=880, index=3.0,
algorithm="feedback", feedback=0.5}) -- with algorithm
Algorithms
| Name | Routing | Character |
stack2 | Op2 → Op1 | Standard 2-op (default) |
stack3 | Op3 → Op2 → Op1 | Brass, rich chain |
stack4 | Op4 → … → Op1 | Maximum depth |
parallel | Op2+Op3 → Op1 | Dual modulator |
feedback | Op2(self) → Op1 | Sawtooth-like |
additive | Op1+2+3+4 | Organ (no mod) |
twin | (Op2→1)+(Op4→3) | Two FM pairs |
diamond | Op3,4 → Op2 → Op1 | Complex intermod |
Operator Envelopes
a = fm({carrier=330, modulator=660, index=3.5,
operators={
{ratio=1.0, level=1.0}, -- carrier: full sustain
{ratio=2.0, level=1.0, -- modulator: fast decay
attack=1, decay=250, sustain=0.05, release=80}
}})
Modulation Index
| Index | Character | Use |
| 0.1–0.5 | Subtle shimmer | Warm pads |
| 1.0–2.0 | Classic FM | E-piano, vibes |
| 3.0–5.0 | Bright, brassy | Brass, leads |
| 5.0–10.0 | Metallic | Bells, gongs |
FM Ratios
| Mod:Car | Character | Use |
| 1:1 | Saw-like | Bass, leads |
| 2:1 | Bright octave | Electric piano |
| 3:1 | Hollow | Clarinet, reed |
| 1.41:1 | Bell-like | Bells, chimes |
FM Recipes
-- DX7 Electric Piano
e = env(5,600,0.3,300, fm({carrier=330,modulator=660,index=3.5,
operators={{ratio=1,level=1},{ratio=2,level=1,
attack=1,decay=250,sustain=0.05,release=80}}}))
-- FM Bass (1:1 ratio + LPF)
b = env(2,150,0.5,100, lpf(600, fm({carrier=55,modulator=55,
index=4.0,operators={{ratio=1,level=1},{ratio=1,level=1,
attack=1,decay=80,sustain=0.2,release=50}}})))
-- FM Bell (inharmonic ratio, curve=2.5 for natural decay)
c = env(1,1500,0,800, fm({carrier=880,modulator=1241,index=5.0}), 2.5)
Carrier op sustain=1.0 (Voice env shapes volume). Modulator decay shapes timbre.
Vocal / Apparatus Synthesis
a = apparatus("ah", 120) -- phoneme at Hz
b = vocal("ee", 220) -- alias
c = apparatus("oh", 150, "jitter=1.5;breathiness=0.8")
d = apparatus("mm", 110, "breathy") -- preset
Phonemes: same as formant vowel list. Params: jitter, breathiness, tremor=RATE,DEPTH. Presets: "breathy", "pressed"
Stringed Instruments
a = stringed(440) -- generic pluck
b = stringed{freq=440, material=SILK, body=PAULOWNIA}
c = guitar(330) -- nylon, spruce
d = guitar{freq=330, material=STEEL, body=ROSEWOOD}
e = piano_str(261.63) -- hammer, steel
f = erhu{freq=440, bow_pressure=0.6} -- bow, customized
g = sitar{freq=261.63, buzz=0.6} -- buzz + sympathetic
Presets accept freq OR table: guitar(440) or guitar{freq=440, material=STEEL}. All 15: guitar koto gayageum harp guzheng banjo mandolin bouzouki shamisen piano_str dulcimer erhu violin_str sitar tanpura. Constants: Materials: SILK NYLON GUT STEEL BRONZE BRASS WOUND. Bodies: SPRUCE CEDAR MAPLE PAULOWNIA ROSEWOOD MAHOGANY GOURD METAL MEMBRANE BAMBOO BODY_NONE. Modes: PLUCK HAMMER BOW
Strumming (Inline Syntax)
(abc/D) -- down strum, default 30ms delay
(abc/U/25) -- up strum, 25ms delay between strings
(abc/DU) -- alternating down-up
(abc/T/40) -- thumb: bass slow, treble fast rake
| Type | Keyword | Description |
| Down | D / DOWN | Low-to-high |
| Up | U / UP | High-to-low |
| Down-Up | DU | Alternating |
| Up-Down | UD | Alternating |
| Random | R / RANDOM | Random order |
| Fan | FAN | Center-outward |
| Thumb | T / THUMB | Bass slow, treble fast |
Delay (optional, after 2nd /): ms between strings. Default: 30ms.
Utilities
note_to_hz("C4") -- returns 261.63
hz_to_note(440) -- returns "A4"
Lua Helper Functions
function lead(note)
local f = note_to_hz(note)
return env(10,80,0.6,60, lpf(2000, saw(f)))
end
a = lead("C4"); b = lead("E4")
Per-Atom Configuration
a.duration = 60 -- sustain time in ms (overrides cell-based)
a.attack = 10 -- attack time in ms (per-atom ADSR override)
a.decay = 20 -- decay time in ms
a.sustain = 0.6 -- sustain level (0.0-1.0)
a.release = 50 -- release time in ms
a.level = {1.0, 2.0} -- volume: {lowercase, UPPERCASE}
a.poly = 4 -- max simultaneous voices for this atom
Global Configuration
root = 440 -- A4 tuning reference in Hz (default: 440)
-- affects all note-string pitches; e.g. root=432 for baroque
Cell Counts
| Element | Cells |
Atom (a, k) | 1 |
Rest = | 1 |
Rest _ | 2 |
Rest __ | 4 |
Staccato a. | 1 (modifier only) |
Chord (abc) | 1 |
Sustain ax4 / (abc)x4 | 4 |
Rest =x4 | 4 |
Pattern ($pat)x4 | 4 × pattern length |
One bar = div cells. Patterns in a multi-track composition should be the same cell count (or intentional multiples for polymetric effects).
Frequency Reference
| Note | Hz | Note | Hz | Note | Hz |
| A1 | 55 | A3 | 220 | A5 | 880 |
| C2 | 65 | C4 | 262 | C6 | 1047 |
| E2 | 82 | E4 | 330 | | |
| A2 | 110 | G4 | 392 | | |
| C3 | 131 | A4 | 440 | | |
| E3 | 165 | C5 | 523 | | |
Note string offsets: "C4+7" = G4, "A4-12" = A3