Imported from sdclibbery/limut (
.claude/skills/write-music/SKILL.md). Install upstream withnpx skills add sdclibbery/limut --skill write-music. Copyright stays with the author.
Write limut DSL code to produce music. Read the CLAUDE.md in the project root for full DSL reference. Below are practical patterns learned from examples.limut and the full index.html documentation.
Structure of a Piece
A piece typically has:
set bpm=N(common: 80-150)set scale=NAME(minor, phrygian, dorian, mixolydian, lydian, penta, major, etc.)- Optional
set root=Nto transpose - Drum/percussion players
- Bass player
- Harmonic/pad players
- Lead/melody players
- Optional visuals (v lines)
- Optional
set ch=...for shared chord progressions
Each line defines one player: ID TYPE PATTERN, param=value, param=value
Limut is case insensitive; piano, Piano, and PIANO are all treated the same.
Basic Syntax
Comments
//line comment/* ... */block comment
Commands
id synth pattern, params- define a playerset id params- override player paramsset var=value- set global varpreset name synth, params- create a synth presetinclude 'url'- include a limut source file
Multi-line commands
Commands can span multiple lines. A new command starts when the beginning of a line matches set , preset , or id type pattern.
Drum Patterns
Use play type with letter patterns. Each character is a 1-beat hit; . is rest.
Play Samples (full list)
Each letter maps to a sample folder. Multiple samples exist per letter (selected via sample=N).
| Char | Sound | Char | Sound |
|---|---|---|---|
x |
Bass drum | X |
Heavy kick |
v |
Soft kick | V |
Hard kick |
o |
Snare drum | O |
Heavy snare |
u |
Soft snare | i |
Jungle snare |
I |
Rock snare | D |
Dirty snare |
- |
Hi hat closed | = |
Hi hat open |
: |
Hi-hats | + |
Clicks |
* |
Clap | H |
Clap |
h |
Finger snaps | # |
Crash |
~ |
Ride cymbal | s |
Shaker |
S |
Tambourine | t |
Rimshot |
T |
Cowbell | e |
Electronic Cowbell |
k |
Wood shaker | K |
Percussive hits |
l |
Robot noise | L |
Noisy percussive hits |
m |
808 toms | M |
Acoustic toms |
r |
Metal | R |
Metallic |
p |
Tabla | P |
Tabla long |
d |
Woodblock | n |
Noise |
a |
Gameboy hihat | A |
Gameboy kick drum |
b |
Noisy beep | B |
Short saw |
c |
Voice/string | C |
Choral |
E |
Ringing percussion | f |
Pops |
F |
Trumpet stabs | g |
Ominous |
G |
Ambient stabs | J |
Ambient stabs |
N |
Gameboy SFX | w |
Dub hits |
W |
Distorted | y |
Percussive hits |
Y |
High buzz | z |
Scratch |
Z |
Loud stabs | q |
Ambient stabs |
Q |
Electronic stabs | U |
Misc. Fx |
| |
Hangdrum | / |
Reverse sounds |
\ |
Lazer | % |
Noise bursts |
$ |
Beatbox | @ |
Gameboy noise |
& |
Chime | ! |
Yeah! |
1 |
Vocals (One) | 2 |
Vocals (Two) |
3 |
Vocals (Three) | 4 |
Vocals (Four) |
Typical drum patterns
kd play X., room=0.1 // four-on-floor kick
kd play (Xv)., room=0.1 // kick with ghost note chord
kd play X..X.X.., room=0.1 // syncopated kick
sd play ..O., room=0.1 // backbeat snare
sd play ..(OH*)., room=0.2 // snare with clap layer
hh play -, echo=1/4, amp=2 // 8th note hats
hh play -[--]-[.-], fold, amp=2 // breakbeat-style hats
hh play .[::], echo=1/8 // hat pattern with tight hats
Pattern Syntax
Brackets in patterns
(...)= chord (play all simultaneously)[...]= subsequence (fit multiple events into onedur)<...>= supersequence (cycle through alternatives each pattern repeat)
Examples:
X<...[.v]>= first time X then ..., second time X then .v[XX]= two kicks in one beat (double time)(OH)= snare and clap together
A chord in a pattern is written with NO separators — (047), not (0,4,7). Pattern notes are
single characters juxtaposed, exactly as 0357 is four notes, so (047) is one chord of three.
Commas belong to expressions, where a chord is (0,4,7) (see Chords under Expression Syntax) —
so add=(0,4,7) is right and p1 lead (0,4,7) is a parse error:
Invalid pattern: Missing bracket, expecting ). Same brackets, different separator rule, and the
error message does not hint at it. For notes needing more than one character in a pattern, use the
expression form via add= instead.
Pattern modifiers
.= rest (silence)_= continuation/tie (extends previous note, NOT rest)0#= sharp0b= flat0== extend duration by 50%0!= shorten duration by 33%0^= 50% louder0v= 33% quieterx^= 50% louder (non-numeric, for play synth)0a= other chars after digits become params; egt tri 01g23, glide=this.g/2
Pattern literal
`01 23` = backtick pattern literal; whitespace is ignored, contents treated as literal
Pattern operators
01 loop 2= play pattern only N times then stop0123 crop 2= crop pattern to N steps (repeats if needed)1 + 2= concatenate two patterns1 * 2= repeat pattern N timesnow 01= play starting on next beat instead of from beat 0follow p1= follow another player's patterngamepad= generate notes from gamepad controllermidi= generate notes from MIDI controller
Audio Synth Types
Basic oscillators
play- sample player (letters/symbols for samples). Duration defaults to 1/2perc- sampled percussion. Duration defaults to 1/4, has choke groupsaw- sawtooth wave, oct=4sine- sine wave, oct=4square- square wave, oct=4tri- triangle wave, oct=4pulse- pulse wave, oct=3wave- waveform specified bywaveparamping- sine wave ping, oct=5, simple envelopenoise- white noise pad (AudioWorklet)
Detuned variants (thicker sound)
dsaw- detuned sawtooth, oct=4dsine- detuned sine pad, oct=4dsquare- detuned square, oct=4dtri- detuned triangle, oct=4dbass- detuned sawtooth bass, oct=2dpulse- detuned pulse, oct=3dwave- detuned waveform specified bywaveparamswell- detuned triangle pad, oct=4
FM synthesis
fm- FM base synth; configure operators withop1toop6paramsop1={ratio:5.19,target:3,wave:'saw',depth:0.8,att:0.01,rel:0.1}
fmbass- FM bass, oct=2bell- FM bellglock- FM glockenspiel, oct=4glass- FM glass/chime, oct=5xylo- FM xylophone, oct=4ethereal- FM pad, oct=5epiano- DX7-style electric piano
Complex synths
audiosynth- base type; useplayparam for custom audio node graphpiano- sampled pianopwm- pulse width modulation (AudioWorklet).pwm=1/2for pulse widthprophet- PWM with LFO pulse-width (AudioWorklet)supersaw- 7-oscillator supersaw lead, oct=4ambi- ambient drone, multiple sinesmultiwave- multiple oscillators:wave1={'saw',amp:[0:1]n,detune:wow}pitchedperc- synthesised percussion (click/hit/body/rattle components)click={1},hit={0,sample:'^',index:1,rate:3/2}body={1,att:5ms,dec:400ms,freq:55hz,boost:150hz,wave:'sine',saturation:0}rattle={1,att:0ms,dec:30ms,rate:1,filter:'lowpass',freq:55hz,boost:205hz,q:18}
bd- preset kick drum based on pitchedperc. Params:accent,tone,tune=55io808- TR-808 simulation.type='bd'selects sound. Params:level,tone,decay,snappy,tuningimpulse- single pulsesample- pitched sample player. Usesampleparam for URL,startfor playback offsetexternal- microphone/line input.track=1,channel=1crackle- crackly soundsnoisefloor- slowly varying noise floorbus- audio mix bus (plays continuously, all params per-frame). Implicitmainbus exists
Special synths
! / stop / none- stop/remove a player
Melodic Patterns
Scale degrees (0-indexed): 0 = root, 1 = 2nd, 2 = 3rd, etc. Negative numbers go down.
// Melodic bass line
db dbass 0[.0][10]_, dur=1/2, lpf=[200:2000]s8, att=0, oct=(1,2)
// Arpeggio
arp dsaw 02435167, dur=1/2, sus=1/4, echo=3/4, lpf={[500:1500]l7,q:22}, att=0
// Chord stab
stab dsaw (0246), dur=[3,3,2]/4, att=0, sus=1/4, echo=3/4, lpf={[500:1700]l5,q:20}
// Pad
pad ethereal (024)(035), dur=4, room=0.7, oct=4
Value Syntax
Basic structure: value{modifiers}@interval
Numeric values
1- constant number0dB- decibel value (converted to gain; 0dB=1, +6dB2, -6dB0.5, -20dB~0.1)100ms- milliseconds;1s- seconds;1Hz/1cps- Hertz;1kHz= 1000Hz1b- beats;1cpb- cycles per beat;1kcpb= 1000cpb;1cpm- cycles per minute- Missing value on param =
1(egmonochromesame asmonochrome=1)
Sequences and timevars
[1,2]- sequence, one per event[]- equivalent to[0,1][1,2]t3- timed sequence, each value lasts 3 beats[1:12,2:4]t- timed sequence with variable timing per value[0:3]t1- expanded timed sequence =[0,1,2,3]t1[1,2]l3- linear interpolated sequence over 3 beats[1:12,2:4]l- linear interpolated with variable timing[1,2]s3- smooth (S-curve) interpolated sequence over 3 beats[1,2]e- interpolated over event duration[1,2]e1- interpolated starting at event start, 1 beat per value[0:!300ms,1:_[1/4b:1/2b]r,0]e- piecewise envelope with mixed timing[1:!200ms,0]e@s- segment-based (more accurate timing for envelopes)
Random and noise
[]r- random 0 to 1 each event[1,4,7]r- random choice from list[:9]ror[0:9]r- random float from range[0,1:7]r- weighted random (1 is 7x more likely than 0)[1,2]r4- random, hold for 4 beats[1:3]r{seed:1,per:4}- deterministic seeded random[]n- smooth Perlin noise 0 to 1[1:2]n4- noise between 1-2, period ~4 beats[]n{seed:1,per:4}- deterministic noise
Piecewise series
[v1:i1t1, v2:i2t2, ...]{p, repeat:0} - interpolate between values with custom control parameter
- Interpolation operators:
:/:\linear,:_const/step,:~smooth bezier,:!exponential,:^4:power curve
Arithmetic
+, -, *, /, %, ^ (power), - (unary minus)
Chords
(1,2,4)- play simultaneously. This comma form is for expressions (egadd=(0,4,7)); in a pattern the same chord is written without separators,(047)— see Brackets in patterns.|- concatenate chords:1|2=(1,2),(1,2)|(3,4)=(1,2,3,4)
Conditionals
??- "if then":this.foo??5returns 5 if foo is truthy, nothing if not (short circuits)?:- "or else":this.foo?:5returns foo if present, else 5 (short circuits)- Ternary:
this.foo>1 ?? 2 ?: 3
Comparisons
==, !=, <, >, <=, >=
Maps
{x:0,y:1}- map with key-value pairs{2,y:1}- map with main value plus subparams (egchop={2,wave:'tri'})- Colours:
{r:0,g:1,b:0,a:1}(RGBA),{h:1,s:1,v:1,a:1}(HSV),{labh:1,c:1,l:1}(LabLCH) - Hex colours:
#f00f(RGBA short),#ff0000ff(RGBA long),#f00/#ff0000(RGB, alpha=1) 'abc'- string value (single line; use\nfor line breaks)
Lookups (dot operator)
p1.amp- player param lookup (returns chord of all playing events)p1.amp.0- chord index (wraps around)p1.pulse.max- chord aggregator:max,min,first,last,rand,count,sum,avgthis.value- current event paramthis.freq- current note frequency{foo:2}.foo- map field lookup([0,1]t1@f).accum- smoothly accumulate values
Modifiers
{time:time*2+8}- modify the time expression{per:8}- repeat sequence every 8 beats{per:8,0:7,2:3}- force values at specific beat positions within repeat{step:1/2}- advance time in discrete steps- Note: modifier and interval order can be swapped:
[]r@f{seed:1}=[]r{seed:1}@f
Intervals
@e- evaluate once per event@f- evaluate per frame (~60fps) for smooth animation- Interval applies to nearest value:
this.foo@eapplies@etofooonly; use(this.foo)@efor the whole lookup
Variable lookup
foo- lookup global var namedfoo; returns string'foo'if no var existsfoo{x:2}- call var as function with named params
Common Event Params
Timing
dur=N- duration in beats (1/4=16th, 1/2=8th, 1=quarter, 2=half, 4=whole)dur=[3,3,2]/4- common shuffle/swing patterndelay=N- event start time offset in beatsdelay={1/2,add:2}- delay with param overrides on delayed eventsstutter=N- split event into N evenly-spaced sub-eventsswing=N- swing amount (50=straight, 58-66=swung, 75=max dotted)swing={66,period:1/2}- swing with custom period (default 1/4)
Volume
amp- player amplitude (defaults to eventvel)vel- event velocity (default 3/4; controls amp and usually brightness)
READ ONLY params
voice- voice index within a chordtime- current time in beats this event has been playingidx- pattern index of this eventexists- 1 if player is definedplaying- 1 if player is currently soundingpulse- rise/fall value as note plays (pre-multiplied by vel)player- name of current playerfreq- note frequency in Hz (set by tonal synths)
Other
rate- playback rate for samples, animation rate for visuals
Audio Params
Envelope
att- attack time (default unit: beats).att=0for sharp attackdec- decay timesus- sustain time.sus={level:0.5}for sustain level (default 0.8)rel- release timeenvelope- type:'full'(ADSR),'simple'(ADR),'organ'(ASR),'pad'(cosine crossfade),'linpad'(linear crossfade),'percussion'(R only). Or custom expression:envelope=[1:!300ms,0]e@s
Pitch & Tuning
oct=N- octave (1=sub bass, 2=bass, 3=low, 4=mid, 5=high)oct=(2,3)- chord across octavesadd=N- add scale degrees to pitch (per-event only)add={0,#:1}- sharpen;add={0,b:1}- flattenaddc=N- add continuous semitones (per-frame, fractional).addc=wowfor pitch driftdetune=N- oscillator detuning in semitonesvib=RATE- vibrato rate (default unit: cpb)vib={2,depth:1}- vibrato depth in semitones (default 0.4)vib={2,delay:1}- delay before vibrato starts (default 1/2 beat)glide=N- portamento time in beats.glide={1/3,curve:4}for curve shape (default curve:1)root=N- override root for this player onlyscale=NAME- override scale for this player
Filter
lpf=FREQ- low pass filter.lpf={400,q:20}for resonance (default q:5).lpf={400,poles:4}for 24dB/octhpf=FREQ- high pass filter. Same subparams as lpfbpf={FREQ,q:20}- band pass filter (default q:1). Multiple parallel:bpf1,bpf2etcnf={FREQ,q:20}- notch filter (default q:1)apf1={FREQ,q:2}- allpass filter. Multipleapf1,apf2etc (parallel, allowing phase cancellation)psf={f1:300,f2:2300,q:0.7}- phaser stage filter. Multiplepsf1,psf2etc (series)low=-6dB- low shelf EQ.low={-8db,freq:400}(default freq:200Hz)mid=-6dB- mid EQ.mid={-8db,freq:400,q:10}(default freq:600Hz, q:5)high=-6dB- high shelf EQ.high={-8db,freq:400}(default freq:1100Hz)- Filter sweep:
lpf={[200:2000]l8,q:15}
Effects
room=N- freeverb (0.1-2).room={2,hpf:300,mix:1/4}(default mix:1/2)reverb=N- convolution reverb duration in beats.reverb={2,curve:3,hpf:300,mix:1/4}(default mix:1/2, curve:5)echo=TIME- echo delay.echo={1,feedback:0.8}(default feedback:0.35). Bus echo:echo={1,max:2}chorus=N- stereo chorus.chorus={1,mix:1/4}phaser=RATE- phaser (default unit: cpb).phaser={1/3,mix:1/4}flanger=RATE- flanger (default unit: cpb).flanger={1/3,mix:1/4}chop=N- tremolo/gating (default unit: cpb).chop={2,wave:'triangle',mix:1/4}(waves: sine, square, triangle, saw)ring=FREQ- ring modulation (default unit: Hz).ring={32,wave:'saw',mix:1/4}(default wave: triangle)drive=N- overdrive/soft clip (1/32=gentle, 1/4=distortion, 1=fuzz).drive={1,gain:2,mix:1/4}fold=N- wavefolder.fold={1,gain:2,mix:1/4}clip=N- hard clipping.clip={1,gain:2,mix:1/4}noisify=N- distort into noise.noisify={1,gain:2,mix:1/4}bits=N- bitcrusher (1=very distorted, 32=clean, 0=off).bits={4,gain:2,mix:1/4}suck=N- scale down small amplitudes (gated tail effect).suck={1,gain:2,mix:1/4}compress=RATIO- compression.compress={12,gain:2,threshold:-40dB,knee:10dB,att:0.1,rel:0.2}
Spatial
pan=N- stereo position (-1 to 1)pan=(-1,1)- hard pan left and rightmono- force signal to mono
Other audio params
choke=GROUP- choke group (string); notes cut off others in same group across playerssample=N- withplay: choose sample set. Withsamplesynth: URL of audio filesample={32,pitch:261.6}- specify original sample pitch (default 261.6Hz = C4)start=N- sample playback start time (default unit: seconds)wave=NAME- waveform:sine,square,sawtooth/saw,triangle/tri,pulse, plus many more:ah,bass,brass,buzzy,celeste,ethnic,organ,piano,trombone,wurlitzer, etc. (hyphenated names need quotes)bus=ID- mix to specified bus player (default:main)cutoff- filter cutoff 0-1 (for presets that support it)resonance- filter resonance (for presets that support it)
Visual Synths
blank- solid colourclouds- moving cloudskal- kaleidoscopeswirl- psychedelic swirljulia- Julia set fractallines- twisting linesblob- morphing 3D blobstreetlight- passing streetlightsgrid- square gridglow- additive glowstars- exploding starsbits- bitwise fractal patternsxor- bitwise xor patternsgradient- simple y gradientshadertoy- shadertoy shader (useidparam; must be public+api)image- online image (useurlparam; must support CORS)webcam- webcam feed.deviceparam for device index/label.width/heightfor resolutiontext- text display.text={'Hello',font:'times',size:'144',linesize:0.7,y:1/4,x:2/3,style:'bold italic'}buffer- render target. Other synths render into it withbuffer=IDscope- audio waveform displayscopefft- frequency spectrum displayreadout- digital value displaydmx- DMX lighting output.channel,set,lights,addl,mul,min,maxparams
Visual Params
add- add to pattern valuezorder- draw order (default: line number / 1000)fade- brightness fadeouttime- override shader time (default unit: beats; overridesrateandsway)rate- shader time progression ratepulse- audio signal effect on visual valuesway- audio signal effect on visual timeloc- position/size:{x:0,y:0,w:2,h:2}for fullscreen. Coords: (-1,-1) bottom-left to (1,1) top-rightwindow- display only portion matching screen positionscroll- scroll shader:{x:0,y:0}repeat- repeat shader section.repeat={1/2,x:0.1,y:-0.05}zoom- zoom:{x:1,y:1}rotate- rotation in full turns (1/2 = 180 degrees)mirror=N- number of mirror planes.mirror={10,rotate:1/4,fan:1}fore- foreground colourmid- middleground colour (defaults to midway between fore and back)back- background colourpixellate=N- pixellate to N pixels in x.pixellate={10,y:20}perspective=N- perspective warp (negative to invert).perspective={-1,shade:1/2}tunnel- tunnel/slitscan warpripple- ripple distortion.ripple={1,scale:1/4}additive- additive blendblend- blend mode:additive,subtractive,average,multiply,invert,min,maxmonochrome- 0=normal, 1=monochromevignette- edge fadeout.vignette={1/2,aspect:4/3,cutoff:0.95}vhs- VHS tape effectrecol- recolour:oil,hue,fire,sunset,neon,titaniumcontrast- power contrast curve (0=none)buffer=ID- render to a buffer synthrez=N- buffer render target resolution scale (default 1/2)feedback- buffer video feedback.feedback={zoom:1.01,ripple:1/32,contrast:0.005}
Main Vars (set, not readable)
bpm- beats per minute (resets unless set every update)scale- scale name (resets unless set every update)root- root pitch offset in semitones (fractional OK for tuning nudges)beat.readouts- beat counter display:beat.readouts=(3,12)(default: (12,16,32), max 3)
Predefined Vars
time- current time in beatswow- smooth random pitch drift for analog feel; egaddc=wowtg- trance gate signal (flips on/off every quarter beat)drop6_2throughdrop63_1- predefined on/off drop patterns (eg[1,0]t[6,2])tile_full,tile_tl/tr/bl/br/m,tile_h1-h5,tile_v1-v5- visual locationsfullscreen- full screen tiletile_random/tile_rand- random location tilesparkle- small random location tiledroplet- small tile falling down screenspark- small tile flying outwardsgravity- accelerating downward motion (combine:spark+gravity)firefly- random insect-like motion- Colours:
transparent,black,darkgray,gray,lightgray,white,red,orange,yellow,green,blue,indigo,violet,neonpink,neongreen random- random colourrainbow- colour cycling through rainbow
Functions
abs{-1}- absolute valuesign{-1}/sgn- sign (-1, 0, 1)pitch{1}- frequency for scale degree.pitch{4#}sharp.pitch{0,oct:3,scale:minor,root:2}eventpitch{}- frequency based on current event paramsfloor{1.5}/ceil{1.2}/round{1.7}- rounding.floor{1.7,to:1/2}rounds to precisionaccum{1}- accumulate value over time (only rises)smooth{[]r@f}- smooth a value.smooth{x,att:4,dec:4}orsmooth{x,2}combined raterate{[0:1]l1@f}- rate of change in units per beatfirst{3,1,2}/last{3,1,2}- first/last argumentmin{3,1,2}/max{3,1,2}- min/max of argumentscount{3,1,2}/sum{3,1,2}/avg{3,1,2}- count/sum/averagerand{3,1,2}- random choice from argumentsrand- pseudo-random 0-1 (time-hashed; respectsstep/permodifiers)euclid{3,from:8}- Euclidean rhythm generator.euclid{3,from:8,offset:1}for rotationsin{x}/cos{x}/tan{x}- trig functions (radians)time- current time in beats (respects modifiers)
Node Functions
For building custom audio graphs with >> connect operator and audiosynth:
osc{'sawtooth',freq:440,phase:0}- oscillator nodeconst{1}- constant value nodegain{1}- gain nodedelay{1/4b,feedback:...,max:1b}- delay nodepanner{0}- stereo panner (-1 to 1)sample{sample:'url',start:0s,rate:1}- sample player. Also:sample{sample:{x}->...,length:0.2s}function-generatedsample{sample:'url',loopstart:1,looplen:2}- looping samplebiquad{'lowpass',freq:440Hz,q:5}- biquad filter (lowpass/highpass/bandpass/notch/lowshelf/highshelf/peaking)compress{ratio:0,threshold:1/316,knee:100,attack:0.01s,release:0.25s}- compressorconvolver{env:{x}->...,length:1s}- convolver reverb. Stereo:convolver{env:{l:{x}->...,r:{x}->...},length:1s}shaper{{x}->...,samples:2,oversample:'2x'}- wave shaper ('2x', '4x', 'none')series{...,count:2}- repeat chain in seriesloop{...,feedback:...}- audio feedback loop (must contain delay)mix{...,mix:1/2}- dry/wet mix. Alsomix3throughmix8for fixed ratiosstereo{l:...,r:...}- split/process/join stereo channelsflipper- swap L/R channelsidnode/dry/thru- pass-through node
Nodes Library (include 'lib/nodes.limut')
Helper functions wrapping node functions (all args can be positional):
lpf{freq,q:5}/hpf{freq,q:5}/bpf{freq,q:5}/nf{freq,q:5}/apf{freq,q:5}- filterslpf4{freq,q:5}- 4-pole low passlsf{gain,freq:1100Hz}/hsf{gain,freq:1100Hz}/pkf{gain,freq:1100Hz}- shelf/peak EQlfo{freq,wave:'triangle',lo:0,hi:1,phase:0}- LFO (any frequency)osc.sin{freq}/osc.saw{freq}/osc.square{freq}/osc.tri{freq}/osc.pulse{freq}- typed oscillatorsdrive{amount:1/2}- overdrive waveshaperstanh{gain:3/2}- tanh saturation (alias forshaper.tanh)shaper.tanh{gain}/shaper.atan{gain}/shaper.poly{gain}/shaper.pow{curve}/shaper.asym{gain,bias}/shaper.diode{gain,pos,neg}- waveshaperscompressor{...}/limiter{...}- dynamics
Don't put a filter in a modulator chain
A filter feeding an audio path is free. A filter in a chain that ends at a param is not:
superosc{freq: eventpitch*(1+(noise>>lpf{1000})/3)} builds a biquad per note inside a param
subgraph, and Chromium leaves cost resident on the audio thread for every one of them, so a fast
line slowly eats the audio thread until it drops out. Nothing in the DSL fixes it.
Smooth a modulator with a slow source instead of a filter: noise{rate:1/20} is white noise
band-limited to ~1.1kHz (rate scales the band: 1/50 -> ~440Hz), with no filter in the chain. It is
louder than the lowpassed version — roughly 2.4x for lpf{1000} at the default q:5 — so scale the
depth down to match. keytar in preset/synth.limut is the worked example.
Effects Library (include 'lib/effects.limut')
Wet-path effects (wrap in mix{} for dry signal):
echo{time:1/8b,feedback:0.7,max}- echo with feedback and dry pathpingpong{time:1/4b,feedback:0.7}- stereo ping-pong delayreverb{length:1b,curve:3}- convolution reverbflanger{control:[0,1]l8@f,feedback:0,lo:0.0005,hi:0.007}- flangerphaser{control:[0,1]l4@f,lo:300Hz,hi:2600Hz,stages:4,q:1/2}- phasershifter{ratio,length:0.03}- pitch shiftershimmer{ratio:2,length:2s}- shimmer reverbtape{wow:1,cut:-10db}- cassette tape effectairverb{delay:1/2b}- airy reverb with flanger and stereo pingponggrain{ratio,length:0.03,phase:0}- granular resynthesis graindemon{}- demon voice effect (for vocals/mic)ultracomb{f:...,p:...,s:...,stages:6}- ultracomb filter (growling sounds)
User Input Functions
slider{0.1}- on-screen slider.slider{name:'Foo',min:-5,max:5,curve:2}gamepad{1}- gamepad axis (-1 to 1).gamepad{button:0}(0-1).gamepad{'lsh'}left stick horizontal.gpis aliasmidi{70}- MIDI control/note (0-1).midi{36,9,1}(control 36, channel 9, port 1)midi{'bend'}- pitch bend (-1 to 1).midi{'notes'}- chord of playing notes.midi{'vel'}- velocity
Console Commands
list- list all player base types including presetslist audio- list filtered by string
Preset Libraries
Synthwave (include 'preset/synthwave.limut')
Audio: moroder, blade, laserharp, synlead, synpluck, synbass, chiparp, softpad, space, blues, basspluck, bloom, pick, softkeys, epiano, lushpad, stringpad, oxy, vambi, cybass, cydist, cyborg, hiss, blips, chime, play80s
Visual: neongrid, neonbars, neonbits, neonlines, neonsine, neonheart, neonshapes, skybars, sun, sunsetsky, vhsbuffer
Many support cutoff param (0-1, default 1/2). Some support decay param.
808 (include 'preset/808.limut')
Individual: bd808, sd808, oh808, ch808, cb808, cp808, ma808, ht808, mt808, lt808, hc808, mc808, lc808, cl808, rs808, cy808
Combined: tr808 (all-in-one; pattern chars select sound: x/v=bd, o/i/u=sd, h/*=cp, -/:/a=ch, ==oh, ~=cy, k=cl, m=mt, t=rs, s=ma, p=mc, e=cb; ^ flag for accent), h808 (hihats; o flag for open), t808 (toms; h/l flags), c808 (congas; h/l flags)
909 (include 'preset/909.limut')
Synthesized: bd909 (level, tune, attack, decay), sd909 (level, tune, tone, snappy), h909 (level, chdecay, ohdecay; o flag for open), cp909 (level), cc909 (level, tune), rc909 (level, tune), t909 (level, tune, decay; l/h flags), rs909 (level)
Sampled (lighter CPU): bds909, sds909, hs909, cps909, ccs909, rcs909, ts909, rss909
All: event value gives accent.
TB-303 (include 'preset/303.limut')
tb303 - acid bass. Controls: wave ('saw'/'square'), cutoff, resonance, envmod, decay, accent (all 0-1, default 1/2). Pattern flags: a accent, u up octave, d down octave, s slide.
Example: b tb303 `0a2u2b0d 0u0a2d3 00u0a0d 0da4ua5a6u`, cutoff=[1/4:1]n3, resonance=0.8, accent=1
Trance (include 'preset/trance.limut')
trance - classic supersaw lead (default add=(0,2)). Controls: cutoff, decay, detune (all 0-1).
Techno (include 'preset/techno.limut')
Includes 909, 303, trance libraries. Audio: hollow, tinbass (cutoff), sweep (cutoff), robot, techbass (cutoff, decay), growl
House (include 'preset/house.limut')
Includes 909, 303, trance libraries. Audio: m1organ (cutoff), reese (cutoff, detune), donk (cutoff, decay), didgeridoo
Available Scales
chromatic, major, minor, pentatonic/penta, minorpentatonic/minorpenta, mixolydian, dorian, phrygian, lydian, locrian, harmonicminor, harmonicmajor, melodicminor, melodicmajor, diminished, egyptian, blues, wholetone, chinese, hungarianminor, romanianminor, halfwhole, wholehalf, bebopmaj, bebopdorian, bebopdom, bebopmelmin, majorpentatonic, aeolian, dorian2, yu, zhi, prometheus, indian, locrianmajor, lydianminor, minmaj, susb9, lydianaug, lydiandom, melmin5th, halfdim, altered
Set for Groups
set (p1,p2,p3) room=0.7, echo=3/4 // apply params to multiple players
set p* amp=0 // wildcard: all players starting with p
set !p* add+=7 // all players NOT starting with p
set * swing=58 // all players
set ch=[0,2,4,3]t8 // define shared variable
Operator overrides
add+=N- add to existing valueamp*=N- multiply existing valueoct+=N- shift octave
Bus Mixing
There is an implicit main bus. Players can route to named buses:
fx bus 0, echo=1/2, room=0.5
bass dbass 0, bus=fx
Override main bus: set main echo=1/2
Genre Recipes
Techno (120-140 bpm, minor/phrygian)
set bpm=130
set scale=phrygian
kd play X., room=0.1
hh play -, echo=1/4, amp=2
sd play ..(H*)., room=0.2
db dbass 0, dur=1/4, lpf={[150:800]r,q:12}, att=0, amp=2
pad dsaw (0246), dur=8, envelope=pad, lpf=[500:3000]l16@f, oct=(3,4), amp=1/2
House (120-128 bpm, minor/dorian)
set bpm=125
set scale=minor
kd play X., room=0.1
hh play .[::], echo=1/4
sd play ..O., room=0.3
bass fmbass 0[.0][.0][0.], dur=1/2, sus=1/2, lpf={[300:800]n4,q:8}, oct=(2,3), att=0, amp=0.8
pad prophet (0246), dur=8, oct=(2,3), amp=1/2, room=0.7
Ambient (60-90 bpm, major/lydian/penta)
set bpm=70
set scale=penta
pad ethereal (024)(035), dur=4, room=2, oct=(3,4), amp=1/3, pan=[-1:1]r
pi piano 0, dur=[2,4], add=[-2:3]n1/4, amp=[0:1]n6, room=0.7, oct=5
n noise 0, dur=8, amp=1/16, lpf=[800:1200]n4, pan=(-1,1), room=2
DnB (160-180 bpm, minor)
set bpm=170
set scale=minor
kd play X..X.X.., room=0.1
sd play ..O., room=0.1
hh play -[--]-[.-], amp=2, echo=1/4
db dbass 0, dur=1/4, lpf=[200:1000]l8, att=0, oct=(1,2), amp=2
Acid (125-135 bpm, phrygian)
set bpm=127
set scale=phrygian
kd play X., room=0.1
hh play -, amp=2
b saw 00[43]-707[-32]-4, dur=1/2, oct=2, amp=2, echo=3/4, lpf={this.freq*[2:50]n4,q:25}
Synthwave (80-120 bpm, minor/phrygian)
set bpm=100
set scale=minor
kd play X.v., room=0.2
sd play ..O., room=0.2
hh play -===---=, dur=1/4, amp=2
db dbass 0, dur=1/4, lpf={[200:1400]n8,q:15}, amp=1+wow, room=0.2
pad blade (024)(035), dur=4, oct=3, add=[0,4,5,6]t8
arp synpluck 02479742, dur=1/4, echo=3/4, lpf=[500:3000]l11
Breakbeat/IDM (120-140 bpm, minor/phrygian)
set bpm=132
set * swing=58
kd play V., room=0.1
hh play [.-]-[.-][--], fold, amp=2
sd play ..*., fold, room=0.1
b saw .0, dur=1/2, oct=2, lpf={[200:1500]l8,q:12}, flanger=1/7, amp=3/2
Tips for Good Music
- Layer sparingly: 4-8 players is plenty. Too many = mud.
- Use filter sweeps:
lpf={[300:2000]l8,q:15}adds movement and interest. - Echo/delay:
echo=3/4(dotted quarter) orecho=1/2creates rhythmic interest. - Shared chord progressions: Define
set ch=[0,2,4,3]t8and useadd=chon multiple players. - Pan for width: Spread elements with
pan=(-1,1)orpan=[-1/2:1/2]r. - Room/reverb: Use sparingly on drums (0.1-0.3), more on pads/leads (0.5-2).
- Sidechain-like pumping:
amp=1-kd.pulse.minmakes things duck with the kick. - Build/drop structure: Use
setoverrides and commented lines for live arrangement. - Octave layering:
oct=(2,3)oroct=(3,4,5)for thickness. - wow for organic movement:
addc=wowadds subtle pitch drift to pads/leads. - Swing:
set * swing=58gives a nice groove (50=straight, 66=heavy triplet swing). - Amp variation:
amp=[0:1]n4for perlin noise dynamics,amp=[]rfor random. - Chop/gate:
chop=2orchop=4for rhythmic gating on pads. - Follow:
dsf dsquare follow ds- layer the same pattern with a different synth.
Production Idioms (from real pieces)
Mastering chain on main bus
Almost every finished piece ends with one of these on set main:
set main compress={2,gain:2}, drive=1/32 // gentle glue + warmth
set main compress={2,gain:2}, drive=1/64, reverb={1,mix:1/4} // + tail
set main compress={3/2,gain:3/2}, drive=1/96, amp=3/2 // heavy techno glue
drive 1/96 → 1/16 trades subtle warmth for audible saturation. compress ratio 2-3 with gain:2-3 is the sweet spot.
Multi-bus organization (techno/house template)
Separate buses per stem so each can be shaped independently:
set p* bus='bp' // all perc → bp
set !p* bus='blead' // everything else → blead
bp bus 0, compress={2,gain:2}, drive=1/32, amp=2/3
blead bus 0, compress={2,gain:2}, reverb={2,mix:1/3}, amp=1/2
Common bus names: bp/bperc, bb/bbass, blead/bl, be (external).
Sparse delay idiom (delay|=[...]t[N,M])
Adds occasional delay throws without changing every event:
set p* delay|=[(),[1,3]r/4]t[14,2] // 14 bars dry, 2 bars random throws
set p* delay|=[(),[1,3,5]r/4]t[13,3]
delay=[0,(0,[1/4,3/4]r)]t[7,1] // single-player form
|= (concat-assign) appends to existing delay rather than overwriting.
Pattern variation with crop and +
b reese 0_7 crop 14 + 89 // 14 beats of 0_7, then 89
ps sd909 ..0 crop 16
t trance 72. crop 14 + 89
Sidechain-style ducking via pulse
amp=2/3+kd.pulse.0/3 // pad swells with kick
contrast=3-2*bs.pulse.max // visual reacts to bass
zoom=1+smooth{main.pulse,att:1/2,dec:1/2} // smoothed pulse for visuals
Visual + buffer feedback (canonical setup)
vk kal 1, buffer='vb', blend=max, contrast=3, fore=neonpink
vb buffer 0, feedback={fore:0.95,zoom:1.01,rotate:0.001}
Common feedback knobs: fore:0.85-0.97 (decay), zoom:1.001-1.05, rotate, ripple, scroll, mirror. Reactive: feedback={fore:0.85+0.15*pk.pulse.0,zoom:1.02}.
Webcam + recolor
vw webcam 0, vignette=0, buffer='vb', recol=neon, blend=max
vb buffer 0, loc=tile_tr, feedback={fore:0.9,zoom:1.02}
VHS retro look
v kal 0, vhs, vignette={1/2,aspect:4/3}, contrast
vb vhsbuffer 0, feedback={fore:0.85}
Build/drop arrangement via riser
riser is typically a slider ramp. Pieces stash a "drop" block as comments:
// set (b*,p*) hpf=10000-10000*riser^3 // sweep filters open
// set t cutoff=riser^3 // open lead filter
// f sd909 2, level=riser^6 // snare roll fades in
Define with set riser=slider{0,name:'Riser'}.
Euclidean rhythms
ph h909 0, o=euclid{16,from:[19,23]t4}-1 // open-hat pattern
b tb303 0, amp=euclid{32,from:53}-7/8 // sparse 303 line
o m1organ 0, dur=euclid{7,from:16}/4 // polymeter via dur
One-shots with now ... loop 1
boom play now [(vx)(vX)(VXM)] loop 1, room=2, echo=0.46
whoosh dsaw now (047) loop 1, envelope=simple, att=1/2, rel=3
now starts on next beat (not beat 0); loop 1 plays once.
Layered delay copies via subparams
Layer a delayed copy with different params on the same event:
delay=(0,{1/4,oct:3}) // dry + delayed-up-an-octave
delay=(0,{3/4,oct:2,addc:19}) // detuned/octave-shifted
delay=(0,{1/2,add:1,stutter:2}) // multiple overrides
Shared chord progressions via set ch=...
set ch=[(0,2,4,7),(0,3,5,7),(1,3,5b,7),(1,3,4,6#)]t4
p dsaw 0, add=ch, glide=2/3
l sine 0, add=ch.rand, oct=5 // .rand picks a voice
b bass 0, add=ch.0 // .0 picks first voice
Non-percussion targeting with set !p*
set !p* add+=[2,-1,1,0]t4
set !p* bus='blead'
Drum preset notes
- 909 synth (
bd909,sd909,h909,cp909,t909,rs909,cc909,rc909): passattackflag tobd909for pitch-envelope click. Params:level,tune,decay,tone,snappy. - 909 sampled (
bds909,sds909,hs909,cps909,ts909,rss909,ccs909,rcs909): much lighter CPU — prefer for dense patterns. h909/hs909: pattern charo= open hat. Drum char letters in patterns also pick variations.tb303flags:a=accent,u=up oct,d=down oct,s=slide.
Live-coding scaffold
Typical improv file:
include 'preset/synthwave.limut' // or techno/house
set scale=minor
set bpm=120
set main compress={2,gain:2}, drive=1/64
vk kal 0, buffer='vb', blend=max, contrast
vb buffer 0, feedback={fore:0.95,zoom:1.01}
pk bd909 0..., attack
ph h909 ..0, swing=54
ps sd909 ..0., swing=58
b tb303 0, cutoff=[0:1]n4, resonance=7/8
// l trance 0_7, decay=5, reverb=2 // bring in lead
// pad softpad (024), dur=4
// set (p*,b) amp=0 // drop block
// set t cutoff=riser^3