Limut live coding

Level:  

Beat: :

Timing: Visual: Limiter:

Examples

Copy and paste an example into the editor window, then hit the play ▶ button:

d1 play x-o-
p1 dsaw 0534, add=(0,2,4), dur=4
Ba dbass 0, dur=1/4, add=[0:2]r*2, lpf={[3:12]r*100,q:8}, att=0
d dsaw 4[32]<10>___.., phaser=1/3, envelope=simple, rel=this.dur*2, amp=3, room={2,mix:1/4}
ARP dsaw 02479742, dur=1/4, lpf={100*[3,30]l6.8,q:8}, Oct=(3,5), echo=3/8, att=0
Bass prophet -2210, dur=4, Oct=(2,3), amp=1.7
set bpm=125
set * swing=61
p play [X-]-(X*)<---[--^]>, high=4, pan=[-1/4:1/4]r
s tri 00[...0][.0..], dur=1, sus=1/6, att=0.05, room, oct=3, fold, phaser=1/16, amp=2/3, pan=[-1/2:1/2]l4@f+[-1/4:1/4]r, add=(0,2,4,6)
b dbass ...[02]...[0246], room=0.1, lpf={this.freq*[3:12]l11@f,q:15}, pan=[-1/4:1/4]r
d play x.h[.x]x[x.]h., room={0.1,mix:1/3}, amp=3/2
set chords=[0,2,4,3]t[16,8,4,4]
be bell 0[.......2], dur=8, add=chords
gl glock 024797.., dur=1/4, add=chords
Perc play lstig, dur=[1,3,2], amp=[1/6:1/2]l16, echo=1/8, pan=[-1:1]r
Amb ambi 0[21]., dur=4, release=8, room=2, pan=(-1,1)
Bass dbass 0, dur=8, att=2, rel=4, lpf=100*[3,30]l16@f
set Prog=[2,5,4,0]t8
Ba piano 0__2, add=prog, amp=4, Oct=2
Ch piano 0, dur=8, Sus=12, add=(prog+(0,2,4))%7, amp=2, Oct=3
Le piano 0_.[.0]2343, amp=3, add=prog, Oct=4
set scale=minor
Ba dbass 0.4[21], dur=4, amp=2, room=1.2, att=0.01, lpf=this.freq*[30,24,4]e1, phaser=1/8
Kd play V[.V]V., dur=2, room=1.1, echo=1/16, amp=2
Sn play .(xO), dur=2, room=1.1, echo=1/16
set bpm=120
set * swing=58
set scale=minor
k play Xv, room=1/4, amp=3/2
h play s-, dur=1/4, amp=1/2
ba saw 020.00.0.040.00., dur=1/4, oct=2, sus=1/6, amp=2, lpf={this.freq*[[4:25]l7,2]e,q:15}, room={1,mix:1/5}
set bpm=65
pad dsaw (024)(025)(035)(026), envelope=pad, dur=4, lpf=[600:3000]s21, oct=3, amp=[1.2:2.0]s70
padf ethereal follow pad, oct=4
pi ping 0, dur=[3,3,2]/20, amp=[0.5:1.2]n*[2:0]s96, oct=[3,4,5,6]t1/4, add=[0,5,3,4]t4+[0,[1:3]n32,4,[6:10]n37]r, sus=2, addc=[-.15:.15]n
v kal 0, tunnel, scroll={x:time/8}, monochrome, fore=rainbow, mid=white
b dbass [(04v).], dur=8, noisify=1/8, drive=2/8, fold=1, phaser=1/8, room=2, add=[2,3,1,0]t8, addc=wow, pan=[-1/2:1/2]r, chorus=2, att=1/2, rel=5
set bpm=145
set scale=minor
v streetlight 0
kd play (xv):, lpf=1000
hh play -, dur=1/4, amp=[0.2:1.2]n
kk play ::[::][::][::]::., lpf=[600:900]l40, rate=[0.8:1.2]n
ba dbass 0, dur=1/4, att=0, lpf=[1000:100]e, hpf=[0:500]n
pi ping [010], add=[-5:5]l18/4, amp=[0:[0:1]t96]n, echo=1/2, oct=6, scale=minorpentatonic
pad dsaw (-303), dur=16, envelope=pad, amp=[0:3/2]s64@f, room=0.5, lpf=[1000:3000]s32@f, oct=3, addc=[2,2,0,0]s[62,4,62,0]@f
set bpm=90
n noise 00, dur=[9,7], envelope=simple, bpf=523*[[1:5]e,[1:1/5]e]*[1:[1.1,1.25]]n1, amp=3/2, att=[2,1/4], rel=[9,7], pan=[-1,1]l8@f
p dsaw 0., dur=[3,1,5,3,3,4], addc=[1:3]r+[2,0]e, envelope=simple, att=0.1, rel=[0.4:0.6]r, oct=6, pan=[-1:1]n8, amp=[0.3:1.1]n8, room=1
s supersaw 0, dur=[1/4:1]l8, scale=penta, add=[-10:10]r1/2, room=2, lpf={[300,3000,300]e,q:11}, amp=2, phaser=1/4
v kal 0, mirror={100,fan:[7:12]s32@f/10}, zoom=3, mid=random, contrast
ld dsaw 0^.70-7.70, dur=1/4, scale=minor, add=[0,2,4]r+[0,2,3,0]t4, lpf={this.freq*[3/2,2,3,5]t4,q:25}, room, delay=(0,[1/4,1/2,3/4,5/4]r), echo=7/8, addc=wow
v kal 1, buffer=vb1, contrast=3, blend=max, zoom=2, scroll=1
vb1 buffer 0, feedback={zoom:1.01,ripple:1/32,contrast:0.005}
include 'preset/house.limut'
set bpm=127
b tb303 `0a2u2b0d 0u0a2d3 00u0a0d 0da4ua5a6u`, cutoff=[1/4:1]n3, resonance=0.8, accent=1  

More examples: https://github.com/sdclibbery/limut/blob/master/examples.limut

Glossary

Chord: multiple simultaneous events

Command: One programming instruction to tell limut what to do

Event: One event to be played; either an audio note/sample or a visual display

Expression: Code that evaluates to a value

Global Var: A variable; something whose value can be set by a command, and then read in an expression

Indicator: Visual display on the limut screen to help with composition or production

Map: Value containing multiple values indexed by keys

Modifier: Additional data thats changes the value of an expression, eg by modifying the current time it sees, or by providing a random seed

Param: A named parameter expression that controls the output produced by a synth

Pattern: A sequence defining the notes or control values to be played by a synth

Player: An player uses a synth to play events in a pattern, controlled by params

Subparam: An additional parameter provided extra info on how a param should control the output of a synth

Synth: An audio or visual synthesizer that can produce sound output or visual display.

Timevar: An expression whose value varies over time

Value: A primitive value to control synths

Indicators

Level VU meter for the output level. Adjust set main amp or the level slider so it just touches red at its loudest. Shows one channel if the mix is mono.

Beat Audio beat count, plus section info: active section, count through it, current repeat, and next queued section.

Timing How late each beat was scheduled. Measured on the main thread, so it turns red when drawing and scheduling can't keep up (heavy visuals, screen recording), not when the audio is too expensive. For audio load use the Audio meter, or limutAudio.stats() in the console for the live worklet voice count (superosc, chaos, pwm).

Visual Green when the framerate keeps up with 60 fps; red when struggling, which may cause visual glitches.

Audio Audio thread load: the fraction of each audio callback's time budget used. Red as it nears full, when you will hear clicks and dropouts. All synths share one audio thread regardless of CPU cores. Hover for details. Usual causes: many superosc voices (unison multiplies the cost), chaos or pwm voices, long rel, big room/echo on many players.

Desktop (Electron) build only.

Limiter Red when the internal limiter is active. It sits before main amp and the VU meter, so if it lights up, lower amp on individual players and raise main amp to compensate.

Console

The console is the text window below the main code editing area. Use the console to see information, warnings and command output. Type a command and press enter to execute it.

list List all player base types, including presets, grouped by player base type and the include file that defined the preset

list audio List all player base types, filtered to those where the base type contains the given string

Collaboration

Live collaboration between browsers over peer-to-peer WebRTC (peer.js). One peer hosts and relays messages between the connected clients. A public peer.js signalling server and TURN relay are used to establish connections.

Console commands

server Start hosting a session. The console will print a Peer session id; share that id with anyone who wants to join. The server's current code, run state, slider values, gamepad state and cursor are sent to each client when they connect.

server id Start hosting using id as the session id rather than a randomly assigned one. Useful for stable, memorable session names. The id must be unique on the peer.js network at the time the server starts.

connect id Connect to an existing session by its peer session id. On connect, the client receives the host's current code and run state and from then on stays synchronised.

connect id name Connect using name as your peer id rather than a randomly assigned one. Other peers will see this name in cursor labels, chat messages and connect/disconnect notifications. The name must be unique on the peer.js network.

say 'message' Send a short chat message to all connected peers. The message is shown in the console of each peer, prefixed with the sender's peer id. Quotes around the message are optional.

disconnect peer-id Server only: forcibly remove the named peer from the session. Useful when a peer has gone away without cleanly disconnecting (e.g. closed their browser or lost network) and the server still has the stale connection. Has no effect on clients.

What is shared

Code When a client connects, the host's current code is sent in full and replaces the client's code. After that, code edits made by any peer are sent as incremental changes and applied to all other peers in real-time. Either side can edit the code.

Run state Pressing run/stop on any peer starts or stops the code on all peers.

Cursors Each peer's text cursor position is shown as a coloured caret with their peer id as a label in every other peer's editor. Each peer is assigned a stable colour derived from their peer id.

Sliders Slider value changes (see the slider function) are broadcast to all peers, so every peer sees the same slider values.

Gamepads Gamepad axis and button state from each peer's locally connected gamepads is shared with every other peer. Remote gamepads are made available to gamepad functions alongside any local ones, so any peer's controller can drive the music.

Metronome The host periodically broadcasts its current beat time and bpm; clients gently slew their metronome to match, so all peers stay in time even if the host changes the bpm.

Chat The say command shares short text messages between peers in the console.

Notes

The connection is peer-to-peer and end-to-end between browsers; code and chat are not sent through any Limut-specific server. However, the public peer.js signalling server and a public TURN relay are used to establish and (if direct connection fails) relay the WebRTC connection.

Audio is rendered locally on each peer using their own synthesised output. The shared code, run state and metronome timing keep the audio in sync rather than streaming audio over the network.

If the host (server) disconnects, clients lose the session. Clients can disconnect and reconnect at any time without affecting the host or other clients.

Syntax Principles for Expressions

() Round brackets define chords (multiple events or values at the same time) in both patterns and expressions. In an expression members are comma separated (add=(0,4,7)); in a pattern they are written together (p1 lead (047)), and a comma is a parse error.

[] Square brackets define sequences in both patterns and expressions; that is, multiple events or values one after another over time (sequences/timevars)

<> Angle brackets define higher level sequences in patterns; that is, a sequence where the next event is selected when the entire pattern repeats next.

{} Curly brackets in an expression supply extra information to a param (subparams), value (modifiers) or function call (arguments)

@ Defines the interval for an expression; that is, how often it is evaluated within the event. The interval applies to the nearest value: in this.foo@e it applies only to foo; use (this.foo)@e for the whole lookup.

Basic Syntax

Limut is case insensitive; piano, Piano, and PIANO are all treated the same; [1:4]t4 is the same as [1:4]T4 etc.

Comments

Anything after // on a line is ignored

Anything within a /* ... */ comments is ignored

Commands

A command defines a player, preset or section, sets overrides or global vars, or includes a file. Commands may span multiple lines; a new command starts at any line beginning with include, set, preset, section, or the id type pattern player syntax.

Example:

    include 'preset/synthwave.limut' // Command that includes a limut source file from a URL
    preset mysynth ping, amp=3       // Command that creates a preset synth based on the 'ping' synth, but overriding the amp value
    set bpm=130                      // Command that sets the global bpm (beats per minute) var to 130bpm.
    kick play x., amp=2,             // Start of a command that defines the player called 'kick', of type `play`, with an amp (volume) of 2
      drive=1/2                      // Continuation of the kick player definition, setting its drive param to 1/2
    set kick drive=0                 // New command defining an override for the kick player, setting its drive param to zero. Overrides like this take precedence over params set on the player itself.
    section drop, length=8           // Command that defines a section called 'drop', with a length of 8 beats. Players and vars can look up the current section and its params.
  

Defining a player

Basic structure: id synth pattern, params

A player command begins when something matching id synth pattern begins a new line. The params may follow on subsequent lines.

id Identifier for the player, so it can be referred to from other players etc. Must contain only alphanumeric characters from the basic Latin alphabet or underscore, and must not start with a digit. No player can be named set, preset, section or sx as those are reserved keywords.

synth Name of the audio or visual synth to use; eg piano

pattern Pattern of notes to play. Specifies the notes or sounds to play, and when to play them. Ends at the first comma; it can also be supplied (or overridden) as the pattern param instead, eg p ping 0, pattern=`0246`. A pattern param given by a preset is only a default, overridden by the pattern on the player line

params Comma separated list of params (eg amp=1/2) that affect how the player plays; see 'Value Syntax' and 'Params' sections below

Overriding player params:

Basic structure: set id params

A set overrides command begins when set begins a new line. The id and params may follow on subsequent lines.

id Player to override. Can be a list (set (kd,sd) amp=0), a wildcard (set p* ..., set * ...), or an exclusion (set !p ... for all players except p). It can also be a section name, or section / sx; see 'Overriding section params' below.

params Comma separated list of params (eg amp=1/2) that affect how the player plays; see 'Value Syntax' and 'Params' sections below

modification overrides params can be defined using operators to modify existing values rather than override them; eg add+=2 will increase the add parameter by an additional 2 on top of what it was already set to.

Setting global vars:

Basic structure: set var = value

A set global var command begins when set begins a new line. The var and value may follow on subsequent lines.

var variable to set; either a predefined var (see 'Predefined Vars' section below) or a user defined var which can be referenced from params on any player(s)

value expression value to set; see 'Value Syntax'

Creating a synth preset:

Basic structure: preset name synth, params

A preset global var command begins when preset begins a new line. The params may follow on subsequent lines.

name The name for the preset. The preset can be used by players by referring to this name as the player type. Must contain only alphanumeric characters from the basic Latin alphabet or underscore, and must not start with a digit.

synth The existing synth (or preset) to base this preset on

params Series of param overrides to apply for players that use this preset

Including a limut source file:

Basic structure: include 'url'

Include a limut source file. The file will be downloaded from the given URL and then executed as limut code. The file will only be downloaded once, but will be executed every time the code is updated.

url The URL of the limut source file to include. Must be in single quotes as a limut string. Include standard limut libraries from eg 'preset/synthwave.limut'. Include limut source from anywhere on the web (CORS permitting) using the full URL; eg 'https://sdclibbery.github.io/limut/preset/synthwave.limut'

Defining a section:

Basic structure: section name, params

Sections describe the large scale structure of a piece (intro, build, drop, breakdown etc). A section is just a named bag of params that is active for a span of beats. Players and vars can look up those params (see 'Lookup examples' below) so that the whole piece can respond to which section is currently playing and how far through it we are.

A section command begins when section name begins a new line, optionally followed by a comma and params. The params may follow on subsequent lines.

name The name for the section, used to refer to it from set section.next / set section.active and from param lookups (eg drop.riser). Must contain only alphanumeric characters from the basic Latin alphabet or underscore, and must not start with a digit.

params Comma separated list of params (eg riser=[0:1]l16@f), evaluated the same way as player params. Any param can be looked up from the section; see 'Value Syntax' below.

Compound assignments combine with the param's default, not the previous definition: section drop, length*=2 is 64 beats, and section drop, length=8, length+=4 is 12.

length The length of the section in beats. Defaults to 32. When the active section has been playing for this many beats it ends and the next section starts. May be an expression involving random or time vars, eg section drop, length=[4,8]r; it is evaluated when the section becomes active and held for that whole run (all its repeats).

next The section to queue when this one becomes active, eg section verse, next=chorus, so the piece sequences itself. An explicit set section.next=name overrides it. Can be an expression, eg next=(chorus,verse)r, chosen afresh each time.

repeat Times the section plays before advancing, eg section verse, length=8, repeat=4, next=chorus. Each repeat restarts time/riser/fall; block players keep sounding. Default 1. May be an expression (repeat=[1,3]r), evaluated when the section becomes active.

Standard section functions:

Every section (including default) defines these functions. Look them up with section.name (the active section, or sx.name) or drop.name (a named section). Override any of them by giving the section a param of the same name.

active 1 when the section is the active section, 0 otherwise.

in Alias for active.

exists 1 for any section that has been defined, 0 otherwise (eg drop.exists).

time The number of beats elapsed through the section, starting at 0. 0 when the section is not active.

rtime The inverse of time: the number of beats remaining, counting down from the section length to 0 at its end. Equal to the section length when the section is not active.

riser Ramps from 0 at the start of the section to 1 at its end. 0 when the section is not active.

rise Alias for riser.

fall Ramps from 1 at the start of the section to 0 at its end. 1 when the section is not active.

count Which repeat of the section is currently playing, counting from 0. Resets to 0 when the section becomes active and increases by 1 each time it reaches its length. Handy with repeat, eg section.count==section.repeat-1 is 1 on the final repeat. 0 when the section is not active.

Overriding section params:

Basic structure: set name params

Section params can be overridden like player params: set drop length=4, set drop riser*=2, set default length=4. Any param works, including length, repeat, next and undeclared ones. Modification overrides combine with the section's value.

Overrides are read live, so they take effect immediately, even mid-section (unlike a length expression, fixed when the section starts). Remove the line and the declared value returns.

set section params (or set sx params) overrides whichever section is currently active rather than a named one, matching how section.riser reads the active section. If a section also has an override by name, the keyword one is applied on top of it.

If a player and a section share a name, the player wins and the override applies to the player — the same precedence param lookups use, where drop.amp reads the player drop if there is one. Wildcards (set p* ...) only ever match players.

set drop next=chorus follows the same rules as the expression form of next rather than the bare name on the section line, so a var of that name would be resolved first; set drop next=(chorus,verse)r works as you would expect.

Section-scoped code:

Block structure:

    section drop {
      set p* vel=0
      f perc f, vel=drop.riser^3
    }, length=16
  

A section can carry a block of code lines that are only active while it is active. Open with { at the end of the section line, put commands (players, set, presets) on lines inside, and close with } at the start of a line, optionally followed by a comma and the section's params. Sections cannot be nested.

When the active section changes the code re-runs: block players for the new section are created and those for the old one removed, so the same player id can differ per section. Block set overrides only apply while the section is active. The section's own params are always defined.

set section.active=name and set section.next=name lines are ignored during these automatic re-runs (otherwise they would refire on every section change); they still apply on a normal code update.

Section timing:

There is always exactly one active section. Until you select one, the built in default section (length 8) plays and repeats. It can be redefined like any other section (params, next, a code block) and referred to by name (default.riser, set section.next=default).

At the end of its length (and any repeats), playback advances to the queued next section, or to default if none is queued. Queuing is consumed when the section starts.

The readout in the top bar shows the active section, how many beats into it we are, its length, and the next section; eg drop 5/16 -> default. When the section has a repeat it also shows the current play, eg drop 5/16 (2/4) -> default.

Triggering a section:

Below the readout is a button per defined section. Click to queue (or unqueue) it for when the current section ends; shift click to switch immediately. The playing section is highlighted and the queued one outlined.

Clicking is usually preferable to putting set section.next=name in the code, because the code lines are re-run every time you update the code, so they re-trigger the section on every update until you comment them out again. A button click happens exactly once.

Patterns

x-o- Sequence of events, each with duration given by the player's dur param, for use with play

0123 Sequence of events, each with duration given by the player's dur param, for use with tonal synths. Numbers are scale degrees

`01 23` Pattern literal: whitespace inside backticks is ignored, and the contents are always literal, so `01 loop 1` is 01loop1. Double quotes are equivalent; use them inside a backtick pattern param, eg pattern=`"01 23" + "45 67"`

0-1-2-3 Negative scale degrees allow tones below the synth root pitch

x. Use a dot for a rest

0# # following numeric event makes it sharp

0b b following numeric event makes it flat

0= = following numeric event extends the duration by 50%

0! ! following numeric event shortens the duration by 33%

0^ ^ following numeric event makes it 50% louder

0v v following numeric event makes it 33% quieter

0a Other characters following numeric event become params; for example t tri 01g23, glide=this.g/2

x^ ^ following non-numeric event makes it 50% louder (for use with play synth)

0_[_1] Continuation: use underscore to extend the previous note duration. Currently continuations are not valid within chords () or supersequences <>

0[123] Subsequence: square brackets to fit multiple events into one dur (triplet shown)

0(135) Chord: Round brackets to play multiple events together

0<12> Supersequence: angle brackets to play events in sequence as the pattern repeats. Eg 0<12> is exactly equivalent to 0102

01 loop 2 Play the pattern a limited number of times (twice here), then stop. It won't restart until the player is recreated, playback restarted, or the pattern changed.

0123 crop 2 Crop a pattern to a given number of steps. For example, 0123 crop 2 is equivalent to 01. Crop will repeat the pattern if necessary, eg 01 crop 5 is equivalent to 01010

1 + 2 Concatenate two patterns; the first pattern plays then the second. For example, 12 + 34 is the same as 1234

1 * 2 Repeat a pattern; the first pattern plays a given number of times. For example, 12 * 2 is the same as 1212

now 01 Play the pattern starting on the next beat, rather than as if it had been playing from beat 0. In the example, the pattern 0123 will play starting from the 0 whatever beat the player is started on.

follow p1 Use follow to follow another player (named 'p1' in the example) pattern

gamepad Use gamepad to generate notes from a gamepad controller rather than a pattern. The gamepad buttons will be mapped to note degrees in some arbitrary way.

gamepad 1 use input from gamepad 1 (default is 0)

gamepad lt:vel Use the left trigger (assumed to be analogue) to control the given param (vel for velocity of the gamepad notes). When the lt: arg is used, the left trigger will not play a note itself.

gamepad rt:echo Use the right trigger (assumed to be analogue) to control the given param (echo in the example). When the rt: arg is used, the right trigger will not play a note itself.

gamepad nodpad On some controllers the dpad can be in the way of the sticks. Using 'nodpad' will disable the dpad input.

midi Generate notes from a midi controller rather than a pattern. White notes map to scale degrees from degree 0 at the A below middle C (so white notes are always in key), black notes sharpen the white note below, so the G# below A gives a minor key's leading note. The A above is degree 7; oct shifts the keyboard. this.vel is the note velocity, 0 to 1.

this.press Aftertouch (channel or polyphonic), 0 to 1, updated live while the note is held, separate from vel: eg l dlead midi 1, lpf=1000+this.press*4000. It holds its last value through the release. Where there is no note event (fx, visuals), use avw2.press.

midi 9 use input from midi channel 9 (default is 0)

midi 9 1 use input from midi channel 9 on port (device) 1 (default is 0)

midi abs 9 0 use input from midi channel 9 port 0 and map it absolutely: the player is set to a chromatic scale and the note value and oct are such that the keyboard note played is the same note played in limut, ignoring the current scale and key

midi perc 9 0 use input from midi channel 9 port 0 and treat it as percussion, so instead of mapping to a note scale value, map to limut percussion samples. For example, midi note 36 (Bass drum 1) maps to 'X' (Heavy kick)

avw2 Play from the Alesis Vortex Wireless 2 keytar, found by name wherever it is plugged in (no port needed). Notes map as for midi, and avw2 abs, avw2 perc and avw2 9 (channel) work the same way. The keys are on channel 1 unless you give one. eg l keytar avw2

this.vel The softest strike gives vel 1/2 and the hardest 1, times the neck slider (avw2.s1). The slider applies live to held notes as well, so it works as a tremolo. vel*= and vel= on the player line still apply on top.

keyboard Use keyboard to generate notes from your computer keyboard rather than a pattern. Hover the mouse over the ⌨️ icon in the UI to enable playing; notes only play from local key presses while the mouse is over the icon. Keys map to scale degrees by row: the home row asdfghjkl plays degrees 0 to 8, the top row qwertyuiop starts an octave up at degree 7, and the bottom row zxcvbnm starts at degree -7.

Ctrl Hold Control while pressing a key for half velocity.

Shift Hold Shift while pressing a key for full velocity.

Alt Hold Alt (Option on a Mac) while pressing a key to sharpen the note by a semitone.

Values

Basic structure value{modifiers}@interval

Note: the order of interval and modifier can be swapped if desired. For example, []r@f{seed:1} is the same as []r{seed:1}@f.

value Actual value (which may include subparameters); see below

{modifiers} Map of modifiers and/or arguments. Modifiers may modify the time passed to the value expression, or the value it returns, or provide additional data for a parameter. For example, [1,2,3]t1{0:0,per:8} will give a sequence that repeats every 8 beats, forced to the value 0 at the start of each repeat. In other words, the repeating sequence: 0,2,3,1,2,3,1,2. See "Modifiers" below.

@interval Intervals specify how often the value should be evaluated, for example once per event (then the same value through the event), or continuously varying. See "Intervals" below.

Values:

1 Single, constant numeric value

0dB Numeric values can be written in dB, converted to a gain at parse time: 0dB is 1, +20dB is 10, -20dB is 1/10. amp=6dB is about amp=2. dB is relative, so combine with multiplication: amp*=3db.

1# 1b For use with the audio add param, sharpen or flatten the value. This is parsed into {1,#:1} or {1,b:1}

100ms Single, constant numeric value with time units. Time units are valid for any param which has a default time unit. In general time units will be automatically converted between beats and seconds (using the current bpm), and also between time and frequency; so lpf=0.001s is equivalent to lpf=1000 because the default units for lpf is Hz.

1s Time in seconds

1000ms Time in milliseconds

1Hz Frequency in Hertz (cycles per second). Also 1cps is the same as 1Hz and 1kHz is 1000Hz.

1cpm Frequency in cycles per minute

1b Time in beats

1cpb Frequency in cycles per beat. Also 1kcpb is 1000cpb

No value: for a param, missing a value is the same as setting the param to 1. Eg monochrome is the same as monochrome=1

(1,2) A chord. Multiple values, all played together at the same time

[1,2] Sequence of values, one after another, one per event in the pattern, ignoring rests. For single length patterns only, all values will be expanded, so p ping 0, add=[0,1,2] will cycle through all three values.

[] Equivalent to [0,1].

[1,2]t3 Timed sequence, each value lasts for 3 beats. Duration must be a const number, cannot be an expression. Default time unit: beats.

[]t Equivalent to [0,1]t.

[1:12,2:4]t Timed sequence with variable timing per value. Durations can be expressions. Default time unit: beats.

[0:3]t1 Timed sequence, expanded. The example is equivalent to [0,1,2,3]t1

[1,2]l3 Timed linear interpolated sequence, each value takes 3 beats to change into the next. Default time unit: beats.

[]l Equivalent to [0,1]l.

[1:12,2:4]l Timed linear interpolated sequence with variable timing per value. Durations can be expressions. Default time unit: beats.

[1,2]s3 Timed smooth interpolated sequence, each value takes 3 beats to change into the next, using an 's' shaped ease in/out curve. Duration must be a const number, cannot be an expression. Default time unit: beats.

[]s Equivalent to [0,1]s.

[1:12,2:4]s Timed smooth interpolated sequence with variable timing per value. Durations can be expressions. Default time unit: beats.

[1,2]tx Section-relative timing: the x suffix counts time from the start of the current section, so the sequence restarts each section. Works with t, l, s, r and n, either side of the letter ([1,2]xt). With no sections defined, the default section restarts it every 8 beats.

[1,2]e Timed interpolated sequence, each value is evenly spaced from beginning to end of the current event

[1,2]e1 Timed interpolated sequence starting at the beginning of the event, each value takes 1 beat to change to the next. Duration must be a const number, cannot be an expression. Default time unit: beats.

[0:!300ms,1:_[1/4b:1/2b]r,0]e Timed piecewise sequence starting at the beginning of the event. In this case it would provide an envelope for the event, with an exponential attack lasting 300ms, followed by a const sustain lasting between 1/4 and 1/2 a beat at random. Default time unit: beats.

[1:!200ms,0]e@s As []e, but on an audio param (eg amp or lpf) it creates audio segments rather than per-frame updates, for more accurate envelopes. Adding an @f part makes the whole expression per frame. Time modifiers don't work correctly with @s.

[]r Select a different value every time, chosen randomly between 0 and 1

[1,4,7]r Select a different value every time, chosen randomly from the numbers 1, 4 and 7

[:9]r Select a different random float every time, between 0 and 9

[0:9]r Select a different random float every time, between 0 and 9. Note if this will be rounded to to an integer (eg for oct, add or sample params), then the upper limit is exclusive. eg oct=[2:3]r will give a float value between 2 and 2.9999...., so since oct rounds down to the nearest integer, it will always get oct=2

[0,1:7]r Select a different random float every time, either 0 or 1. The 1 is 7 times more likely than the 0.

[1,2]r4 Select a different value and hold for 4 beats, chosen randomly from the numbers 1, 2. The hold time must be a const number, it cannot be an expression. Default unit: beats.

[1:3]r{seed:1,per:4} Select a different determninistic random float every time, between 1 and 3; seed determines the random sequence; the sequence repeats every per beats. seed is also in beats, so adding 1 to seed pushes the sequence by 1 beat.

[]n Smoothly varying noise, moving between 0 and 1

[1:2]n Smoothly varying noise, moving between 1 and 2

[1:2]n4 Smoothly varying noise, moving between 1 and 2, taking 4 beats to progress from one value to another. The timing must be a const number, it cannot be an expression. Default time unit: beats.

[]n{seed:1,per:4} Smoothly varying deterministic noise, moving between 0 and 1; seed and per work the same as for []r

[ v1:i1t1, v2:i2t2, ... ]{p,repeat:0} Piecewise series. Allows interpolating between a set of values, specifying the interpolation and 'travel' between each pair of values, and allowing a custom parameter to control the interpolation. For example, [0:_1,1:\]{time*2} is controlled by double-speed time, starts at 0, stays constant at zero for 1 unit of double-speed time, then jumps to 1 and linearly falls back down to 0 for 1 unit of double-speed time.

v1 etc Expressions giving the values to interpolate between

i1 etc Interpolation operator

:/ :\ Linear interpolation operators; both do the same, but the two can be used to indicate direction

:_ Const interpolation operator: stay at original value right to end of step

:_ Step interpolation operator: immediately jump to new value from start of step

:~ Smoothed interpolation operator: bezier ease in/out s-shaped smooth transition through step

:! Exponential interpolation operator: exponential growth/decay to target

:^4: Power interpolation operator: power curve interpolation. In this example, the interpolation is raised to power 4, so it will move slowly to start with the quickly reach the target. :^1/4: would quickly move toward the target, then slow down.

t1 etc The amount of parameter p travel between this value and the next; note the sequence wraps round. Default time unit: beats.

p Parameter expression giving the current point of travel through the piecewise series

repeat Optional repeat parameter. Leave out for a repeating pattern, set to zero to disable repeat and clamp the value when the parameter is less than zero or greater than the full length of the piecewise.

vN: A trailing colon with no size on the last value is shorthand for repeat:0: the final value holds forever. [1:\2,3:]{x} ramps 1 to 3 over 2 units then stays at 3. vN:_ is the same; other trailing interpolators still repeat.

[v1,v2]:iN A default interpolator/size written after the bracket applies to every segment that doesn't specify its own; per-segment specs override it field by field. For example [1,2]:_8{x} is equivalent to [1:_8,2:_8]{x}, and [1,2:/4]:_8{x} is equivalent to [1:_8,2:/4]{x}. Interpolator and size default independently, so [1,2:4]:_8{x} is equivalent to [1:_8,2:_4]{x}.

+ Add values together; eg [2,3]+4 is equivalent to [6,7]

- Subtract values; eg [2,3]-1 is equivalent to [1,2]

- Unary minus operator; eg -[1,2] is equivalent to [-1,-2]

/ Divide values; eg [1,2,3]/2

* Multiply values; eg [1,2,3]*1000

% Take remainder; eg (0,4,7,9)%7 is equivalent to (0,4,0,2)

^ Raise to the power; eg 2^3 is 8. Imaginary results return 0; eg -1^1/2 returns 0.

| Concatenate chords; eg 1|2 is the same as (1,2), and (1,2)|(3,4) is (1,2,3,4)

?? "If then" operator. If the left operand is truthy return the right operand, otherwise nothing; eg this.foo??5. Short circuits. On visual nodes it compiles into the shader; see visual synths.

?: "Or else" operator. Return the left operand, or the right if the left is nothing; eg this.foo?:5. Combine with ?? for a ternary: this.foo>1 ?? 2 ?: 3. Short circuits.

. Lookup values in a player (with a param name), a section (with a param name), a chord (with an index or aggregator) or a map (with a field name). See Lookup Examples section below.

== Compare values for equality. Returns 1 if equal, 0 if not.

!= Compare values for inequality. Returns 0 if equal, 1 if not.

< Check if the left operand is less than the right. Returns 1 if left is less, 0 if not.

> Check if the left operand is greater than the right. Returns 1 if left is greater, 0 if not.

<= Check if the left operand is less than or equal to the right. Returns 1 if left is less or equal, 0 if not.

>= Check if the left operand is greater than or equal to the right. Returns 1 if left is greater or equal, 0 if not.

{x:0,y:1} Map, containing keys and values

{2,y:1} Map, containing keys and values, with a main value of 2. The main value is the same as if no map was used; eg chop={2,wave:'tri'} is the same as chop=2, but also supplies an additional 'wave' subparameter.

{r:1,g:0,b:0,a:1} RGBA colour; component range is 0 - 1.

{h:1,s:1,v:1,a:1} HSV + alpha colour; component range is 0 - 1, including hue. Note RGB components can also be specified as overrides; eg {h:1,b:1,a:1} will be magenta not red.

{labh:1,c:1,l:1,a:1} LabLCH + alpha colour; component range is 0 - 1, including hue. Note RGB components can also be specified as overrides; eg {labh:1,b:1,a:1} will be magenta not red.

#0369 Hex colour value which becomes the colour map {r:0,g:0.2,b:0.4,a:0.6}

#036 Hex colour value which becomes the colour map {r:0,g:0.2,b:0.4,a:1}

#00336699 Hex colour value which becomes the colour map {r:0,g:0.2,b:0.4,a:0.6}

#003366 Hex colour value which becomes the colour map {r:0,g:0.2,b:0.4,a:1}

#.f. Hex colour value with . for an absent channel, which becomes the colour map {g:1,a:1}. Absent channels are left out of the map, so they keep whatever value the param already had; eg fore=#..0f becomes {b:0,a:1} and only changes the blue and alpha of the foreground colour. In the 6 and 8 digit forms an absent channel is two dots; eg #..ff.. becomes {g:1,a:1}. Note that a colour can still be followed by a lookup, eg #e000.r is the red component 0.93.

'abc' String value. Strings must be defined on a single line; multiline strings are not supported. However, line breaks can be inserted with \n

`abc` String value; backticks are exactly equivalent to quotes, but are highlighted as a pattern and read better where the string is a pattern, eg pattern=`0123`

foo Lookup a global var named foo. If there is no var, predefined var, or var function named foo, then the string 'foo' will be returned instead. If there is a var named foo, but no arguments are provided, then the string 'foo' will be returned instead.

foo{x:2} Lookup a global var named foo, and call it as a function passing in named parameters.

Modifiers:

{time:time} Provide an expression to give the modified time value. For example value{time:time*2+8} will run time at double speed, with an offset of 8.

{per:8} Repeat a sequence [1,2,3]t1{per:8} will give a sequence that repeats every 8 beats.

{per:8,0:7,2:3} Force the value to evaluate to 7 at the start of each repeat, and 3 on the second beat of every repeat. Note this only works if per is used to make a repeating sequence. Default time units: beats.

{step:2} Advance time in discrete steps only [1,4]l4{step:1/2}@f will give a sequence that changes value only every 1/2 beat. Default units: beats.

Intervals:

@e Use @e to evaluate this value once per event

@f Use @f to evaluate once per frame (60 times a second). For example lpf=[300:3000]l8@f will update the lpf cutoff frequency continuously, while lpf=[300:3000]l8 will be evaluated only once for each event played.

@s Evaluate per segment for audio params, for accurate envelopes: amp=[1:!200ms,0]e@s creates a single exponential segment, where @f would update 60 times per second. Mixing @s and @f in one expression makes the whole expression per frame.

Lookup examples

p1.amp Lookup the value of param amp from player p1. Returns a chord of values from all currently playing events.

p1.amp.0 Lookup the value of param amp from player p1, and if it contains a chord, extract the first element only from it.

drop.foo Lookup the value of param foo from the section named drop (defined with section drop, foo=1/2).

section.foo Lookup the value of param foo from the currently active section. section is a reserved keyword referring to whichever section is playing now.

p1.pulse.max Lookup the value of the pulse from player p1, and if it contains a chord, extract the largest element only from it. The pulse param gives a smoothed approximate envelope shaped value between 0 and 1 for each event.

p1.pre Tap player p1's audio for use in an fx chain, eg q ping, fx=gain{p1.pre} ring-modulates q with p1. The tap is after p1's filters/pan/amp but before its room/echo, fx and bus (for a bus, its input). p1 still plays normally; route it to bus=silent to use it only as a modulator. p1 may be defined later; an unknown id is silent. A bus fed by a player tapping that bus's own .pre is a feedback loop and is silent unless it contains a delay. Audio fx only.

this.value Get param value from the current event. Returns a single value (not generally a chord) from the event the expression is being evaluated on.

([0,1]t1@f).accum Smoothly accumulate 1 every other beat

{foo:2}.foo Returns a field in a map (returns 2 in this example)

(3,1,2).0 Returns the nth element in a chord (returns 3 in the example)

(3,1,2).3 Returns the nth element in a chord, wrapping around (returns 3 in the example)

(3,1,2).(0,2) Returns multiple elements in a chord (returns (3,2) in the example)

(3,1,2).first Returns the first element in a chord (returns 3 in the example)

(3,1,2).last Returns the last element in a chord (returns 2 in the example)

(3,1,2).rand Returns a random element from a chord (returns 1, 2 or 3 at random in this example)

(3,1,2).min Returns the smallest element in a chord (returns 1 in the example)

(3,1,2).max Returns the largest element in a chord (returns 3 in the example)

(3,1,2).count Returns the number of elements in a chord (returns 3 in the example)

(3,1,2).sum Returns the sum of all elements in a chord (returns 6 in the example)

(3,1,2).avg Returns the mean average of all elements in a chord (returns 2 in the example)

User Defined Functions

Basic structure: {args} -> body

User defined functions (also called lambdas) let you build reusable expressions. The {args} declares the parameters, and the expression after -> is the function body. Functions are values like any other, so they are usually stored in a global var with set and then called by name.

Defining a function

set double = {value} -> value*2 Define a function called double with one argument called value, which returns the argument doubled.

set square = {x} -> x^2 Argument names can be anything; they are referenced by name inside the body.

set add = {x,y} -> x+y Multiple arguments are separated by commas.

set hello = {} -> 'hi' A function with no arguments still needs the empty {} on the left.

set lpf = {freq, q:5} -> biquad{'lowpass', freq:freq, q:q} Arguments can have default values using name:default. If the caller does not supply that argument, the default is used. Arguments without a default are required.

Calling a function

double{3} Call with a single positional argument. Returns 6.

add{3,4} Call with multiple positional arguments. Returns 7.

add{x:3,y:4} Call with named arguments. Order does not matter; add{y:4,x:3} gives the same result.

add{3,y:4} Positional and named arguments can be mixed.

hello{} No-argument calls can optionally include the {}

hello A user function can still be called without an arg list.

Piping with >>

3>>double The >> operator feeds the value on its left into the call on its right as its first argument, so this is double{3}. Any positional arguments already at the callsite shift up a place, so 3>>add{4} is add{3,4}.

3>>double>>add{1} Piping reads left to right in the order things happen, which is a lot clearer to read and quicker to edit while performing than the equivalent nested calls (add{double{3},1}).

set scaled = {in, by:2} -> in*by A function meant to be piped into should declare the piped value as its first argument, so that 4>>scaled{3} gives 12.

(1.7).floor{1/2} The . operator passes its left side in exactly the same way when the right side is a call with an argument list. It binds tightest where >> binds loosest, so it suits a single call inside a larger expression.

Node functions are the exception: for them >> connects audio rather than passing arguments, so osc{}>>lpf{500} wires the oscillator into the filter as it always has. The same applies whenever the value on the left is already an audio node, so effects built as user functions still connect: shifter{3/4}>>reverb{1b}.

Positional argument names

Positional arguments are looked up by the names value, value1, value2 etc, in order. So {value,value1} -> value*value1 can be called as foo{3,4} to get 12. This means that the first argument name value is special: if you declare {value} -> ... then the first positional arg fills value, and you can also pass it by name as foo{value:3}.

A consequence is that a function with a first argument named value can be called very concisely. This is how many library functions (for example mix2, echo, and the shapers in lib/nodes.limut) accept their main argument positionally without the caller writing any name.

Inline (anonymous) functions

({x} -> x^2){3} A function can be called immediately without storing it in a var, by wrapping the definition in round brackets and applying arguments. Returns 9.

convolver{{x} -> (1-x)^3, length:1s} Anonymous functions are often passed directly as arguments to other functions (for example to node functions such as convolver and shaper) to provide a shape or envelope.

set twice = {f,x} -> f{f{x}} An argument that holds a function can itself be called with arguments inside the body. For example twice{{v}->v*2, 3} returns 12. This allows wrapper functions around node functions like parallel, for example set wrapped = {chain,count} -> parallel{{i}->chain{i}, count} (the library multiband function is built this way).

Scope and global vars

Inside a function body, argument names take priority over global vars with the same name. So in {foo} -> foo, the foo in the body refers to the argument, not to any global var called foo.

global.foo Use the global. prefix inside a function body to force a lookup of the global var, bypassing any argument of the same name. For example {foo} -> global.foo ignores the passed argument and returns the global foo.

Global vars that are not shadowed by an argument are visible inside the body as normal. So set gain = 2 followed by set amp = {x} -> x*gain will multiply x by whatever gain is when amp is called.

What a function can return

The body is a normal expression, so it can return anything an expression can produce: numbers, chords, timevars, maps, strings, or audio node chains. For example {freq} -> osc{'sine', freq:freq} returns an audio node; {} -> []r@f returns a per-frame random value. Interval markers (@e, @f, @s) inside the body carry through to the caller as expected.

Passing a timevar as an argument works naturally: double{[4,5]t1@f} evaluates the timevar on each frame and doubles each value.

Examples

set clamp = {x, lo:0, hi:1} -> min{max{x,lo},hi} Define, then call as clamp{0.7}, clamp{1.5, hi:2}, etc.

set mix2 = {chain} -> mix{chain, 1/2} A thin wrapper around a node function (as used in lib/nodes.limut).

set echo = {time:1/8b, feedback:0.7, max} -> delay{time, feedback:feedback, max:max??max?:time*2} A library effect built from the delay node function (as used in lib/effects.limut).

Audio Synths

! / stop / none

Base synths

audiosynth Base type with no defined audionode graph. The play param takes an audionode graph for each note. Note the player freq param is predefined for this player type as the note pitch derived from the pattern and other usual player params (add, oct, scale etc).

bus Audio mix bus. Plays continuously; a pattern is ignored, so all params are evaluated per frame, and chords and []e are not valid. Players choose a bus with the bus param. A bus has wave effects (compress, drive, fold etc) but no pitch effects (addc, vib etc). The implicit main bus is the final mix; set its params with overrides, eg set main echo=1/2. The implicit silent bus has amp=0: route a player there (bus=silent) to hear it only via its .pre tap, eg as a modulator.

external take audio from microphone or line in. Use the track param to specify the track within the stream. If the input signal appears on only one stereo channel (or you only want the signal from one stereo channel), use the channel param to select the channel and force it to mono.

track=1 Select which audio track to use from the input.

channel=1 Select a single audio channel to use from the input.

fm FM base synth; allows configuration of FM operators and envelopes. Each of up to 6 operators is specified using params op1 to op6:

op1={ratio:5.19,target:3,wave:'saw',depth:0.8,att:0.01,rel:0.1} ratio: frequency ratio to the event frequency. target: operator number to modulate, or 'out' for the synth output. wave: waveform. depth: modulation amount (scaled by note frequency). att/rel: operator envelope, in beats.

impulse Play an atonal impulse; that is a single pulse of millisecond duration.

io808 TR-808 simulation based on IO-808. Eg p io808 0.9, type='bd' plays a pattern with two bass drum hits, one unaccented then one with maximum accent. Event duration is 1/4 beat (16th note) to correspond to 808 sequencer.

type='bd' type of 808 sound: 'bd', 'sd', 'oh', 'ch', 'cb', 'cp', 'ma', 'ht', 'mt', 'lt', 'hc', 'mc', 'lc', 'cl', 'rs', 'cy'. The value from the pattern determines the level of accent placed on each event.

level=1 Output level. Used on all types.

tone=1/2 Tone control; used on bd, sd, and cy.

decay=1/2 Decay time; used on bd, cy, and oh.

snappy=1/2 Snappy control for sd.

tuning=1/2 Tuning control for toms and congas ht, mt, lt, hc, mc, and lc.

multiwave Synthesizer providing multiple waveform oscillators which can have their amp and detune controlled individually and dynamically. The waves are specified using params wave1, wave2 etc:

wave1={'saw',amp:[0:1]n,detune:wow} The main value is a string giving the waveform to use. amp is a per frame amplitude control. detune is a per frame detune control in semitones.

noise Noise pad. Note pattern value is not used; white noise is produced for all values (uses AudioWorklet so may use more audio render capacity than other synth types).

piano Sampled piano

guitar Sampled electric guitar, recorded dry from the pickup; eg g guitar (024), dur=1/2, drive=1/8, fx=cabinet{}. Plays at oct=3, and vel below 0.72 picks a softer pluck. It sounds thin on its own: add drive and cabinet{}, or use the guitar presets. addc, glide and vib bend the pitch.

dead=0 When set, play a muted string "chuck" instead of the note; the string is picked from the note pitch, so a chord of dead notes scrapes across strings. eg g guitar 0[.0]0., dead=[0,1]

fretnoise=1 Level of the finger release noise at the end of each note; quieter the longer the note was held. 0 turns it off.

pitchedperc Synthesised pitched percussion for kick drums, snare, toms etc. Note that as this synth is not sample based, it only accepts digits in the player pattern, unlike the play synth. The sound is made up of click, hit, body and rattle components. The synth has a number of unique params:

click={1} The click is the initial impact sound. The main param is the loudness of the click. The default values are shown.

hit={0,sample:'^',index:1,rate:3/2} The hit is a sample to be played with the initial impact sound. The main param is the loudness of the hit. Sample, index and rate control the sample playback (similar to the play synth). The default values are shown.

body={1,att:5ms,dec:400ms,freq:55hz,boost:150hz,pitchatt:0ms,pitchdec:50ms,wave:'sine',saturation:0} The resonant, pitched body. Main value is loudness; att/dec the envelope; freq base frequency and boost initial pitch boost, swept by pitchatt/pitchdec; wave the waveform; saturation tanh drive. Envelope times default to seconds.

body2={0,att:5ms,dec:400ms,freq:55hz,boost:150hz,pitchatt:0ms,pitchdec:50ms,wave:'sine',saturation:0} Second body tone. May be useful for snares and toms etc.

rattle={1,att:0ms,dec:30ms,rate:1,filter:'lowpass',freq:55hz,boost:205hz,pitchatt:0ms,pitchdec:50ms,q:18} The noisy component. Main value is loudness; att/dec the envelope; rate the noise sample rate (extra filtering); freq base filter frequency and boost initial boost, swept by pitchatt/pitchdec; q resonance. Envelope times default to seconds.

play Play samples. Use letters, symbols and digits 1-4 in the pattern to choose the sample (see below for full list). Note duration defaults to 1/2.

pwm Pulse width modulation synth. A pulse wave with variable pulse width, from pulse to square. Note: uses AudioWorklet so may use more audio render capacity than other synth types. Also note there may be aliasing, so you may want to apply some filtering to cut harsh high frequencies.

pwm=1/2 Set the pulse width from 0 to 1. The default is 1/2, a square wave (50% duty cycle). values very close to 0 and 1 will produce no sound since the actual pulse width will become vanishingly small.

sample Pitched sample player. Use the 'sample' param, and then use it as a pitched synth. Use the start param to specify the playback start time within the original sample in seconds. Note: if the rate param is set, then this sets the sample playback rate and overrides the value and add params, which are ignored.

speak Robotic text-to-speech synth: speaks the text param (via meSpeak) and plays it like a pitched sample, transposed by the note (0 = natural pitch). The engine loads on first use, so the first phrase may be silent; results are cached. If rate is set it overrides value and add. Voice params: pitch (0-99, default 50), speed (words per minute, default 175), wordgap (units of 10ms), amplitude (0-200, default 100), variant (eg 'f2', 'm3').

superosc Wavetable oscillator. The wt param morphs across the wavetable's frames. Silent until the wavetable has loaded. Uses an AudioWorklet, so costs more audio render capacity than other synths.

wavetable={'sample/wt64/SUPERSAW.WAV', count:64, smooth:0} Sample URL, sliced into count single-cycle frames (default 64). Use count:1 for single-cycle files, eg sample/wave/SAW.WAV, SINE.WAV, SQUARE.WAV, TRI.WAV. smooth (0 to 1) reduces clicks when using an ordinary sample rather than a wt64 table. Defaults to a single-cycle saw.

Available wt64 wavetables (256)

Multi-frame wavetables in sample/wt64/, used as wavetable='sample/wt64/NAME.WAV':

111___00.WAV 111.WAV 303.WAV A_55HZ_-.WAV AAHWOHYE.WAV ACID_SP.WAV ADDITIVE.WAV AKWF_FMS.WAV ALIEN_SP.WAV ALIEN_VO.WAV ALPHA_2_.WAV ALTO_SAX.WAV AMEN_LOO.WAV AMEN.WAV ASSYMETR.WAV AUDIOTER.WAV BANK_410.WAV BANK_A.WAV BANK_B.WAV BANK_C.WAV BASS_BY_.WAV BBELLS.WAV BEST_OF_.WAV BOWING.WAV BRAIDS01.WAV BRAIDS02.WAV BRAIDS03.WAV BRAIDS04.WAV CHEBYSHE.WAV CLOCK_MU.WAV CRUSH_AD.WAV CYBERNET.WAV CYBORG.WAV DIRTY_00.WAV DIRTY_01.WAV DIRTY_02.WAV DIRTY_HA.WAV DOSE_WIT.WAV ELOB_A.WAV ELOB_B.WAV ELOB_C.WAV ENSHTU02.WAV ENSHTU03.WAV ENSHTURZ.WAV ESQ1-HI.WAV ESQ1-LO.WAV EUCLIDEA.WAV FAIRLI01.WAV FMADDI02.WAV FMADDITI.WAV FOLDFEED.WAV FOLDING_.WAV FOURIER.WAV FOURIER2.WAV FRED_DUR.WAV G2_ASTRA.WAV GENTLE_M.WAV GEOMETRI.WAV GLITCHBO.WAV GRAV-A1.WAV GRAV-A10.WAV GRAV-A2.WAV GRAV-A3.WAV GRAV-A4.WAV GRAV-A5.WAV GRAV-A6.WAV GRAV-A7.WAV GRAV-A8.WAV GRAV-A9.WAV GRAV-B1.WAV GRAV-B10.WAV GRAV-B2.WAV GRAV-B3.WAV GRAV-B4.WAV GRAV-B5.WAV GRAV-B6.WAV GRAV-B7.WAV GRAV-B8.WAV GRAV-B9.WAV GRAV-C1.WAV GRAV-C10.WAV GRAV-C2.WAV GRAV-C3.WAV GRAV-C5.WAV GRAV-C6.WAV GRAV-C7.WAV GRAV-C8.WAV GRAV-C9.WAV GRAVITYM.WAV HARMONIX.WAV HMMMMMMM.WAV HORROR.WAV HVOICEA.WAV HYPERBOL.WAV ISOBELLE.WAV ISOLDE.WAV JUST_RAN.WAV KERMIT_R.WAV KOMPLE01.WAV KONBANWA.WAV KUATO.WAV KYMA_PAR.WAV LFO_PL00.WAV LFO_PLAY.WAV LIGHT_00.WAV LIGHT_YE.WAV LOFIRISE.WAV LOM_A.WAV LSDJ_WAV.WAV MELLOW_D.WAV MICRO_Q_.WAV MICROBRU.WAV MK_DWG_H.WAV MODDROP.WAV MS2K.WAV MUTATION.WAV NOMAD.WAV ORGANIC_.WAV PD1-1.WAV PD1-2.WAV PD1-3.WAV PD1-4.WAV PD101.WAV PD102.WAV PD103.WAV PD104.WAV PHANTOMS.WAV PISTON_H.WAV PLAITS01.WAV PLAITS02.WAV PLAITS03.WAV PPG_UPPE.WAV PPG_WA00.WAV PPG_WA01.WAV PPG_WA02.WAV PPG_WA03.WAV PPG_WA04.WAV PPG_WA05.WAV PPG_WA06.WAV PPG_WA07.WAV PPG_WA08.WAV PPG_WA09.WAV PPG_WA10.WAV PPG_WA11.WAV PPG_WA12.WAV PPG_WA13.WAV PPG_WA14.WAV PPG_WA15.WAV PPG_WA16.WAV PPG_WA17.WAV PPG_WA18.WAV PPG_WA19.WAV PPG_WA20.WAV PPG_WA21.WAV PPG_WA22.WAV PPG_WA23.WAV PPG_WA24.WAV PPG_WA25.WAV PPG_WA26.WAV PPG_WA27.WAV PPG_WA28.WAV PPG_WA29.WAV PPG_WA30.WAV PPG_WA31.WAV PROPHET_.WAV PUSH_THE.WAV PWN_SAW.WAV QUACK.WAV QUX_FMY.WAV RADIUS.WAV REALIZE.WAV RETRO_SP.WAV RINGSHAP.WAV ROFL.WAV RRLYRQ5.WAV RRLYRQ6.WAV RRLYRQ7.WAV SAND_EYE.WAV SAW_BEND.WAV SAW_RING.WAV SINE_A01.WAV SINE_A02.WAV SINE_A03.WAV SINE_MUT.WAV SINE2SAW.WAV SITAR.WAV SMOOTH_T.WAV SNAKE_EY.WAV SQ8_SH.WAV STREICHF.WAV SUPERSAW.WAV SYNTH_VO.WAV SYNTHARP.WAV SYNTHESI.WAV TABLE_TI.WAV TALKING.WAV TELEPHON.WAV TEXTURE4.WAV TEZZALOG.WAV TIDY001.WAV TIDY002.WAV TIDY003.WAV TIDY004.WAV TIDY005.WAV TIDY006.WAV TIDY007.WAV TIDY008.WAV TIDY009.WAV TIDY010.WAV TIDY011.WAV TIDY012.WAV TIDY013.WAV TIDYB001.WAV TIDYB002.WAV TIDYB003.WAV TIDYB004.WAV TIDYB005.WAV TIDYB006.WAV TIDYB007.WAV TIDYB008.WAV TIDYB009.WAV TIDYB010.WAV TIDYB011.WAV TIDYB012.WAV TIDYB013.WAV TIDYBN00.WAV TIDYBN01.WAV TIDYBN02.WAV TIDYBN03.WAV TIDYBN04.WAV TIDYBN05.WAV TIDYBN06.WAV TIDYBN07.WAV TIDYBN08.WAV TIDYBN09.WAV TIDYBN10.WAV TIDYBN11.WAV TIDYBNK0.WAV TRON_MAL.WAV TWIST02.WAV TWIST1.WAV TWIST31.WAV TWIST6.WAV TWISTED_.WAV UH...PSY.WAV VINCENT_.WAV VIRAL.WAV VIRUS_SA.WAV VOCAL_00.WAV VOCAL_FO.WAV VOXSYNTH.WAV VPS.WAV WAVETRIP.WAV WHERE_NO.WAV WOWEE.WAV ZAP.WAV

wt=0 Morph position across the wavetable's frames, normalised 0 to 1 (0 = first frame, 1 = last frame), lerping between adjacent frames. Modulate it to sweep the table, eg wt=[0:1] or wt=sine.

detune=0 Detune the oscillator in semitones.

sync=0 Hard-sync ratio (0 = off): the waveform restarts sync times per cycle. Negative values soft sync, for a less clicky tone.

crush=0 Phase quantisation in bits (0 = off, max 12), for a lo-fi stepped timbre; crush=3 gives 8 steps.

pwm=0 Phase power warp (0 = off): skews the waveform toward its start (>0) or end (<0), like a generalised pulse width.

formant=0 Formant shift (0 = off): shifts the spectral formants up (>0) or down (<0) while keeping the pitch, for vowel-like timbres.

unison={1, detune:1.01, amp:1, pan:0.5} Number of detuned voices (1 to 16), eg unison={7, detune:1.02}. detune is the max frequency ratio of the outermost voices. amp is the centre voice's level relative to the outermost (above 1 for a stronger centre). pan is the stereo spread of the outermost voices (0 = all centred, 1 = hard left/right). Cost scales with voice count.

wave Synthesizer using a waveform specified by the wave param. Possible values saw, square, sine, triangle, pulse

Presets

acid Dark acid bass preset on superosc: resonant 4 pole lpf with a per-note filter envelope, and hard sync pushed up on accented notes. Sixteenth-note bass line (dur=1/4, oct=2); wavetable, wt and unison can be overridden. The following custom control params are available:

cutoff Filter cutoff, 0 to 1; default ramps up over 8 beats ([]l8@s). Set a constant for a fixed tone, eg cutoff=1/4. Also raises the default resonance.

decay Filter envelope decay control, 0 to 1; default 1/2. Sets how long the envelope takes to fall (roughly 200 ms at 0 up to about 1.2 s at 1), so low values give short blips and high values long sweeps.

accent Accent amount, default this.a (the a pattern flag). Accented notes are louder, brighter, more synced and have a longer filter sweep. accent=1 accents every note, accent=0 none.

resonance Filter resonance control, 0 to 1; default 1/4+cutoff/4, so it opens up along with the cutoff. Scales the lpf q; higher values give the squelchy acid whistle.

The following pattern flags can be used, as for tb303:

a accent flag. Makes the preceding note accented (louder, brighter, longer filter sweep and more sync).

s slide flag. The preceding note glides from the one before it (70 ms rather than 20 ms); the envelope still retriggers.

u up octave. Makes the preceding note an octave higher.

d down octave. Makes the preceding note an octave lower.

For example b acid 0a0u3s0d, decay=3/4 plays four notes: accented, up an octave, slid into, then down an octave.

ambi Ambient drone preset; multiple sines with varying detunes

bd Preset based on pitchedperc. Provides a basic synthesized kick drum. Note that as this synth is not sample based, it only accepts digits in the player pattern, unlike the play synth. Use sus or dec params to control the duration of the kick. Uses some unique params:

accent=0 Positive values accent the kick making it louder and stronger. Negative values diminish the kick. Default value is derived from the player pattern value and 'add' param.

tone=0 Tone control for the kick. Larger values will open the filters.

tune=55 Base frequency for the kick in Hz. Usually this should be between about 45 and 65 Hz. The pitch{} function can be used to calculate specific frequencies for tuning the kick to the current key; for example tune=pitch{0,oct:2}

bell FM generated bell preset

crackle Crackly crackle sounds.

cleangtr, leadgtr, fuzzgtr Clean, lead and fuzz presets for the guitar synth. Need include 'lib/effects.limut' for their saturation and cabinet.

dbass Detuned saw bass preset - multiple sawtooth waves slightly out of tune for a fuller sound

dbd Disperser kick drum preset based on impulse. A short impulse is fed through a disperser (allpass filter chain) to produce a deep dub-style boom kick. Uses params:

freq=45 Base frequency for the kick in Hz. Sets both the disperser resonance and an hpf cutoff. Default time units: hz.

decay=1/3 Controls the disperser Q (computed as 0.4+decay/2); larger values give a longer, more pronounced boom tail.

dlead Distorted lead preset on superosc: detuned unison, an lpf swept down each note, a constantly shifting wt and pwm, analog pitch drift, glide and delayed vibrato. Plays at oct=2; wavetable, wt and unison can be overridden. The following custom control params are available:

cutoff Filter cutoff, 0 to 1; default 1/2. Added to vel; higher is brighter.

dist Distortion, 0 to 1; default 1/2. Drives both the waveshaper and the hard sync amount: low for a clean lead, high for a snarling one.

dsaw Detuned saw preset - multiple sawtooth waves slightly out of tune for a fuller sound

dsine Detuned sine pad preset - multiple sine waves slightly out of tune for a fuller sound

dsquare Detuned square preset - multiple square waves slightly out of tune for a fuller sound

dtri Detuned tri preset - multiple triangle waves slightly out of tune for a fuller sound

dwave Synthesizer preset using multiple detuned waveforms specified by the wave param. Possible values saw, square, sine, triangle, pulse

epiano DX7 FM style electric piano

ethereal FM Ethereal pad preset

fmbass Funky FM bass preset

glass FM glass synth with a chiming sound preset

glock FM generated glockenspiel preset

keytar Crispy keytar lead preset (after Au5's Shadow Away lead): a saw with hard sync warped by an LFO, a noise pitch jitter, a plucked noise attack and a resonant hpf. Plays at oct=4, dur=1/2.
Needs include 'lib/effects.limut' for its built-in fx chain (disperser, wavefolder, cabinet, ott, echo). The fx chain is per note, so for fast lines move it to a bus (fx cleared here, bus=b1).
Built on audiosynth, so glide and vib have no effect (addc does work); use vibrato instead.
The following custom control params are available:

crisp Wavefolder drive; default 1. Adds hard upper harmonics without changing level.

jitter Pitch noise depth; default 1/2. The "crispy" ingredient: 0 is clean, larger values fizz and break up. Scaled by vel.

warp Sync warp depth; default 1. Moves the sync ratio and hpf together. 0 is a plain saw.

lfo The LFO warp follows; default [0,1]l2@s. Set a constant to freeze the tone.

cutoff Highpass cutoff, 0 to 1; default 1/2, scaled by vel. Shapes the low end weight.

click Attack transient amount; default 1/2.

vibrato Vibrato depth as a frequency ratio; default 1/50. The rate is fixed at 5.5 Hz.

lately FM bass in the style of the "lately bass"

noisefloor Provide a slowly varying noise floor.

perc Play sampled percussion. Similar to the play synth, and uses the same pattern values and samples, but defaults to 1/4 beat duration, and each player has a choke group.

ping Sine wave ping preset

prophet Prophet style preset: pulse wave with lfo controlled pulse-width (uses AudioWorklet so may use more audio render capacity than other synth types)

cutoff=1/2 Filter cutoff control from 0 to 1; also scaled by vel.

resonance=0.4 Filter resonance q.

pulse Pulse wave preset

saw Sawtooth wave preset

sine Sine wave preset

square Square wave preset

superbass Fat, unstable bass preset: a triangle sub an octave down plus a 15-voice superosc drifted by chaos LFOs.

supersaw supersaw lead synth preset; 7 detuned saws

supersweep Evolving superosc pad with a wandering formant and a noise-swept resonant lpf, plus airverb. Play it low and sparsely.

swell Swell pad - triangle wave

tri Triangle wave preset

vocode Vocoder demo preset: a speech sample through vocodethis, so a tuned saw carrier speaks the words at the played pitch. The following custom control params are available:

wave='saw' Carrier waveform; a bright wave such as saw gives the most intelligible speech.

mix=0.95 Wet/dry mix of the vocoded signal against the dry sample (1 = fully vocoded, 0 = dry).

vocospeak Singing text-to-speech preset: speaks the text param at the played pitch, mixing the pitch-shifted voice with a vocoded saw. A new phrase may not sound until a later trigger while it synthesizes. The following custom control params are available:

text='hello world' The words the voice will say/sing. Set this to whatever you want spoken, eg p1 vocospeak (0,3,5), text='limut is alive'.

rate=1 Speech delivery speed (higher = faster); does not change the sung pitch.

vox Vocal-ish superosc pad: the VOXSYNTH wavetable with 7 unison voices, wt sweeping over 24 beats. Override wt for a fixed vowel.

xylo FM xylophone preset

Visual Synths

! / stop / none

bits Bitwise operations giving fractal patterns

blank Blank: sets every pixel to the back colour.

blob Morphing 3D blob

buffer Maintain a render target, which other synths can render into. The render target is then drawn to the screen by this synth. Use the buffer param on another synth to direct its output to a buffer synth. Use the rez param on the buffer synth to set the render target texture resolution scale. Use the feedback param to provide video feedback by rendering this buffer on top of itself every frame. The subparams will modify the feedback render.

clouds Moving clouds

dmx Output to a DMX universe to control lighting fixtures. Requires web serial support to function (Chromium based browsers only currently), and a USB to DMX interface (tested with Enttec OpenDMX). For example, if there are two DMX lights on DMX channels 1 and 8, that each take a master dimmer channel followed by red, green and blue channels, then d dmx, channel=(1,8), set={1,rainbow} will output a rainbow colour to both lights.

channel Set the base DMX channel number. DMX channels are 1-based, so this should be one or more, never zero.

set Set DMX channels to the supplied values. For example set={0.1,0.2} sets the first channel to 0.1 and the second channel to 0.2. The channel offsets can also be supplied explicitly, eg set={0:0.1, 1:0.2}

lights Add values into DMX channel(s). Can be used in the same player or another to add value(s) to whatever is already set.

addl Add values into DMX channel(s).

mul Multiply values into DMX channel(s).

min Take the minimum of the supplied value(s) with the current DMX channel value(s).

max Take the maximum of the supplied value(s) with the current DMX channel value(s).

glow Additive glow for lights, sparks etc

gradient Simple gradient in the y direction

grid Square grid

image Display an online image. Use the url param to specify the image url. The image must be served using CORS. by default the image back is transparent.

julia Julia set fractal

kal Changing kaleidoscope pattern

lines Twisting lines

readout Display the current value as a digital display

scope Display the current audio waveform

scopefft Display the frequency spectrum of the current audio waveform

shadertoy Display a shadertoy synth. Use the id param to specify which shader. Only shaders published with the 'public+api' option are available, others will give error 'Shader not found'. At present, shaders that use textures or multiple channels will also not work.

stars Exploding stars for fireworks etc

streetlight Looking up at the passing streetlights on a night drive

swirl Psychedelic swirl

text Draw and display text. Use the text param; eg text='Hello'. Use \n to split to multiple lines: text='Hello\nWorld'. Use subparams to control the text rendering, eg: text={'Hello',font:'times',size:'144',linesize:0.7,y:1/4,x:2/3,style:'bold italic'}. Text is evaluated only per event, not per frame.

visualsynth Build a visual from a chain of visual nodes, compiled into a single shader. Set the px param to a node chain connected with >>; eg px=mul{2}>>tex{webcam{}}. Each pixel starts as its own coordinate (xy set, y up, −1 to 1 vertically and wider horizontally by the aspect ratio), flows through the chain, and the final value is the pixel colour (rgba). Node params may be timevars etc and update every frame; a constant param is compiled into the shader, so editing a number recompiles. In-shader params (zoom, recol, pixellate, fore, back) do not apply; non-shader params (loc, blend, dur etc) do. A px that is a plain value fills the quad, eg px=#f00.

display Send the output to a HUB75 LED panel instead of the canvas: display='hub75-01' (an mDNS name, hostname or 'host:port', default port 7575; '10.42.0.1' if the name will not resolve). The panel holds the picture for as long as the player exists. One player per display. dim sets the whole-panel brightness, 0 to 1, per frame. tex1d/tex2d/tex3d work on the panel; tex{webcam{}} and tex{'url'} do not, and the chain is refused with a warning. Type hub75 in the console for status, test patterns and the dimmer.

id is the value arriving at that point in the chain; px=id>>X is the same as px=X. A call at the head of a chain (or of an arithmetic expression) is handed that value automatically, so px=floor{1/40} floors the coordinate and px=floor{1/8}+1/2 is px=floor{1/8}>>add{1/2}. A call that already holds a visual node in its arguments keeps them as written: px=abs{sin{id*4}}.

Operators compile into the shader and apply to all 4 components: arithmetic (+ - * / % ^), eg px=mul{1}/2+#080, and comparisons (< > == etc), which give 1 or 0, eg px=id>0.5. If both sides are visual nodes, each sees the same incoming value: px=tex{'a.png'}+tex{'b.png'}. Note >> binds looser than arithmetic: px=id/2>>tex{webcam{}} is (id/2)>>tex{webcam{}}.

?? and ?: give a ternary: px=length<1/2 ?? #f00 ?: #00f is a red disc on blue. Both sides are always evaluated (no short circuit), and the edge is hard; use smoothstep and mix{} for antialiasing. The test is per component, so reduce a colour to one value to use it as a condition, eg dot{id,#3b1}>1/2. With no ?: the false side is the incoming value: px=tex{webcam{}}>>(id.r>1/2 ?? #f00) reddens bright pixels. Note >> binds tighter than ??, so bracket the conditional. Chains of ?? ?: work as else-ifs.

Maths functions work on visual nodes and apply per component: floor, ceil, round, fract, abs, sign (sgn), min, max, clamp, smoothstep, sqrt, exp, pow, dot, cross, length, normalize, pxhash, pxhashf, pxfwidth, sin, cos, tan, tanh, atan. They pipe naturally, eg px=floor{1/40}>>tex{webcam{}} pixellates the camera.

Colours can be written in hsv or lab anywhere in a chain: px={h:1/3}, px=tex{webcam{}}>>mul{{h:1/2,v:1/2}}. An h or labh is what marks a map as a colour, including in node subparams: set{h:1/3,s:1/2} sets a colour, while set{s:1/2} sets the first channel. A component can itself be a chain, eg px=set{h:id.u} sweeps the hue across the frame. Hue wraps, so negative coordinates are fine.

The four channels can be named as x y z w, r g b a, or u v / s t p q; w is always the fourth, so the third texture coordinate is p. Read channels with .: one channel comes back in all four (id.v), several are rearranged in order and the rest kept (id.vu swaps texture coords, id.bgr swaps red and blue). A function name wins over a channel read, so tex{}.abs is abs.

Visual nodes can be built up as user defined functions taking the incoming value as the first argument: set pixellate = {in,size:8} -> floor{in+(0.5/size),to:1/size}, then px=pixellate{40}>>tex{webcam{}}; or set swap = {in} -> in.vu, then px=swap>>tex{webcam{}}.

uv The value the whole chain started with (the pixel coordinate), wherever it is used, including inside channels{} and user defined functions: px=perlin2>>mul{y:uv.v/2+1/2} fades noise out towards the bottom. uv.v/2+1/2 is a 0 to 1 vertical ramp.

mul{value} Multiply each channel by the param. A number applies to all channels; a vector or subparams (mul{x:2,y:1}) apply per channel, leaving unnamed channels alone. The param may be a visual node, eg px=tex{webcam{}}>>mul{tex{'mask.png'}}.

add{value} Add the param to each channel, as for mul; eg px=add{v:1/4}>>tex{webcam{}} shifts the image vertically.

set{value} Force channels to the param, leaving unnamed channels untouched: px=tex{webcam{}}>>set{#.f..} forces green on, px=set{u:1/2}>>tex{webcam{}} samples one column. A plain number replaces all four. Channel values can be nodes: set{u:id.v, v:id.u}. Note a three digit colour literal carries an alpha, so set{#.f.} also forces alpha to 1; use set{#.f..} to leave it.

In a mul/add/set param, a call with no value is handed the incoming pixel (mul{pxhash}, mul{noise2{scale:8}}), but a call that has a value keeps it (mul{sin{time}} is one value for the whole quad). Use mul{floor{id,1/8}} or mul{id>>floor{1/8}} to pass the pixel explicitly.

channels{a,b,c,d} Run an expression of its own on each channel, eg px=channels{sin{id*1},sin{id*2},sin{id*3}}. Args are positional in channel order or named (channels{r:id^2, b:1-id}); channels with no expression pass through. Inside, id is that one channel. Each argument is a chain, so its head always takes the channel: channels{floor{1/8}} floors the channel (unlike mul{floor{1/8}}).

mix{a,b,t} Blend per channel from a (t=0) to b (t=1). With two args the first value is the incoming one: px=tex{webcam{}}>>mix{dot{id,#3b1},1/2} half desaturates. The amount can be named (mix:1/4, default 1/2), and any arg can be a node, eg a texture mask.

pxhash{value,seed} Random value per pixel (a hash of the incoming value), so px=pxhash is white noise and px=floor{1/8}>>pxhash blocky noise. RGB are independent in 0 to 1; alpha is kept. The field is fixed unless seed changes: px=pxhash{seed:[]l} is TV static. Note plain rand gives one value for the whole quad.

pxhashf{value,seed} A cheaper pxhash. Its pattern varies between GPUs and it bands at large inputs or seeds; prefer pxhash unless many hashes per pixel are measurably slow.

pxfwidth{value} How much the value changes from one pixel to the next: px=tex{webcam{}}>>pxfwidth outlines the camera.

let{'name'} Name the value at that point in the chain so it can be used further along, eg px=sdstar>>let{'foo'}>>tex{webcam{}}>>mul{foo}. The chain passes through unchanged. let{'name', expr} binds an expression instead (seeded with the incoming value, like a mul param): px=fbm2>>let{'wc',uv>>tex{webcam{}}}>>cospal{wc,wc}. let{'name', pxfn{…}} binds the function itself (see 3D SDF Library).

The name may be bare (let{foo}) and is case insensitive. It is in scope for the rest of the player's params, including called functions, and shadows other names, so avoid naming it after a function you use. It must be set before it is used. Inside a loop{} body it goes out of scope at the end of the body unless declared in carry:. A let{} at the head of a loop{} body needs id>> in front.

The audio repetition node functions work on px chains too. series{}, parallel{} and multitap{} unroll, so the index is an ordinary number; loop{} compiles to a real loop, better for large counts. The count is fixed when the line is run and does not animate. A body starting with a plain call needs id>>: series{{i}->id>>pixellate{4*(i+1)}, 3}.

series{{i}->chain, count} Chain count repeats, eg px=series{{i}->add{x:i/8}, 4}>>tex{'a.png'}.

parallel{{i}->chain, count} Sum count copies built from the same input (divide for an average): px=parallel{{i}->noise2{scale:4*(2^i)}*(8/(2^i)), 4}/15.

multitap{{i}->chain, count} Chain like series, but sum every stage's output: px=multitap{{i}->noise2{scale:2^i}*(1/(i+1)), 3}/2.

loop{{i}->chain, count} Feed the chain's output back into its input count times: px=loop{mul{2}>>fract, 4}. i works in expressions but not for anything structural (a size or another count); use series for that. Loops nest.

loop{chain, count, map:{v,i}->expr, fold:{a,v,i}->expr} Fold a term from every value the loop visits (the input plus each step, so count+1 terms) and hand on the total. fold defaults to a sum, eg px=loop{mul{2}, 3, map:{v,i}->noise2{v}/(2^i)}/2 is four octaves of noise; fold:{a,v}->min{a,v} takes a minimum. Either param alone starts a fold. A non-function map is a chain fed the value (map:id>>length). The body can be left off: px=loop{map:{v,i}->sin{v*i}, 4}/4, with i running 0 to count.

loop{chain, count, until:{v,i}->cond} Stop early once the condition on the step's output is non zero (the body runs at least once): px=loop{mul{2}, 16, until: id>>length > 1}. Multiply comparisons for and, add for or. The condition can read a let{} bound in the body. The saving is per block of pixels, not per pixel.

loop{chain, count, carry:{name:start, …}} Named values that persist across iterations and after the loop. Assign with let{name, expr} in the body, read by name. Starting values are chains fed the loop's input (t:0, p:uv) and cannot reference each other. Eg a march: px=loop{{i}->id>>let{p, ro+rd*t}>>scene>>let{d}>>let{t, t+d.x}, 64, carry:{p:ro, t:0}, until:(d.x<1/1000)+(t.x>20)}>>shade{p,t}.

pxfn{chain} Compile the chain once as a shader function and call it at each use, rather than duplicating it; px=pxfn{X} renders the same as px=X. Worth it for a large chain used many times (eg a 3d scene marched, normalled and shadowed); pointless for a chain used once. The body sees only its input, uv and params: a let{} from outside or an enclosing loop{} index is an error, so pass values in through the chain. No recursion.

Libraries built from these nodes: include 'lib/visual.limut' for noise, transforms, palettes, kal, perpetual zoom, vhs and synthwave; include 'lib/sdf.limut' for 2d shapes; include 'lib/sdf3.limut' for 3d shapes, raymarching and fractals. See the library sections.

tex{source} Sample a texture at the incoming xy. The source is an image url (tex{'favicon-32x32.png'}) or a webcam (tex{webcam{'5mp'}}).

tex1d{f} Look the incoming x up in a texture generated from a function of 0 to 1 that returns a number or colour: px=tex{webcam{}}>>tex1d{{x}->{labh:x}}. Piecewises make gradients: tex1d{{x}->[#f00:\1/2,#00f:\1/2,#f00]{x}}. size sets texels (default 256).

tex2d{f} As tex1d, two dimensional, indexed by xy: px=tex2d{{x,y}->{r:x,g:y}}. Default size 64.

tex3d{f} As tex1d, three dimensional, indexed by xyz, eg a colour grade: px=tex{webcam{}}>>tex3d{{r,g,b}->{r:b,g:g,b:r}}. Default size 16; cost is the cube of the size.

The tex1d/tex2d/tex3d maps are built once when the line runs: timevars or sliders inside them are frozen.

pal{stops…} A colour ramp from evenly spaced stops, indexed by the incoming x and clamped: px=sdstar>>pal{0,#408,red,1}. Stops blend in linear light, so midpoints are brighter than a plain mix. Unlike tex1d the stops stay live and can animate or be chains; only their number is fixed. Pipe into it rather than calling it around a value. A plain number stop also sets alpha, so add >>set{a:1} if layering with blend. For uneven stops use tex1d with a piecewise.

webcam{device} Webcam texture source for tex{}, with device, width, height and fps as for the webcam visual synth: tex{webcam{'5mp', width:1280, height:720}}. Editing them reopens the camera.

webcam Display the webcam feed (the browser asks permission first). The console lists available devices on first use.

device Device index or part of the device label. Defaults to the first non-virtual camera (eg not OBS Virtual Camera).

width, height, fps Capture mode; defaults 640x480 at the fastest frame rate. Smaller modes lag less. A size the camera does not support is an error; the console logs the actual mode (and scaled, not a native mode if the browser is resizing) and the delivered frame rate. If the delivered rate is well below the advertised one, the room is too dark for the camera's auto exposure; add light.

xor Bitwise xor with mod, giving changing pixellated patterns

Common Event Params

amp Player amplitude control. For audio players, this defaults to the event vel so that velocity controls the player volume.

delay Event start time offset. Default time units: beats. Once applied, delay is removed from the event so it is not visible to expressions via this.delay or player.delay.

delay={1/2,add:2} subparams on delay will be used to override the event values. In the example given, the add param of the delayed event will be set to 2, but any params can be overridden this way. This is most useful with chords of delay values, as it allows individual delayed events to change their properties.

dur Pattern step duration. Timevars, random vars and index vars can be used to give variation in duration. Chords cannot be used with the dur param. Default time units: beats.

pattern Pattern to play, replacing the one on the player line; eg p ping 0, pattern=`0246`. A string, so backticks or quotes are required; full pattern syntax works, eg set p pattern=`0123 crop 3`. Use this for a pattern containing a comma.

Evaluated once per beat, so it can change as the player plays: set p pattern=[`0246`,`1357`]t8. A changed pattern stays aligned to the beat grid; use now (pattern=`now 0246`) to restart it instead.

The player line pattern overrides a preset's pattern; a pattern param on the player line overrides both, and a set override wins over all. Compound operators combine with the player line pattern: p ping 01, pattern+=`23` plays 0123.

Only players that play a pattern use this param, so it has no effect on follow, midi, gamepad and keyboard players, or on continuous players such as bus and the visual synths.

quantise Snap event count to a grid of the given size, in beats. Eg quantise=1/4 snaps events to the nearest 1/16-note grid point. Applied after stutter so stuttered events can be quantised. If snapping back would put the event in the past, it is pushed forward to the next grid slot instead. Set to 0 to disable.

rate Playback rate for 'play' samples, animation rate for visuals

stutter Split each event into this many evenly spaced events within the same time (rounded down; 0 or 1 is off); eg stutter=2 turns one 1 beat event into two 1/2 beat events. this.stutter is not visible to expressions.

stutter={2,dur:1/4} the dur subparam forces the time between stutter events rather than dividing the original event's duration evenly. Default time units: beats, eg stutter={2,dur:1/4s} for quarter-second spacing.

Any other subparam can be supplied and will be applied to the extra stutter events only — the first event keeps its original params. Eg stutter={2,amp:1/4} plays the first note at normal amp and the second at amp=1/4, useful for echo-like tails.

A user defined function can be supplied to control each stutter event individually, receiving the stutter index (0-based) and returning a map of param overrides. The lambda is applied to every stutter event including the first. Eg stutter={4,{i}->{oct:2+i}} plays 4 notes per pattern event with octaves 2, 3, 4 and 5.

sus Sustain time. Note the 'sustain' in an ADSR envelope sets the sustain level, however this param sets the sustain time. The default is time required after attack and decay to take the note to the end of the duration. Default time units: beats.

swing Amount to swing alternating 1/4 beats, as a Linn LM-1 style percentage from 50 (no swing) to 66 (perfect triplet swing) to 75 (maximum dotted note swing). You can set swing for all players at once using (eg) set * swing=60

swing={66,period:1/2} The period to apply the swing. Defaults to 1/4 of a beat (suitable for house music).

Read only params

exists READ ONLY param. One if the player is defined, even if its not currently playing any event.

idx READ ONLY param. The pattern index of this event. The pattern index counts through the pattern, then resets when the pattern repeats.

player READ ONLY param. The name of the player currently playing. For a follow player, will be the name of the follow player (not the player it's following).

playing READ ONLY param. One if the player exists and is currently playing one or more events.

pulse READ ONLY param. A value that rises and falls as a note plays on the player. If multiple notes are playing, the value will be a chord. The value is pre-multiplied by the note velocity (the vel param).

time READ ONLY param. The current time in beats this event has been playing for.

voice READ ONLY. The index of an event within a chord; eg in p ping 0, add=(0,2) the two events have voice 0 and 1.

Audio Params

add Amount to add onto event scale degree, in degrees. Evaluated per event only.

add={0,#:1} Sharpen the scale degree (add a given number of semitones)

add={0,b:1} Flatten the scale degree (subtract a given number of semitones)

addc Amount to add onto event pitch, in semitones. Evaluated per frame and may be fractional, so addc=[0:12]e slides up an octave over the note. Also works on audiosynth presets (keytar, epiano, superbass…) via eventpitch, where glide and vib do not.

apf1 All pass filter frequency. Default time units: Hertz. Multiple allpass filters can be specified with params apf1, apf2 etc. They will operate in parallel so their outputs sum, allowing phase cancellation.

apf={400,q:2} Allpass filter resonance. Default: 1.

att Attack time. Default time units: beats.

bits bit crush effect; value is number of bits. 1 bit is very distorted to (eg) 32 bits is relatively clean. 0 disables the effect.

bits={12,gain:2} Input gain; gain amp to apply to the signal before bits is applied

bits={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

bpf Band pass filter frequency. Default time units: Hertz.

bpf={400,q:20} Band pass filter resonance. Default: 1.

bpf={400,poles:4} Filter poles. Default is a 2 pole filter giving 12 dB/octave. Alternatively, specify 4 for a Moog-like 4 pole filter giving 24 dB/octave.

bpf1 Multiple, parallel bandpass filters are available using params bpf1, bpf2 etc.

bus id of the bus that this player should mix to. If the sepcified player does not exist, this player will be silent. If the bus param is not specified, this player will be mixed to the global main bus.

choke Set a choke group. Notes played with a choke group will cut off other notes that are already playing if they have the same choke group. The choke group can be a string, and can apply across multiple players.

chop Apply a tremolo effect that chops the audio signal, eg chop=2cpb chops the signal twice per beat. Default time units: cycles per beat

chop={2,wave:triangle} chops twice per beat with a triangle waveform. The default waveform is sine; the options are sine, square, triangle, saw.

chop={4,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

chorus Strength of lfo-delay based stereo chorus effect.

chorus={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

clip boost the signal then apply hard clipping

clip={12,gain:2} Input gain; gain amp to apply to the signal before clip is applied

clip={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

compress Compression ratio, or the reduction in volume for signals above the compression threshold. A ratio of zero disables compression.

compress={12,gain:2} Input gain; gain amp to apply to the signal before compression is applied

compress={12,threshold:-40dB} Threshold at which the compression ratio kicks in. Default is -50dB

compress={12,knee:10dB} the 'knee' is a softening of the compression curve, so that there is no sudden changeover at the threshold. A value of 0dB disables the knee and gives a sudden changeover. Other values are the range over which the knee should apply. Default is 40dB.

compress={12,att:0.1} Compressor attack time. The attack time is the time it takes for compression to 'kick in' once a signal crosses the threshold. Default is 0.01 (or 10ms). Default time units: seconds.

compress={12,rel:0.2} Compressor release time. The release time is the time it takes for compression to 'go away' once a signal falls back below the threshold. Default is 0.25. Default time units: seconds.

dec Decay time. Default time units: beats.

detune Amount of detune in semitones

drive overdrive by boosting the signal then applying soft clipping. Low values (eg 1/32, 1/16) give a gentle overdrive. Mid values (eg 1/8, 1/4) give a distortion effect. Higher values (eg 1/2, 1) give a full on fuzz.

drive={12,gain:2} Input gain; gain amp to apply to the signal before drive is applied

drive={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

echo Echo time delay. Default time unit: beats.

echo={1,feedback:0.8} Echo feedback gain, 0 to 1, default 0.35. Higher value means the echo persists for longer.

echo={1,max:2} Echo max time delay. Only valid for bus echo. The bus echo time can be varied, but cannot be larger than this. Defaults to either 1 beat, or the initial value of the delay time, whichever is larger. Default time unit: beats.

envelope Envelope type: 'full' (ADSR), 'simple' (ADR), 'organ' (ASR at full volume), 'pad' (cosine crossfade, constant volume across consecutive notes), 'linpad' (linear crossfade, better when consecutive events are phase identical), 'percussion' (instant attack, release only). Or an expression, eg envelope=[1:!300ms,0]e@s

flanger LFO frequency for flanger effect. Default time units: cycles per beat.

flanger={1/3,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

fold distort the signal by boosting then folding the overdriven part of the waveform

fold={12,gain:2} Input gain; gain amp to apply to the signal before fold is applied

fold={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

glide Glide from one note to the next (portamento). When a note plays with glide set, if there is a previous note playing on the same player in the same voice, then both notes will glide in pitch from the old note's pitch to the new. glide=1/3 means it takes 1/3 of a beat to glide from the old note to the new. Default value is 0 (no glide). Default time units: beats.

glide={1/3,curve:4} glide curve. Zero means no curve, or a linear frequency change. Positive curve means the frequency moves quickly to the target, then slows down as it approaches. Default is 1.

high High shelf gain. Eg high=-6dB will act like a gain of 1/2 for the high frequencies. Must specify the value with the dB unit, or use a relative gain value instead.

high={-8db,freq:400} High shelf cutoff frequency. Default: 1100Hz. Default units: Hertz

hpf High pass filter frequency. Default time units: Hertz

hpf={400,q:20} High pass filter resonance. Default: 5.

hpf={400,poles:4} Filter poles. Default is a 2 pole filter giving 12 dB/octave. Alternatively, specify 4 for a Moog-like 4 pole filter giving 24 dB/octave.

low Low shelf gain. Eg low=-6dB will act like a gain of 1/2 for the low frequencies. Must specify the value with the dB unit, or use a relative gain value instead.

low={-8db,freq:400} Low shelf cutoff frequency. Default: 200Hz. Default units: Hertz

lpf Low pass filter frequency. Default time units: Hertz

lpf={400,q:20} Low pass filter resonance. 10 is approximately the transition where resonance starts to kick in. Default: 5.

lpf={400,poles:4} Filter poles. Default is a 2 pole filter giving 12 dB/octave. Alternatively, specify 4 for a Moog-like 4 pole filter giving 24 dB/octave.

mid Mid gain. Eg mid=-6dB will act like a gain of 1/2 for the mid frequencies. Must specify the value with the dB unit, or use a relative gain value instead.

mid={-8db,freq:400} Mid cutoff frequency. Default: 600Hz. Default units: Hertz

mid={-8db,q:10} Mid q defines the width of the mid frequency band; low q is wide. Default: 5

mono Force a signal to mono: the input signal appears equally in both left and right channels

nf Notch filter frequency. Default time units: Hertz

nf={400,q:20} Notch filter resonance. Default: 1.

noisify noisify: distort the signal into noise

noisify={12,gain:2} Input gain; gain amp to apply to the signal before noisify is applied

noisify={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

oct Octave; middle C is in octave 4

pan pan from -1 (left) to 1 (right). default: 0

press Aftertouch pressure, from 0 to 1. Default is 0. Set per event by the midi player for as long as the note is held; on every other player it stays at its default, so a preset written against this.press still works when played from a pattern.

phaser LFO frequency for phaser phase sweep effect. Default time units: cycles per beat.

phaser={1/3,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

psf Phaser stage filter consisting of two allpass filters in parallel. Multiple phaserstage filters can be specified with params psf1, psf2 etc. The phaserstages will operate in series, allowing more complex phasers to be built up with multiple peaks and troughs in the frequency spectrum. Default units: Hertz

psf={f1:300,f2:2300,q:0.7} f1 and f2 are the frequencies in Hertz of the two allpass filters. Sweep these dynamically for the swirling phaser effect. q is filter resonance. Default: 1.

rel Release time. Default time units: beats.

reverb Convolution reverb duration. The value is a time in beats that the reverb tail will last for. Default time unit: beats.

reverb={2,curve:3} Decay curve power of the reverb tail. 1 is a linear decay, larger values make the tail decay faster. default: 5

reverb={2,hpf:300} HPF subparam Sets the cutoff frequency for a high pass filter placed before the reverb. 0 disables the highpass filter altogether. default: 0. Default time units: hz

reverb={2,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1/2

ring Apply ring modulation at the given frequency, eg ring=32hz apply ring modulation at 32Hz. Default time units: hz

ring={32,wave:saw} 32Hz ring modulation with a sawtooth waveform. The default waveform is triangle; the options are sine, square, triangle, saw.

ring={32,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

room freeverb room size.

room={2,hpf:300} HPF subparam Sets the cutoff frequency for a high pass filter placed before the freeverb. 0 disables the highpass filter altogether. default: 0. Default time units: hz

room={2,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1/2

root override root for this player; used instead of main root for this player only.

sample With 'play' synth: number: choose which sample set to use. With 'sample' synth: string: url of online audio file to play (note: server must support cross origin requests for the file)

sample={32,pitch:261.6} Specify the original pitch of the sample (eg default is 261.6 Hz for C4). Default time units: hz

scale override scale for this player; set to one of these strings: chromatic, major, majorpentatonic, pentatonic, penta, minor, aeolian, minorpentatonic, minorpenta, mixolydian, melodicminor, melodicmajor, harmonicminor, harmonicmajor, dorian, dorian2, diminished, egyptian, yu, zhi, phrygian, prometheus, indian, locrian, locrianmajor, lydian, lydianminor, hungarianminor, romanianminor, chinese, wholetone, halfwhole, wholehalf, bebopmaj, bebopdorian, bebopdom, bebopmelmin, blues, minmaj, susb9, lydianaug, lydiandom, melmin5th, halfdim, altered,

start For the 'sample' synth, specify the playback start time within the sample. Default time units: seconds.

suck suck smaller amplitudes even smaller, but leave large amplitudes alone; value is a limit amplitude: when the waveform is within this limit, it is scaled down. This can provide an unusual form of distortion, or for samples with a long tail, can give a 'gated' effect.

suck={12,gain:2} Input gain; gain amp to apply to the signal before suck is applied

suck={1,mix:1/4} Mix subparam controls dry/wet mix. 0 for dry only, 1 for wet only. default: 1

sus Sustain time. Note the 'sustain' in an ADSR envelope sets the sustain level, however this param sets the sustain time. The default is time required after attack and decay to take the note to the end of the duration. Default time units: beats.

sus={level:1/6} Set the amplitude level for sustain. Note the 'sustain' in an ADSR envelope is the level, so it is this value not the main 'sus' value (which is actually the sustain time). Default: 0.8.

vel Event velocity, default 3/4. Controls amp and usually brightness. Set by midi or gamepad players, or by params. Fixed when the note starts; for aftertouch see press.

vib vibrato rate. Default time unit: cycles per beat

vib={2,depth:1} vibrato depth in semitones (default 0.4). Evaluated per frame, so eg vib={2,depth:[0:1]e} widens the vibrato over the course of the note

vib={2,delay:1} delay time before vibrato starts (default 1/2). Default time units: beats

wave For audio synths that use a waveform, specify which waveform to use, from this list: sine, square, sawtooth (can also use saw for convenience), triangle (can also use tri for convenience), pulse, ah, bah, bass, 'bass-amp360', 'bass-fuzz', 'bass-fuzz-2', 'bass-sub-dub', 'bass-sub-dub-2', brass, 'brit-blues', 'brit-blues-driven', buzzy, 'buzzy-2', celeste, 'chorus-strings', 'dissonant-1', 'dissonant-2', 'dissonant-piano', 'dropped-saw', 'dropped-square', 'dyna-ep-bright', 'dyna-ep-med', ee, ethnic, full, 'full-2', 'guitar-fuzz', harsh, 'mkl-hard', noise, o, ooh, organ, 'organ-2', piano, 'pop-ahhh', pulse, 'putney-wavering', tb303, throaty, trombone, 'twelve-optines', 'twelve-string-guitar', 'warm-saw', 'warm-square', 'warm-triangle', wurlitzer, 'wurlitzer-2' Note that values with hyphens must be enclosed in quotes. In addition, there may be a performance problem with using dynamic (per frame) pitch sweeps using these waveforms. This penalty does not apply to the basic waveforms (sine, sawtooth, triangle, square).

Read only params

freq READ ONLY param. This param is set by all tonal synths, to the frequency in Hz of the note being played. This value can then be read for other purposes; eg lpf=this.freq*2 will set the low pass cutoff frequency to double the note frequency.

pre READ ONLY param. An audionode that taps off the players audio signal before shared effects like scho/reverb/chorus and any fx chain are applied.

Visual Params

add Amount to add onto pattern value

additive add this shaders output to those beneath instead of blending

back background colour; eg {r:0,g:1,b:0,a:1} for green or #00ff for orange.

blend set the blend mode which defines how this shaders output interacts with the color already present underneath. Possible values: additive subtractive average multiply invert min max

buffer send the output of this synth to a buffer player. For example, buffer=vb makes this synth render to the synth with id vb, which must be a synth of type buffer. The buffer synth can then render itself to the screen to display the output.

contrast apply a power based contrast curve. Default is zero meaning no contrast curve applied.

fade brightness/transparent fadeout

feedback only for a buffer synth: use video feedback by rednering the buffer onto itself every frame. The subparams control that rendering. Any visual params can be used as feedback subparams. For example feedback={zoom:1.01} will render the feedback slightly zoomed, giving a classic video feedback effect.

fore foreground colour; eg {r:0,g:1,b:0,a:1} for green or #fa0f for orange.

jpeg Apply a JPEG/MPEG style digital glitch effect, including macroblock colour quantisation, chroma crush, blocky ringing, and displaced corrupted blocks of varying sizes arriving in clustered bursts. 0 for none, 1 for full effect.

jpeg={1,size:24} number of macroblocks across the visual; smaller values give larger, chunkier blocks. Defaults to 8.

loc position and size. The visual coordinate system runs from (-1,-1) in the bottom left of the screen to (1,1) in the top right. x and y define the centre of the visual, and w and h are the full width and height; eg {x:0,y:0,w:2,h:2} covers the full screen.

mid middleground colour; eg {r:0,g:1,b:0,a:1} for green or #fa0f for orange. If not specified, defaults to midway between fore and back.

mirror mirror the shader; number of mirror planes to apply; default: 0

mirror={10,rotate:1/4} Rotation offset (0-1) to apply to the mirror planes. Defaults to 0.

mirror={10,fan:1} fan out angle to apply to mirror segments, so each segment is taken from an incrementing portion of the original image. A fan of 1 moves each next segment by the size of one segment. Defaults to 0.

monochrome make the shader output monochrome; 0 for normal, 1 for monochrome

perspective perspective warp to apply to shader (negative to invert), or 0 to disable

perspective={-1,shade:1/2} amount of darkening shade to apply to the part of the image 'in the distance'.

pixellate number of pixels to pixellate to in x direction, or 0 to disable pixellation

pixellate={10,y:20} number of pixels to pixellate to in y direction, or same as x if not present.

pulse extent to which the audio signal affects the value passed to visual synths

rate the rate that time should progress in the shader. If the time param is set, this has no effect

recol recolour the shader. Set to oil for oil film colours, hue for a hue spectrum, fire for a flame effect, sunset for dusk colours, neon for neon megenta and cyan, titanium for titanium anodisation colours

repeat repeat a section of the shader image in both directions. Value is the size of the area to be repeated.

repeat={1/2,x:0.1,y:-0.05} x/y offsets to apply to the area of the shader image being repeated.

rez only for a buffer synth: sets the render target resolution scaling. For example, a rez of 1 will make a render target texture that is identical width and height to the display screen canvas. Default value is 1/2.

ripple Apply a ripple warp to the shader, to give the effect of waves of image distortion rippling out from the centre of the shader.

ripple={1,scale:1/4} scale (size) of ripples. Defaults to 1.

rotate rotate the shader; angle in full rotations so 1/2 is 180 degrees; default: 0

scroll scroll the shader; default: {x:0,y:0}

sway extent to which the audio signal affects the time passed to visual synths

time time value to use in the shader. If this is set, the rate and sway params have no effect. Default time unit: beats.

tunnel tunnel warp to apply to shader, or 0 to disable. Applies a slitscan type warp, where scrolling in the x direction will scroll the original shader through the tunnel.

vhs Apply a VHS tape effect, including colour modification, horizontal bars, wobble, and noise. For a visualsynth, use vhs{} from the Visual Library in the px chain instead.

vignette fade out the visual around the edges. The value controls the shape.

vignette={1/2,aspect:4/3} aspect ratio of vignette effect. Defaults to 1.

vignette={1,cutoff:0.95} cutoff for vignette; lower values provide a more gradual fade around the edges. Defaults to 0.9.

window display the only part of the shader image appropriate to the location of the tile on the screen. This way, a tile moved around the screen will act like a window onto the overall shader image.

zoom zoom the shader; default: {x:1,y:1}

zorder order in which to draw visual synths; defaults to the 1/1000th of the source code line number for the player, so that visuals draw strictly in the order they appear in the source code; however, this param can be used to override this and force specific visuals to the front or rear of the draw order.

Main Vars

Main vars can be set, but not read.

bpm Beats per minute. Value resets unless set every time the code is updated.

root Main root pitch offset; default 0 roots the pitches on middle c; 1 pitches up by one semitone etc. This is fractional, so can be used to 'nudge' the tuning to match other instruments; eg root=0.135

scale Scale eg minor, major, chromatic, majorpentatonic, lydian etc etc. Value resets unless set every time the code is updated.

Sections

set section.active=name Force the named section active immediately (applied on the next beat), restarting its timing from now. Useful for jumping straight to a section while performing live.

set section.next=name Queue the named section to become active when the current section finishes. This is the normal way to sequence a piece; each section can queue the one that should follow it.

sx is a short alias for section — handy when live coding. Use it in param lookups (sx.rise, sx.fall) and in set (set sx.next=name, set sx.active=name). Defining a section always uses the full section keyword.

Predefined Vars

Predefined vars are predefined values that may be useful as shortcuts when live coding.

drop6_2 Predefined time var: equivalent to [1,0]t[6.2]. These are all the predefined drops: drop6_2, drop7_1, drop12_4, drop14_2, drop15_1, drop24_8, drop28_4, drop30_2, drop31_1, drop56_8, drop60_4, drop62_2, drop63_1

tg Predefined time var: trance gate signal. Flips on/off every quarter beat.

time Time, counted in beats (including fractions of a beat)

wow Predefined time var: Randomly but smoothly varying signal, good for simulating analog 'wow' pitch distortion; eg addc=wow

Loc params

droplet For loc visual param. Small tile in a random location, then falling down the screen

firefly For loc visual param. Random motion like a flying insect

fullscreen For loc visual param. Tile covering the full screen

gravity For loc visual param. Accelrating motion downwards. Best combined with other values; eg spark+gravity

spark For loc visual param. Small tile flying outwards

sparkle For loc visual param. Small tile in a random location

tile_full For loc visual param. Predefined loc param map to cover the full screen: equivalent to {x:0,y:0,w:1,h:1}. Other predefined locations: tile_tl/tr/bl/br/m, tile_h1/2/3/4/5, tile_v1/2/3/4/5

tile_rand For loc visual param. Tile in a random location

tile_random For loc visual param. Tile in a random location

Colour params

rainbow For colour visual params. Colour changing through the rainbow

random For colour visual params. Random colour

transparent For colour visual params. Predefined colour. Others: black, darkgray, gray, lightgray, white, red, orange, yellow, green, blue, indigo, violet, neonpink, neongreen

User Input

gamepad{} Take input from a gamepad. With no args it logs inputs to the console to identify buttons and axes. Click a stick to lock its position; click again, or move it after 2 seconds, to unlock.

gp{} gp is an alias for gamepad for easier access.

gamepad{1} Take input from axis 1 on the gamepad. Range -1 to 1 based on axis value. Defaults to gamepad 0.

gamepad{2,1} Take input from axis 2 on gamepad 1. Range -1 to 1 based on axis value.

gamepad{button:0} Take input from button 0 on the gamepad. Range 0 to 1 based on button press value. Defaults to gamepad 0.

gamepad{axis:1} Take input from axis 1 on the gamepad. Range -1 to 1 based on axis value. Defaults to gamepad 0.

gamepad{pad:1} Take input from gamepad 1. Defaults to axis 0.

gamepad{'lsh'} Take input from the left stick, horizontal axis, mapped to -1 to 1. ls is left stick, rs is right stick, then h/v for horizontal/vertical axis.

gamepad{'lsl'} Take input from the left stick, left direction, mapped to 0 to 1. ls is left stick, rs is right stick, then u/d/l/r for the directions.

gamepad{'lsud'} Take input from the left stick, up or down direction, mapped to 0 to 1. ls is left stick, rs is right stick, then ud and du both mean up or down; lr and rl mean left or right.

gamepad{'lsx'} Take input from the left stick, radially out from the centre, from to 0 to 1. ls is left stick, rs is right stick.

gamepad{'lt'} Take input from the left trigger, mapped to 0 to 1. lt is left trigger, rt is right trigger.

gp.lsr Shorthand for gamepad{'lsr'} for easier access. Same for other named axes.

midi{} Take input from an external midi controller. When called with no args, it will log inputs to the console to help identify which midi control is on which port, channel, and control/note number.

midi{70} Take input from control 70 (or note 70 if there is no control 70). Range 0 to 1 based on controller value or velocity and aftertouch. Defaults to port 0 and channel 0.

midi{36,9} Take input from control or note 36 on channel 9. Range 0 to 1 based on controller value or velocity and aftertouch. Defaults to port 0.

midi{36,9,1} Take input from control or note 36 on channel 9 of port (device) 1. Range 0 to 1 based on controller value, or velocity and aftertouch for a note.

midi{'bend'} Take input from the pitch bend control, range -1 to +1. Defaults to port 0 and channel 0.

midi{control:70,channel:0,port:0} Take input from the given controller. The range is 0 to 1.

midi{note:36,channel:9,port:0} Take input from the given note. The value is the velocity then the aftertouch, or 0 if the note is not pressed. The range is 0 to 1.

midi{'notes'} Chord of currently playing notes, in chromatic semitones from the currently set root, from middle C. Defaults to port 0 and channel 0.

midi{'vel'} Velocity/aftertouch of the current playing notes, from 0 to 1. Defaults to port 0 and channel 0.

midi{'press'} Aftertouch pressure (channel or polyphonic) of the keys held, from 0 to 1, and 0 when nothing is held. Defaults to port 0 and channel 0.

avw2 Input from an Alesis Vortex Wireless 2 keytar, recognised by name whenever plugged in (no port needed, no re-run on replug). On its own it is 1 when connected, 0 otherwise, and logs midi ports and the last input to help identify controls.

avw2.f1 Fader 1 on the body, from 0 to 1. f1 to f8 for the eight faders.

avw2.p1 Pad 1, from 0 to 1 based on velocity then aftertouch, or 0 when it is not pressed. p1 to p8 for the eight pads.

avw2.s1 The slider on the neck, from 0 to 1. Reads 1 until it is first moved.

avw2.bend The pitch bend wheel, from -1 to +1.

avw2.ribbon The touch ribbon on the neck, from 0 to 1. ribbon1 and ribbon3 for ribbon banks 1 and 3 specifically; ribbon is bank 1.

avw2.sus The sustain button, 1 while held and 0 when released.

avw2.notes Chord of the keys currently held, in chromatic semitones from the currently set root, from middle C. avw2.vel is their velocity/aftertouch, from 0 to 1.

avw2.press Aftertouch of the keys held, from 0 to 1: eg fx=lpf{400+avw2.press*4000}. One value for the whole keytar that drops to 0 as the last key lifts, so unlike this.press it does not hold through a release tail.

avw2.connected 1 when the keytar is plugged in, 0 when it is not, without logging anything.

avw2{14} Take input from control 14 (or note 14 if there is no control 14) on the keytar, wherever it turns out to be. Also avw2{control:14}, avw2{note:36} and avw2{channel:1}. All of the controls above are on channel 0.

avw2{'f1', port:2} Read a named control from an explicit port, for when the keytar is not recognised by name.

slider{} Create a new slider control on the page, which can be used for live control of live coded expressions. The value dynamically updates to reflect the current position of the slider control.

slider{0.1} Initial value for the slider control when it is first created. Default is 0.

slider{name:'Foo'} Name to display with the slider. The name uniquely identifies the slider. Multiple slider usages in the source code with the same name will be treated as all referencing the same slider. Default is a name constructed from the player and param names.

slider{min:-5, max:5} Minimum and maximum values when the slider is at the ends of its travel. Defaults are min:0 max:1.

slider{curve:2} Curve to apply to the values. Positive values give greater change in value towards the lower end of travel. Default is 0.

Functions

Any of these can be called by piping a value in with >>: 1.7>>floor{1/2} is floor{1.7,1/2}. The maths functions also work on visual nodes; see visual synths.

abs{-1} Returns the absolute of the given value, so flips negative numbers to positive.

accum{1} Accumulates a value over time. In the example, the result will be 1 after the first beat, 2 after the second etc, 3 after the third etc. Note that negative values are ignored; the accum value will only ever rise over time.

The accumulated value survives code updates, including edits to the expression inside it. It resets to zero if moved to another param, the player is renamed, or the line is commented out and run (the way to reset it deliberately). smooth{} and rate{} keep state the same way.

accum{1,keep:'name'} Names the value explicitly rather than letting the param name it. Two params with the same keep name share one accumulator, and a named one keeps its value wherever you move it to.

avg{3,1,2} Returns the mean average of all arguments (2 in the example)

atan{1} Returns the arctangent of the given value, in radians. With a second argument it is the two argument arctangent instead, ie the angle of the vector x,y, which is what a polar coordinate wants: atan{y,x}, or named as atan{y,x:...}.

ceil{1.5} Returns the rounded up value (returns 2 in the example)

ceil{1.2,to:1/2} Precision to round to, so the example rounds 1.2 up to the nearest 1/2, which is 1.5. Defaults to 1, which rounds up to the nearest integer above. Can also be given as the second positional argument, as ceil{1.2,1/2}.

chord{2,3} Build a chord of size members all set to value. The example is equivalent to (3,3) and will expand into two voices.

chord{3,{i}->i*2} If value is a user-defined function, it is called once per chord member with the member index i (starting at 0). The example is equivalent to (0,2,4).

chord{3,{i}->{i,add:2*i}} A lambda can return a map to set per-voice subparams; the example is equivalent to ({0,add:0},{1,add:2},{2,add:4}). Useful for staggering subparams like delay or add across the voices of a built chord.

clamp{2} Limits the value to a range, so the example gives 1. The range defaults to 0 to 1; give another as lo and hi, named or as the second and third positional args, eg clamp{5,-2,2} is 2.

cos{time*pi} Returns the cosine of the given value in radians

count{3,1,2} Returns the number of arguments (3 in the example)

cross{{x:1},{y:1}} Cross product of the red, green and blue components of the two arguments, so the example gives {x:0,y:0,z:1}. As with dot, a plain number counts as all three components, and with one argument the value is crossed with itself (which is always zero). On a visual node it keeps the incoming alpha.

dot{#f00,#ff0} Dot product of the red, green and blue components (1 in the example). A plain number counts as all three, so dot{1,1} is 3. With one argument the value is dotted with itself. On a visual node, px=tex{webcam{}}>>dot{#3b1} is a weighted monochrome.

euclid{3,from:8} Euclidean rhythm: spread 3 events evenly over 8 beats, eg p ping 0, dur=euclid{3,from:8} gives durations 3,2,3. The ordering may differ from classic Euclidean (3,3,2); use the offset subparam to rotate it.

euclid{3,from:8,offset:1} Offset rotates the pattern. The example will give a 3,3,2 pattern which is the default 3,2,3 pattern but rotated by one.

eventpitch{} The frequency in Hz the event is sounding at, from sound, add, oct, sharp, scale and addc. Tracks a time varying addc through the note; eventpitch@e pins it to the note start. Use @f inside a continuous fx chain, eg fx=osc{'sine',freq:eventpitch@f}*0.01.

exp{1} Returns e raised to the given value (2.718... in the example)

first{3,1,2} Returns the first argument (3 in the example)

floor{1.5} Returns the rounded down value (returns 1 in the example)

floor{1.7,to:1/2} Precision to round to, so the example rounds 1.7 down to the nearest 1/2, which is 1.5. Defaults to 1, which rounds down to the nearest integer below. Can also be given as the second positional argument, as floor{1.7,1/2}, which is what 1.7>>floor{1/2} gives.

fract{1.25} Returns the fractional part of the given value (0.25 in the example). As GLSL's fract, so it is always positive: fract{-1.25} is 0.75. Mostly useful on visual nodes, where it is the position within a lattice cell that smooth noise interpolates across.

last{3,1,2} Returns the last argument (2 in the example)

length{{x:3,y:4}} Length of the red, green and blue components as a vector (5 in the example); a plain number counts as all three. On a visual node px=length is a radial gradient; a comparison also affects alpha, so use px=(length<1/2)>>set{a:1} for an opaque disc.

ln{2.718} Natural logarithm (1 in the example). Named ln because log is the console debug function. Zero or negative gives NaN.

max{3,1,2} Returns the largest argument (3 in the example)

min{3,1,2} Returns the smallest argument (1 in the example)

normalize{{x:2}} Scales the red, green and blue components to a length of one, so the example gives {x:1,y:0,z:0}. A zero vector has no direction, so it comes back as zero rather than as a NaN. On a visual node it keeps the incoming alpha.

pitch{1} Calculate a frequency in Hz for a given scale degree.

pitch{4#} Calculate a frequency in Hz for a given scale degree with sharpening (#) or flattening.

pitch{0,oct:3} Calculate a frequency in Hz for a given scale degree on a given octave. Default octave is 4.

pitch{0,scale:minor} Calculate a frequency in Hz for a given scale degree using a given scale. Default scale is the current global var scale

pitch{0,root:2} Calculate a frequency in Hz for a given scale degree using a given root. Default root is the current global var root

pow{2,3} First argument raised to the second (8), also pow{2,by:3}; squares by default. Unlike ^ it does not clamp the base to zero, so pow{-2,3} is -8 where (-2)^3 is 0; a negative base with a fractional exponent is undefined on visual nodes.

rand{3,1,2} Returns one of the arguments at random

rand Pseudo random number between zero and one, from time hashing (so time modifiers like step and per apply). rand@e is constant within each event. rand{seed:0} sets a seed. On a visual node it is one value for the whole quad; use pxhash per pixel.

pxhash{3} Stable hash of the argument, 0 to 1, with optional seed. Unlike rand it does not depend on time. Mainly for visual nodes; see visual synths. pxhashf is a cheaper shader variant.

rate{[0:1]l1@f} Rate of change of a value, in units per beat (about 1 then -1 in the example). Results are jittery, so consider smooth{}. Keeps state across code updates like accum{}.

round{1.5} Returns the rounded value (returns 2 in the example)

round{1.7,to:1/2} Precision to round to, so the example rounds 1.7 to the nearest 1/2, which is 1.5. Defaults to 1, which rounds to the nearest integer. Can also be given as the second positional argument, as round{1.7,1/2}.

sign{-1} Returns the sign of the given value, -1 for negative, 0 for zero, and 1 for positive. Can also use sgn.

sin{time*pi} Returns the sine of the given value in radians

smooth{[]r@f} Smooths a value over time. In the example, the result will rise towards 1. Uses default attack and decay. Like accum{}, it keeps its state across a code update, so it carries on from where it was rather than re-attacking from zero.

smooth{[]r@f,att:4} Smoothing attack rate; higher rate means the value will converge to the target faster when the target is above the current value. Default is 8.

smooth{[]r@f,dec:4} Smoothing decay rate; higher rate means the value will converge to the target faster when the target is below the current value. Default is 4.

smooth{[]r@f,2} Smoothing with combined attack and decay rate; higher rate means the value will converge to the target faster.

smoothstep{1/2} Smooth 0 to 1 ramp with flat ends: 0 at or below lo, 1 at or above hi (default 0 to 1), cubic between. smoothstep{3,2,4} is 1/2.

sqrt{9} Returns the square root of the given value (3 in the example)

sum{3,1,2} Returns the sum of all arguments (6 in the example)

tan{time*pi} Returns the tangent of the given value in radians

time Returns the current time in beats. Time modifiers such as step and per are respected.

Node Functions

biquad{'lowpass',freq:440Hz,q:5,gain} Create a biquad filter node of type 'lowpass', 'highpass', 'bandpass', 'notch', 'lowshelf', 'highshelf' or 'peaking'.

chaos{freq:440, type:lorenz, chaos:0.5, axis:x, smooth:0, seed:0} Chaos oscillator source node: a deterministic but non-repeating signal, roughly −1 to 1. Works as an LFO or an audio-rate modulator: lpf{ 400 + chaos{freq:6}*300 } is a non-repeating filter wobble, osc{ freq: 110 + chaos{110}*40 } organic FM. Uses an AudioWorklet, so costs more audio render capacity.

freq=440 Evolution speed in Hz (so chaos{6} or chaos{440Hz} both work), spanning slow LFO rates up through audio rate. value/freq both set it.

type=lorenz Algorithm: lorenz (dwells in two lobes, then jumps), rossler (gentle spiral with occasional spikes), thomas (very smooth, a safe slow-drift LFO), logistic (stepped sample-and-hold at freq steps per second), duffing (tonal, gritty at audio rate).

chaos=0.5 Character macro (0..1) mapped to each algorithm's bifurcation parameter, sweeping it from ordered toward fully chaotic.

axis=x Selects which dimension of the 3D attractors to output (x/y/z, default x) — the axes give correlated-but-distinct modulation streams.

smooth=0 Lowpasses the output (0..1, default 0), taming the logistic map's steps and de-sharpening audio-rate attractor transitions.

seed=0 Offsets the starting point; instances with different seeds never phase-lock, eg osc{ freq: 110 + (chaos{freq:0.2,seed:1}+chaos{freq:0.3,seed:2})*4 }.

compress{ratio:0, threshold:1/316, knee:100, attack:0.01s, release:0.25s} Create a compressor node.

const{1} Create a node that always outputs a given constant value.

convolver{env:{x}->...,length:1s} Create a convolver node with the given length, and an environment shaping user defined function. The main impulse response will be random noise, shaped by the envelope function.

convolver{env:{l:{x}->...,r:{x}->...},length:1s} Create a stereo convolver node with separate left and right envelopes.

convolver{{x}->...,length:1s} Create a convolver node with the impulse response defined directly by a user defined function. Note this can be slow to create as the user defined function may be called many thousands of times depending on the length.

convolver{l:{x}->...,r:{x}->...,length:1s} Create a stereo convolver node with separate left and right impulse responses.

delay{1/4b, feedback:..., max:1b} Create a delay node with a given delay time, a max delay time, and an optional feedback chain.

delay{1/4b, ..., max:1b} The feedback arg can be positional instead of being named.

delay{[60ms:500ms]l8@f, max:500ms} The delay time can vary per frame, but max is fixed when the node is built, so it is only read once at the start of the event. If the delay time sweeps wider than that first max, set max explicitly to the longest delay you want.

flipper Create a node that flips the left and right stereo channels.

gain{1} Create a gain node with a gain ratio given by the main parameter.

idnode Create an identity node that passes signals through unchanged.

dry An alias for idnode.

thru An alias for idnode.

impulse{rate:1} Create a one-shot click (1ms impulse) source node, so an impulse can be wired into an fx chain like any other source, eg impulse{} >> reverb{room:0.9} to excite an effect's impulse response. rate changes playback speed like a sample. Does not loop.

let{'name'} Name the signal at that point in the chain for use further along: fx=let{'foo'}>>reverb{}>>panner{foo} pans the reverb by its dry input. The chain passes through unchanged. With a second argument the name is bound to that instead: fx=let{'lfo',osc{'sine',freq:3}}>>lpf{800}>>panner{lfo}. A bare call there is fed the signal at that point, as after >>: fx=let{env, follower{}}>>gain{env}. The name may be bare (let{foo}), is case insensitive, is in scope for the rest of the player's params, shadows other names, and must be set before use. Also works on px chains.

loop{..., feedback:...} Create an audiograph loop, with a signal passing through the main node chain, and fed back through the feedback node chain. Note the feedback node chain must contain a delay node otherwise it will not function.

loop{..., ...} The feedback arg can be positional instead of being named.

loop{{i}->..., 4} On a visual synth px chain this compiles to a real loop in the shader instead: the chain is iterated the given number of times with its output feeding back into its input. See the Visual Synth section.

loop{..., 3, map:{v,i}->..., fold:{a,v,i}->...} On a px chain only, fold a term from each iteration into the output; see visual synths.

loop{..., 16, until:{v,i}->...} On a px chain, an early exit: the condition is tested after each iteration and the loop stops as soon as it is true, so a march can stop at a hit or at the far plane. See the Visual Synth section.

loop{..., 64, carry:{name:start, ...}} On a px chain, values carried alongside by name: each is declared with the value it starts from, assigned in the body with let{name, expr}, and still readable after the loop — which is how a march steps a point and a distance and brings a material back out. See the Visual Synth section.

mix{..., mix:1/2} Provide a mix between a dry signal and a wet signal processed through the given node chain. Mix 0 is full dry, mix 1 is full wet.

mix{..., 1/2} The mix arg can be positional instead of being named.

mix{a,b,t} Also works on visual nodes, compiling into the shader as a blend between the two values; see the visual synth section. The mix2-mix8 shortcuts below are audio only, since their single argument is the wet chain and in a px chain the incoming value lands there instead; use mix{} directly for visuals.

mix3{...} Provide a fixed mix of 1/3 wet.

mix4{...} Provide a fixed mix of 1/4 wet.

mix5{...} Provide a fixed mix of 1/5 wet.

mix6{...} Provide a fixed mix of 1/6 wet.

mix7{...} Provide a fixed mix of 1/7 wet.

mix8{...} Provide a fixed mix of 1/8 wet.

multitap{..., count:2} Run the given node chain count times in series like series, but also tap each stage's output and sum all the taps together into the final output.

multitap{..., 2} The count arg can be positional instead of being named.

multitap{{i}->..., 4} The chain can be a user defined function, given the stage index i, so each stage can differ. For example multitap{{i}->echo{1/2}>>gain{0.7^i}, 4} creates a decaying multi-tap echo.

It works on a visual synth px chain too, summing each stage's output into the shader. See the Visual Synth section.

noise{rate:1} Create a looping white-noise source node, so noise can be wired into an fx chain like any other source, eg noise{} >> lpf{800}. rate changes playback speed/pitch like a sample.

osc{'sawtooth', freq:440, phase:0} Oscillator node. The positional param is the waveform if a string (osc{'sine'}), otherwise the frequency (osc{440}, osc{110 + chaos{2}*40}). phase sets the starting phase.

panner{0} Create a stereo panner that can push a signal to the left (-1) or right (1).

parallel{..., count:2} Run the given node chain count times in parallel: the input is fanned out to every copy and the copies' outputs are summed back together.

parallel{..., 2} The count arg can be positional instead of being named.

parallel{{i}->..., 4} The chain can be a user defined function, given the copy index i, so each copy can differ. For example parallel{{i}->bpf{300*(i+1)}, 4} runs four bandpass filters at different frequencies in parallel.

It works on a visual synth px chain too, summing the copies into the shader — which is how the octave sums in the Visual Library are built. See the Visual Synth section.

sample{sample:'...', start:0s, rate:1} Create a sample player node that loads a sample from a URL and plays with the given start point and rate.

sample{sample:{x}->..., length:0.2s, rate:1} Create a sample player that creates a sample of given length from a user defined function and plays it at the given rate.

sample{sample:'...', loopstart:1, looplen:2, rate:1} Create a sample player that plays a section from the given sample as a loop.

series{..., count:2} Repeat the given node chain in series count times.

series{..., 2} The count arg can be positional instead of being named.

series{{i}->..., 4} The chain can be a user defined function, given the repeat index i, so each repeat can differ. For example series{{i}->lpf{600*(i+1)}, 4} chains four lowpass filters at increasing frequencies.

It works on a visual synth px chain too, writing the repeats into the shader one after another. See the Visual Synth section.

shaper{{x}->..., samples:2, oversample:'2x'} Create a wave shaper node, using a user defined function to create the wave shape. Oversmaple can be '2x', '4x', or 'none'. Note oversample other than 'none' may result in a signal delay.

stereo{l:...,r:...} Split the incoming signal into left and right channels, process each through a separate node chain given by the 'l' and 'r' arguments, and then join them back together into a single, stereo, signal.

stereo{...,...} The l and r arguments can be positional.

superosc{freq:440, detune:0, wavetable:{'sample/wt64/SUPERSAW.WAV', count:64, smooth:0}, wt:0, sync:0, crush:0, pwm:0, formant:0, unison:{1, detune:1.01, amp:1, pan:0.5}} Wavetable oscillator source node, as the superosc synth, for use in fx chains: superosc{440, wavetable:'sample/wt64/SUPERSAW.WAV', wt:sine} >> lpf{800}. Frequency is the positional param. Silent until the wavetable loads. Uses an AudioWorklet, so costs more audio render capacity.

freq=440 Frequency in Hz. value/freq both set it, mirroring the native osc node, so superosc{440}, superosc{55Hz} and superosc{2kHz} are all fine. It takes a modulating node chain too, eg superosc{110 + chaos{2}*40}.

detune=0 Shift the frequency in cents, mirroring the native osc node.

wavetable={'sample/wt64/SUPERSAW.WAV', count:64, smooth:0} Sample URL, sliced into count single-cycle frames (default 64). Use count:1 for single-cycle files, eg sample/wave/SAW.WAV, SINE.WAV, SQUARE.WAV, TRI.WAV. smooth (0 to 1) reduces clicks when using an ordinary sample rather than a wt64 table. Defaults to a single-cycle saw.

wt=0 Morph position across the wavetable's frames (0..1), lerping between adjacent frames.

sync=0 Oscillator hard-sync ratio (0 = off): the phase is remapped so the waveform restarts sync times per fundamental cycle, giving the classic hard-sync timbre. A negative sync keeps the same ratio magnitude but softens the phase reset with a short crossfade (a "soft sync"), for a less clicky, less aggressive tone.

crush=0 Phase-quantisation amount in bits (0 = off): the phase (after any sync remap) is snapped to 2^crush steps before the wavetable lookup, stepping the waveform for a lo-fi/aliased timbre, so crush=3 gives 8 steps and each extra bit doubles the resolution (max 12).

pwm=0 Phase power-warp amount (0 = off): the phase (after any sync remap) is raised to the power 2^pwm before the wavetable lookup, skewing the waveform toward its start (pwm>0) or end (pwm<0) like a generalised pulse width.

formant=0 Formant shift (0 = off): shifts the spectral formants up (>0) or down (<0) while keeping the pitch.

unison={1, detune:1.01, amp:1, pan:0.5} Detuned voices (1 to 16). detune is the max frequency ratio, amp the centre-to-outer voice level, pan the stereo spread of the outer voices. Output is mono unless there are 2+ voices and a non-zero pan.

tts{'hello', rate:1, pitch:50, speed:175} Text-to-speech source node: tts{'hello world'} >> lpf{800}. Voice options: pitch 0–99, speed words per minute, wordgap, amplitude 0–200, variant eg 'f2'; rate changes playback speed and pitch. Does not loop. A new phrase starts once synthesized; phrases are cached.

Play Samples

a Gameboy hihat
A Gameboy kick drum
b Noisy beep
B Short saw
c Voice/string
C Choral
d Woodblock
D Dirty snare
e Electronic Cowbell
E Ringing percussion
f Pops
F Trumpet stabs
g Ominous
G Ambient stabs
h Finger snaps
H Clap
i Jungle snare
I Rock snare
j Whines
J Ambient stabs
k Wood shaker
K Percussive hits
l Robot noise
L Noisy percussive hits
m 808 toms
M Acoustic toms
n Noise
N Gameboy SFX
o Snare drum
O Heavy snare
p Tabla
P Tabla long
q Ambient stabs
Q Electronic stabs
r Metal
R Metallic
s Shaker
S Tamborine
t Rimshot
T Cowbell
u Soft snare
U Misc. Fx
v Soft kick
V Hard kick
w Dub hits
W Distorted
x Bass drum
X Heavy kick
y Percussive hits
Y High buzz
z Scratch
Z Loud stabs
- Hi hat closed
| Hangdrum
= Hi hat open
/ Reverse sounds
* Clap
\ Lazer
~ Ride cymbal
% Noise bursts
$ Beatbox
# Crash
! Yeah!
+ Clicks
& Chime
@ Gameboy noise
: Hi-hats
1 Vocals (One)
2 Vocals (Two)
3 Vocals (Three)
4 Vocals (Four)

Nodes Library

Nodes library: include 'lib/nodes.limut'

This library contains basic helper functions wrapping node functions. As these afre user defined functions, all arguments can be positional.

apf{freq, q:5} All pass filter, 2 pole. q is resonance.

bpf{freq, q:5} Bandpass pass filter, 2 pole. q is resonance.

bwhpf{freq} Butterworth high pass filter, 2 pole. Maximally flat passband; no resonance.

bwhpf2{freq} Butterworth high pass filter, 2 pole. Alias of bwhpf.

bwhpf4{freq} Butterworth high pass filter, 4 pole. Maximally flat passband; no resonance.

bwlpf{freq} Butterworth low pass filter, 2 pole. Maximally flat passband; no resonance.

bwlpf2{freq} Butterworth low pass filter, 2 pole. Alias of bwlpf.

bwlpf4{freq} Butterworth low pass filter, 4 pole. Maximally flat passband; no resonance.

compressor{threshold:-50db, ratio:3db, attack:1ms, release:50ms, knee:2db} Dynamics compressor.

drive{amount:1/2} Waveshaper providing overdrive/distortion by applying an x/(1+x) based function. Use amount to push the distortion harder.

hpf{freq, q:5} High pass filter, 2 pole. q is resonance.

hsf{gain, freq:1100Hz} High shelf filter.

lfo{freq,wave:'triangle',lo:0,hi:1,phase:0} Oscillator wrapper for use as an LFO. It can run at any frequency (including audio frequency). The output range is controlled with the lo and hi arguments.

limiter{threshold:-50db, ratio:3db, attack:1ms, release:50ms, knee:2db} Dynamics limiter.

lpf{freq, q:5} Low pass filter, 2 pole. q is resonance.

lpf2{freq, q:5} Low pass filter, 2 pole. q is resonance. Alias of lpf.

lpf4{freq, q:5} Low pass filter, 4 pole. q is resonance.

lsf{gain, freq:1100Hz} Low shelf filter.

nf{freq, q:5} Notch filter, 2 pole. q is resonance.

osc.pulse{freq} Pulse wave oscillator.

osc.saw{freq} Sawtooth wave oscillator.

osc.sawtooth{freq} Sawtooth wave oscillator.

osc.sin{freq} Sine wave oscillator.

osc.sine{freq} Sine wave oscillator.

osc.square{freq} Square wave oscillator.

osc.tri{freq} Triangle wave oscillator.

osc.triangle{freq} Triangle wave oscillator.

pkf{gain, freq:1100Hz} Peaking (mid shelf) filter.

shaper.asym{gain:3/2,bias:1/2} Waveshaper providing saturation/distortion by applying an asymmetrical tanh. Use gain to push the distortion harder, and bias to push the asymmetry.

shaper.atan{gain:3/2} Waveshaper providing saturation/distortion by applying an arctan function. Use gain to push the distortion harder.

shaper.diode{gain:5/4,pos:0.9,neg:-0.3} Waveshaper providing saturation/distortion by applying assymmetrical cutoffs like a diode based fuzz.

shaper.poly{gain:1} Waveshaper providing saturation/distortion by applying a polynomial distortion. Use gain to push the distortion harder

shaper.pow{curve:0.9} Waveshaper providing saturation/distortion by applying a power curve. A curve of 1 is linear and has no effect, less than one applies distortion

shaper.tanh{gain:3/2} Waveshaper providing saturation/distortion by applying a tanh function. Use gain to push the distortion harder

stanh{gain:3/2} Convenience alias for shaper.tanh. Waveshaper providing saturation/distortion by applying a tanh function. Use gain to push the distortion harder

tilt{gain, freq:700Hz} Tilt EQ: a low shelf and high shelf with opposite gains around the pivot freq, so one control seesaws the spectrum (darker/brighter). gain is symmetric — tilt{3db} boosts highs +3dB and cuts lows -3dB; tilt{1} is flat. Built from lsf+hsf.

Effects Library

Effects library: include 'lib/effects.limut'

This library contains more sophisticated effects. As these are user defined functions, all arguments can be positional. In general, these effects are the wet path only; you can wrap them in the mix{} function to provide a dry signal as well.

airverb{delay:1/2b} Haunting airy reverb with flanger and stereo pingpong with given delay.

autofuzz{amount:1/2, freq:20} As fuzz, but the amount of fuzz is the same however loud the input is, while the output still follows the input's dynamics: g guitar 0, fx=autofuzz{3/4}>>cabinet. Softens the attack by about 15ms; freq is the level tracking speed (higher = snappier attack, rougher bass). Quiet tails are fuzzed too, down to -60dB. It does not clean up when you play softly.

cabinet{flatten:1} Speaker cabinet voicing, to tame the harsh upper harmonics of saturation; put one after each saturation stage. flatten is the strength: 0 transparent, 1 fully tamed.

chorus{rate:0.62, time:0.0025, depth:0.002, voices:3, spread:3/4, wobble:0.0003, wrate:6.67} Stereo chorus: voices copies delayed by time±depth seconds, swept by staggered rate Hz LFOs and spread across the stereo field by spread. wobble adds a fast wrate Hz shimmer. Times are plain numbers in seconds.

p1 wave 0, fx=mix{chorus{rate:1, depth:0.003},1/2}

demon{} Demon voice effect for use with vocal audio (eg through a microphone and the external synth).

disperser{freq:440Hz, q:3, stages:24} Series chain of allpass filters that smears transients into a frequency-swept tail. Useful for creating dub-style boom kicks and resonant clicks. freq is the resonant frequency, q is the allpass Q, stages is the number of allpass filters in series.

duck{depth:1, length:0.5, period:1, attack:0.1} Sidechain-style pumping: periodically dips the volume. depth is how far it drops (1 = silence), length the time to the lowest point, period the time between dips, attack the recovery time.

echo{time:1/8b, feedback:0.7, max} Echo effect wraps delay with feedback and a dry path to give a complete echo effect.

echo{[60ms:500ms]l8@f, max:500ms} max defaults to twice the echo time, read once at the start of the event. A time that sweeps more than that needs an explicit max, otherwise the longer delays are clamped.

ensemble{rate:0.5, time:0.0035, depth:0.0018} Juno-style ensemble: two chorus voices with opposite LFOs, panned hard left and right, each mixed with the dry signal. Includes the dry signal, so use it without mix{}.

flanger{control:[0,1]l8@f, feedback:0, lo:0.0005, hi:0.007} Flanger effect.

follower{freq:20, boost:pi/2} Envelope follower: turns the input into a roughly 0 to 1 control signal tracking its volume. freq is response speed (higher = snappier), boost scales the output. Attack and release are the same speed. Typically fed a player's .pre tap inside a gain{} to sidechain or gate another player.

q play 0., fx=gain{1 - kick.pre>>follower{}} Sidechain ducking: q's volume dips whenever kick sounds. Route the modulator to the silent bus (kick play X..., bus=silent) to hear it only through its .pre tap. Drop the 1 - for the opposite: gate q open only while the kick plays.

fuzz{amount:1/2, level:1/24} Drive with one control: 0 clean, then overdrive, distortion, and fuzz at 1. Loudness stays roughly level as amount changes, so it can be animated: g guitar 0, fx=fuzz{[0,1]l8@f}>>cabinet. The fuzz and the loudness match only depend on the input being near level (a typical single synth note): a louder player (chords, high amp) comes out fuzzier but no louder, a quieter one cleaner and louder, so set level to match. autofuzz does this for you.

gatedverb{length:1/4b, fade:0.05, rise:0, hpf:200} 80s gated reverb: a flat dense tail cut off sharply after length. rise swells the tail before the cut. Wet only, eg s play ..o., fx=mix{gatedverb,3/4}.

grain{ratio, length:0.03, phase:0, max} Granular grain: replays recent audio at the pitch ratio, with windows of length seconds. phase (0 to 1) staggers grains; two grains at phases p and p+1/2 overlap seamlessly. max overrides the max delay (default length*16).

multiband{{i,centre}->..., count:3} Split into count log-spaced bands, process each through the chain built by the callback (given band index i and centre frequency), and sum. Left unprocessed the bands sum back to the input exactly.

multiband{{i,centre}->..., 8} The band count arg can be positional (second, as in series) instead of being named.

multiband{count:3} With no callback the bands are simply split and recombined (a no-op), useful as a starting point.

multiband2{{i,centre}->..., count:3} Alias of multiband (2 pole crossover filters).

multiband4{{i,centre}->..., count:3} As multiband but with 4 pole crossover filters: steeper crossovers so less bleed between bands. Still fully transparent when left unprocessed.

multibandbp{{i,centre,lo,hi}->..., count:3, lo:20, hi:20000} As multiband but with tighter bandpass bands, which do not sum back exactly (expect ripple at the crossovers). lo/hi set the overall range. The callback also receives each band's lo/hi edges.

ott{depth:1, count:3, trim:1} OTT style multiband compression: count bands each compressed hard and fast, bringing detail forward. Downward only (no upward expansion). depth 0 is transparent, 1 the usual heavy setting.

trim Output level, default 1. Not gain neutral: it gets louder as depth rises, so use trim to level match.

phaser{control:[0,1]l4@f, lo:300Hz, hi:2600Hz, stages:4, q:1/2} Phaser effect.

pingpong{time:1/4b, feedback:0.7} Ping pong echo; the echoes alternate stereo sides.

reverb{length:1b, curve:3} Reverb effect with given length and power falloff curve.

shifter{ratio, length:0.1} Pitch shifter effect: two overlapping grains of the given length (in seconds) transpose the audio by exactly the given pitch ratio. shifter{1} is transparent.

shimmer{ratio:2,length:2s} Shimmer reverb.

tape{wow:1,cut:-10db} Cassette tape effect with 'wow' pitch wobble and low/high freq cut .

ultracomb{f:smooth{[]r1@f,att:1}, p:smooth{[]r2/3@f,att:1}, s:smooth{[]r1/2@f,att:1}, stages:6} Ultracomb filter; combination flanger, phaser and pitch shifter to create growling sounds. f, p and s are the control parameters.

vocoder{mod, count:16, freq:40, tilt:0.8, boost:pi/2, lo:150, hi:7000} Vocoder: imposes the spectral envelope of the modulator mod (a .pre tap) onto the signal in this fx chain. The carrier should be bright and harmonically rich (saws, pulse, noise). count is the number of bands, between lo and hi (a speech range by default; lo:20, hi:20000 for full range). freq is follower speed (higher = crisper consonants). tilt brightens the high bands, tilt×6 dB/octave; raise it (1–2) for more intelligible speech, 0 for flat. boost scales the level.

v1 saw, oct=2, fx=vocoder{voice.pre} with voice sample 'X-X-', bus=silent: the saw takes on the voice's rhythm and formants; the silent bus means the voice is heard only through the vocoder.

vocodethis{wave:'saw', unison:3, freq:eventpitch@f} Vocode the player it is applied to, with its own carrier: unison detuned oscillators of waveform wave at freq (the event pitch by default). Fully wet; blend with mix{} if needed.

p1 sample 'voice', oct=2, fx=vocodethis{} — the synth sings the sample's words at the player's pitch.

Visual Library

Visual library: include 'lib/visual.limut'

Smooth noise, 2d and 3d transforms, colour palettes, a kaleidoscope pattern, a VHS tape effect and a synthwave scene for visualsynth px chains, written as ordinary user defined functions.

Noise

Smooth noise interpolates random values (value noise) or random gradients (perlin noise, less blocky) across a lattice, so neighbouring pixels get neighbouring values. Compare px=pxhash (white noise).

All noise functions take {in, scale:4, seed:0}, all positional. in is the incoming value, so they pipe (px=fbm2) or take a value of their own (px=noise2{id*8}). scale is the number of lattice cells across the coordinate space; animate seed for churning noise. The 1/2/3 suffix is how many dimensions the noise varies over; every one returns a single value in all four channels. The 3d ones take their third dimension from the incoming z, so drive z for noise that moves in place.

noise1 One dimensional value noise, 0 to 1, varying along x: px=noise1{scale:8} is soft vertical bands.

noise2 Two dimensional value noise, 0 to 1: px=noise2{scale:16}.

noise3 Three dimensional value noise, 0 to 1: px=noise3{id>>set{z:[]l8}}. Value noise briefly eases to a stop each time z crosses a lattice cell; perlin noise does not.

perlin1, perlin2, perlin3 Gradient (perlin) noise, 0 to 1.

perlin1s, perlin2s, perlin3s Signed perlin noise, roughly −1 to 1, for displacing coordinates: px=add{perlin2s{id,8}/4}>>tex{webcam{}}.

fbm2, fbm3 Fractional brownian motion: four octaves of noise2/noise3 summed into 0 to 1, for cloud and terrain looks: px=fbm2>>tex1d{{x}->{labh:x}}, px=fbm3{id>>set{z:[]l8}}.

turb2, turb3 Turbulence: the octave sum of absolute signed noise, creased at the zero crossings, for smoke and marble.

noise2t{in, scale:4, seed:0, t:time/8}, perlin2t, perlin2st, fbm2t, turb2t The 2d fields animated in place by t (in beats, default one lattice cell per 8 beats; t:0 freezes): px=fbm2t>>cospal{#036}. Higher scale also animates faster.

For more or fewer octaves, use parallel{} or a folding loop{}, eg px=loop{mul{2}, 3, map:{v,i}->noise2{v}*(8/(2^i))}/15 (divisor is 2^octaves-1). Helpers for building your own noise: noiseface, noisefacefn, perlingrad, perlinface, noiseflat1, noiseflat2, noiseflat3, noise1at, noise2at, noise3at, perlin1at, perlin2at, perlin3at, octshift, kallinefn.

Transforms

Transforms normally go at the head of a chain, transforming the coordinate the shape or texture after them sees. Order reads backwards: the transform written last is applied to the shape first.

mat2{in, xx:1, xy:0, yx:0, yy:1} 2x2 matrix on xy (identity by default): mat2{xy:1/2} shears, mat2{xx:-1} mirrors in x.

rot2{in, a:0} Rotate xy about the origin, in turns; positive rotates the picture anticlockwise: px=rot2{[0:1]l8}>>tex{webcam{}}.

mat3{in, xx:1, xy:0, xz:0, yx:0, yy:1, yz:0, zx:0, zy:0, zz:1} 3x3 matrix on xyz (identity by default), eg for posing a 3d scene.

rot3x{in, a:0}, rot3y{in, a:0}, rot3z{in, a:0} Rotate about each axis, in turns. rot3y spins a standing object; rot3z is the same as rot2.

topolar{in}, frompolar{in} Convert xy to and from polar: radius in x, angle in y in turns (−1/2 to 1/2, seam along −x; add >>fract for 0 to 1). px=topolar.x>>cospal is rings, px=topolar.y>>cospal a colour wheel. Go out, change the radius or angle, and come back:

v1 visualsynth, px=topolar>>add{y:id.x/4}>>frompolar>>sdbox{b:{x:1,y:1/8}}>>sdfill
v1 visualsynth, px=topolar>>set{y:floor{id.y*8}/8}>>frompolar>>tex{webcam{}}

The first twists, the second makes eight wedges. Between topolar and frompolar, id.x is the radius, not x.

Colour

cospal{in, d:#012, c:#fff, b:#888, a:#888} Cosine palette (after Inigo Quilez): a+b*cos{6.28318*(c*in+d)} per channel, turning a single value into a cycling colour. a is the centre, b the swing, c the cycles over 0 to 1 and d the phase. Args are live and can animate. Pipe it a single value: px=fbm2>>cospal, px=length>>cospal, or px=cospal{id.x}.

Args after in are in reverse order, so the phase is first positional: cospal{#05a} is a rainbow, cospal{#000} a grey ramp, cospal{c:#248} gives each channel different cycles, cospal{{h:[]n}} turns the phase through the hues.

It also sets alpha; add >>set{a:1} if layering with blend. To name the colours outright, use the pal{} node instead.

Patterns

kal{shape:0, in:id, t:time/2, glow:1, n:8} The kal visual's kaleidoscope pattern as a single value field: px=kal{glow:1/2}/2>>pal{#012,#28a,#fff}. glow is filament thickness and brightness (the default is mostly white bare; try kal{glow:1/3}). shape is the visual's note value and is the first positional (kal{5}). t drives the motion, in beats (t:0 freezes). n is the number of levels, and cannot be smoothly animated.

kal2{shape:0, in:id, t:time/2, glow:1, n:8} As kal, but every shape value gives a usable picture, so it is the one to sweep: px=kal2{[0:8]l32, glow:1/2}/2>>pal{#012,#28a,#fff}. kal washes out to white around shape 2 (and every 8 or so after). The two match at shape 0.

The kal visual's other params are done with the chain: transforms before (px=rot2{[0:1]l32}>>kal{glow:1/3}, kal{in:id/2} to zoom) and a palette after.

Perpetual zoom

droste{src, scale:2, t:time/4, soft:1/4, gamma:2, round:1, in:id} Zoom into src forever, as nested copies in circular rings (round:0 for squares, between for rounded squares): each copy shrinks inside the last, so whatever leaves the frame reappears at the centre. t is the zoom in powers of scale (negative zooms out). soft crossfades the seam between rings, from 0 (hard) up to 1. px=droste{noise2>>cospal{#05a}}

zoomer{src, scale:2, n:4, t:time/4, gamma:2, in:id} As droste, but n whole copies are blended, each fading in small and out large. n cannot animate. px=zoomer{kal{glow:1/3}/2>>pal{#012,#28a,#fff}}

droste only ever shows each copy's ring between 1/scale and 1, so the centre of src is never seen. gamma is the blend space: 2 is close to linear light, which keeps mixes from dimming; 1 blends the colour values as they are. Negative channels blend as 0. src must read its input coordinate: a flat colour won't zoom. Pass a transform as in: (droste{src, in:rot2{1/8}}), not piped in, because a piped value lands on src.

VHS

vhs{src, amt:1, t:time} VHS tape effect (the vhs visual param, which a px chain does not get): creases, wobble, switching noise, scanlines and colour shift. src is fed the warped coordinate: px=vhs{tex{webcam{}}}. amt mixes it in, t animates it.

vhsuv{in, amt:1, t:time}, vhsrgb{in, amt:1, t:time} The coordinate and colour halves of vhs: px=vhsuv>>tex{webcam{}}>>vhsrgb is px=vhs{tex{webcam{}}}.

Synthwave

synthwave{in, t:time, pulse:0, glow:1, sun:0.35, hills:0.3, hue:0} A synthwave scene: dusk sky with twinkling stars, striped sun, wireframe mountains and a neon grid scrolling one line per beat. Drive the params from the music:

v1 visualsynth, px=synthwave{pulse:kd.pulse, hills:1/4+bs.pulse/8, hue:[0:1]l64}

pulse flashes the grid, haze and sun halo (0 to 1). glow is the neon brightness, sun the sun radius, hills the mountain height (0 for none), and hue rotates every colour (1 is a full turn). t drives the scrolling, stripes and twinkle (t:time*2 for double speed, t:0 to freeze). The horizon is at the vertical centre; move it with a transform in front, eg px=add{y:1/4}>>synthwave.

SDF Library

Signed distance field library: include 'lib/sdf.limut'

2d signed distance field shapes for visualsynth px chains, from Inigo Quilez's 2d distance functions. Includes the Visual Library.

A shape gives each pixel's distance to its edge: negative inside, positive outside. Draw it with a separate step (sdfill etc). Distances combine with arithmetic: min is union, subtracting rounds corners.

v1 visualsynth, px=sdcircle{r:1/2}>>sdfill
v1 visualsynth, px=sdstar{r:3/4}>>sdbands
v1 visualsynth, px=sdbox{b:{x:3/4,y:1/8}}>>sdannular{1/32}>>sdfill{#0f0}

Every shape takes the incoming value as its first argument, so they pipe or take a coordinate of their own (px=sdcircle{id*2, 1/4}). Units are the px coordinates, so a radius is a fraction of half the frame height.

A positional argument only lands where you expect when the call is piped: px=sdcircle{1/2} is a radius, but in px=sdfill{sdcircle{1/2}} the 1/2 is the coordinate. Name the args (sdcircle{r:1/2}) or pass the coordinate (sdcircle{id, 1/2}) when not piping. The same applies to the noise functions.

Shapes

sdcircle{in, r:1/2} Circle of radius r.

sdbox{in, b:1/2} Rectangle of half extents b, ie b is the distance from the middle to an edge, not the width. A plain number gives a square (sdbox{b:1/2}); a vector gives a rectangle (sdbox{b:{x:3/4,y:1/8}}).

sdsegment{in, ax:-1/2, ay:0, bx:1/2, by:0} Distance to a line between two points; needs sdround to be visible: px=sdsegment{ax:-3/4, ay:-1/2, bx:3/4, by:1/2}>>sdround{1/16}>>sdfill.

sdtriangle{in, r:1/2} Equilateral triangle of radius r, pointing up.

sdpentagon{in, r:1/2}, sdhexagon{in, r:1/2} Regular pentagon and hexagon, r being the distance from the middle to the flat of an edge. The pentagon points up; the hexagon has flat top and bottom and points left and right.

sdstar{in, r:1/2, n:5, m:10/3} Star with n points of radius r. m is notch depth, from 2 (a regular polygon) to n (needles): px=sdstar{r:3/4, m:[2:5]l4}>>sdfill.

sdheart{in, r:1/2} Heart of half height r.

sdcross{in, arm:1/2, thick:1/6, r:0} Greek cross (a plus sign) with arms of half length arm and half thickness thick. r rounds the corners, inner ones included, without growing the shape.

sdcirclewave{in, tb:1/2, ra:1/4} An endless wave of circular arcs of radius ra along x; tb is how much of each circle is used (1/2 is half circles). A line, so give it sdround: px=sdcirclewave{tb:[0:1]l8}>>sdround{1/64}>>sdfill.

To move a shape, shift the coordinate going in, which moves it the opposite way: px=add{x:1/2}>>sdcircle{r:1/4}>>sdfill moves it left. Use rot2 and mat2 to rotate and shear.

Voronoi

Moving voronoi cells. All three take {in, scale:4, seed:0, t:time/8, jitter:1}: scale is cells per unit, each cell's point orbits once per t step of 1 (t:0 freezes), jitter is how far points wander from the cell centre.

sdvoronoi Distance to the nearest cell edge, 0 on the edge: px=sdvoronoi{scale:6}>>sdannular{1/96}>>sdfill{#0ff}.

sdvorcentre Distance to the cell's point: px=sdvorcentre>>sdround{1/16}>>sdfill.

vorcell A random 0 to 1 value per cell: px=vorcell{scale:6}>>cospal{#05a}.

jitter above 1 gives wrong distances. Each call does its own search, so two in one chain cost twice as much.

Combining

Union and intersection are min{} and max{}: px=min{sdcircle{r:1/3}, sdbox{b:{x:3/4,y:1/8}}}>>sdfill. These take the distance first, so they pipe.

sdround{d, r:1/16} Round off every corner by r. It also grows the shape by r in every direction, which is what turns a line into a stroke of that half width.

sdannular{d, w:1/32} Hollow the shape out into an outline of half thickness w. Unlike sdstroke below this gives back a distance field rather than a colour, so the outline can go on being combined with other shapes.

sdsub{a, b} Cut shape b out of shape a; eg px=sdsub{sdbox{b:1/2}, sdcircle{r:1/3}}>>sdfill is a square with a round hole.

sdblend{a, b, k:1/8} Smooth union of width k, so shapes flow together like liquid.

Drawing

These turn a distance into a picture. soft is the antialiasing width in coordinate units, so a shape scaled down wants a smaller one.

sdfill{d, col:#fff, bg:#000, soft:0.01} Fill the inside with col and the outside with bg.

sdstroke{d, w:0.01, col:#fff, bg:#000, soft:0.01} Draw the edge only, w either side of it.

sdbands{d, outside:#e93, inside:#adf, k:6, n:150, soft:0.01} Show the whole field: coloured inside and out, fading with distance, banded, with the edge in white. Useful while building a shape. k is fade speed, n the band count.

Anything that maps one value to a colour works too: px=sdstar{r:3/4}>>cospal, px=sdheart{r:3/4}>>pal{0,#408,red,1}.

Helpers for writing your own shapes: sdflat, sdvec, sdval, sdstep, sdfold. A function body that uses let{} must start with id>>, and must not end in a call with positional args.

3D SDF Library

3d signed distance fields: include 'lib/sdf3.limut'

3d solids for visualsynth px chains, a raymarcher to draw them, and fractals, from Inigo Quilez's 3d distance functions. Includes the SDF and Visual Libraries.

v1 visualsynth, px=sd3march>>sd3lit
set scene = pxfn{{p} -> min{ sd3sphere{p, r:1/2}, sd3plane{p} }}
v1 visualsynth, px=sd3march{scene, yaw:[0:1]l8}>>sd3lit{scene}
v1 visualsynth, px=let{'scene', pxfn{{p} -> sd3torus{p}}}>>sd3march{scene}>>sd3lit{scene}

A scene is a function of the point, and should be a pxfn{}, defined with set or inline with let{} (which compiles smaller), then passed to each function that needs it. Without pxfn{} the scene is copied into the shader at every use, which can exhaust a small GPU. The scene can animate via params, but cannot see an outside let{}.

The scene is the first argument of every function that takes one, so sd3march{scene} works with a bare name.

Coordinates: x right, y up, z away from the viewer, same units as 2d. The default camera is at z=-3 looking at the origin.

Shapes

Inside a scene, pass the point explicitly (sd3sphere{p, r:1/2}).

sd3sphere{in, r:1/2} Sphere of radius r.

sd3box{in, b:1/2} Box of half extents b, ie the distance from the middle to a face rather than the width. A plain number gives a cube, a vector a cuboid: sd3box{b:{x:1,y:1/8,z:1}} is a slab.

sd3torus{in, r:1/2, t:1/8} Torus lying in the xz plane, r the radius of the ring measured to the middle of the tube and t the radius of the tube itself.

sd3capsule{in, ax:0, ay:-1/2, az:0, bx:0, by:1/2, bz:0, r:1/4} A line from a to b with radius r: a cylinder with rounded ends.

sd3cylinder{in, r:1/2, h:1/2} Cylinder of radius r and half height h, standing on the y axis with flat ends.

sd3plane{in, nx:0, ny:1, nz:0, h:1/2} The half space below a plane with unit normal nx,ny,nz, lifted by h (default: a floor at y=-1/2). Gives shadows a ground.

sd3octahedron{in, r:1/2} Octahedron of radius r. Not an exact distance away from the surface, so rounding or blending it is inexact.

Shapes from 2d ones

Make a solid from any 2d SDF Library shape, passed as an argument:

sd3extrude{in, shape, h:1/4} Extrude a 2d shape (in xy) along z to half thickness h: set slab = pxfn{{p}->sd3extrude{p, sdstar{r:1/2}, h:1/8}}.

sd3revolve{in, shape, o:1/2} Revolve a 2d shape about the y axis at distance o; with a circle it is a torus.

Fractals
set bulb = pxfn{{p} -> sd3mandelbulb{p}}
v1 visualsynth, px=sd3march{bulb, steps:96}>>sd3lit{bulb}

For all fractals: the iteration count n is fixed when the line runs and cannot be smoothly animated (other params can). The distance is an estimate, so use step below 1 on sd3march if the surface shows holes or banding. Fractals are expensive; always use a pxfn{}.

sd3menger{in, n:3, b:1/2, scale:3} Menger sponge of half extent b. scale other than 3 gives other shapes. Marches at full step.

sd3sierpinski{in, n:8, scale:2, ox:1, oy:1, oz:1, yaw:0, pitch:0, r:2} Sierpinski tetrahedron, generalised to a kaleidoscopic IFS. Exact and cheap, so large n is fine.

yaw and pitch (turns) twist the folds into kaleidoscopic shapes and are the thing to animate: px=sd3march{pxfn{{p}->sd3sierpinski{p, yaw:[0:1]l16}}, step:1/2}>>sd3lit. r sets the thickness (r/scale^n); it must be non zero for the march to hit anything.

sd3mandelbox{in, n:8, scale:2, minr:1/2, fixr:1, b:1/8} The mandelbox.

scale changes the shape and its size; b sizes it in the frame and must be adjusted to match: about 1/8 for 2, 1/4 for −3/2, 1/12 for 3. A wrong b gives an empty frame: px=sd3march{pxfn{{p}->sd3mandelbox{p, scale:-3/2, b:1/4}}, steps:96}>>sd3lit.

sd3mandelbulb{in, n:8, power:8, bail:2} The mandelbulb, standing along y.

power need not be whole: 8 is the classic, 2 a lumpy blob: px=sd3march{pxfn{{p}->sd3mandelbulb{p, power:[2:8]l16}}, steps:96}>>sd3lit. Raise bail only for large power.

Combining

min{}/max{}, sdround, sdannular, sdsub and sdblend from the SDF Library all work on 3d distances.

sd3rep{in, p:1} Repeat space on a lattice of spacing p (all components non zero). Raise far on the march to see further.

Marching

sd3march{scene, in, ox:0, oy:0, oz:-3, fov:1, yaw:0, pitch:0, steps:64, eps:1/2000, far:16, step:1} March a ray from the camera into the scene; outputs the point reached.

The camera is at ox,oy,oz looking along +z, turned by pitch then yaw (turns). Larger fov is a longer lens. steps is the maximum step count and cannot be smoothly animated; eps is the hit distance; far the give-up distance.

step scales each step. Below 1 fixes holes and banding on fractals and sd3octahedron; raise steps with it (step:1/2, steps:128). Raising eps is a cheaper alternative that softens fine detail.

The march sets names for shading to read: sd3d (last scene distance, in x), sd3t (distance travelled) and sd3n (surface normal).

Shading

sd3lit{scene, in, lx:1/2, ly:1, lz:-1/2, col:#fff, bg:#123, amb:1/5, shadow:1, occ:1, eps:1/500, fog:0} Shade a marched point: misses are bg, hits are col lit from lx,ly,lz plus ambient, with fog fading to bg by distance.

shadow and occ multiply the light and ambient terms; pass the functions below for shadows and occlusion:

set scene = pxfn{{p} -> min{ sd3sphere{p, r:1/2}, sd3plane{p} }}
v1 visualsynth, px=sd3march{scene}>>sd3lit{scene, shadow:sd3shadow{scene}, occ:sd3ao{scene}}

sd3shadow{scene, in, lx:1/2, ly:1, lz:-1/2, k:8, steps:24, mint:1/64, far:8} Soft shadow, 1 lit to 0 shadowed. Smaller k is softer. Use the same light position as sd3lit.

sd3ao{scene, in, steps:4, hstep:1/32, falloff:0.95, k:3} Ambient occlusion: darkens creases and the ground around objects.

sd3nrm{scene, in, h:1/2000} Surface normal at a point (already available as sd3n after a march). h too small is noisy, too large rounds edges.

There is one colour per render, not per shape. col can vary, eg col:fbm3{id}, and fog:1/16 with a dark bg gives depth. For custom shading, read sd3d, sd3t and sd3n in your own chain (not in an argument at the callsite).

Helpers for writing your own shapes: sd3flat, sd3vec, sd3radial, sd3reflect, sd3opaque, plus sdval and sdstep. Arithmetic on two constants alone can come out flat white; make sure a node is involved.

Synthwave Library

Synthwave library: include 'preset/synthwave.limut'
Visual

neonbars Visual preset of neon perspective bars scrolling towards the viewer, in the lower half of the frame

neonbits Visual preset of bit patterns, in the lower half of the frame

neongrid Visual preset of a pink neon perspective grid scrolling towards the viewer, in the lower half of the frame. Shader loaded from shadertoy.com

neonheart Visual preset of a neon heart with a VHS TV effect. Shader loaded from shadertoy.com

neonlines Visual preset of wavy neon lines, in the lower half of the frame

neonshapes Visual preset of prismatic neon shapes with a VHS TV effect. Shader loaded from shadertoy.com

neonsine Visual preset of neon sine waves with a VHS TV effect. Shader loaded from shadertoy.com

skybars Visual preset of horizontal bars across the sky

sun Visual preset of a setting sun. Shader loaded from shadertoy.com

sunsetsky Visual preset of a sunset gradient sky in the top half of the frame

vhsbuffer Visual preset of a buffer that displays like a VHS tape on a CRT TV

Audio

basspluck plucked synth bass preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

blade Wobbly sawtooth lead preset in the style of the Blade Runner opening titles. Eg l blade 0v___1v-2__-3___....

cutoff Cutoff control 0 to 1; defaults to 1/2.

blips random computer blips preset

bloom Big detuned saw bass pad

blues Sawtooth lead preset in the style of Blade Runner blues

cutoff Cutoff control 0 to 1; defaults to 1/2.

chime Synth chime preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

chiparp Chip style preset that plays a simple arpeggio from the given note

cybass Cyberpunk bass preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

cyborg Wobbly synth lead preset

cutoff Filter cutoff control from 0 to 1.

cydist Cyberpunk distorted preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

hiss Noise hiss synth preset; can be used for percussion

laserharp PWM preset vaguely in the style of the Elka Synthex laser harp sound. Eg l laserharp [98]_7_____[6#6]_5_____

cutoff Cutoff control 0 to 1; defaults to 1/2.

lushpad 80s style string pad preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

moroder Moroder bass preset with a 1/16 note delay echo with stereo pan, broadly in the style of the I Feel Love bass. Eg b moroder 00-3-1b

cutoff Cutoff control 0 to 1; defaults to a slow LFO sweep.

oxy Attempt at the Solina strings from an Eminent organ, with a phaser and delay effect. Eg o oxy -7-3-50-3264, dur=1/2, sus=1/4

cutoff Cutoff control 0 to 1; defaults to 1/2.

pick Staccato pluck sound with a haunting reverb

cutoff Filter cutoff control from 0 to 1.

decay Filter pluck decay from 0 to 1.

play80s Percussion preset with a punch 80s style

softkeys Gentle chiming keys sound

softpad Soft swirly pad preset with phaser

space Spacey vibrato lead preset

stringpad String pad preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

synbass Synth bass preset based on a sawtooth

cutoff Cutoff control 0 to 1; defaults to 1/2.

synlead Synth lead preset with glide (pitch slide betwen notes)

cutoff Cutoff control 0 to 1; defaults to 1/2.

synpluck Synth pluck preset

cutoff Cutoff control 0 to 1; defaults to 1/2.

vambi PWM ambient preset in the style of Vangelis.

808 Library

808 library: include 'preset/808.limut'
Individual sounds

bd808 Simulated 808 bd kick. Based on io808.

cb808 Simulated 808 cb cowbell. Based on io808.

ch808 Simulated 808 ch closed hat. Based on io808.

cl808 Simulated 808 cl clave. Based on io808.

cp808 Simulated 808 cp clap. Based on io808.

cy808 Simulated 808 cy cymbal. Based on io808.

hc808 Simulated 808 hc high conga. Based on io808.

ht808 Simulated 808 ht high tom. Based on io808.

lc808 Simulated 808 lc low conga. Based on io808.

lt808 Simulated 808 lt low tom. Based on io808.

ma808 Simulated 808 ma maraca. Based on io808.

mc808 Simulated 808 mc mid conga. Based on io808.

mt808 Simulated 808 mt mid tom. Based on io808.

oh808 Simulated 808 oh open hat. Based on io808.

rs808 Simulated 808 rs rimshot. Based on io808.

sd808 Simulated 808 sd snare. Based on io808.

Combined

c808 Combined 808 congas. Use 'h' and 'l' pattern flags to specify high and low congas; medium conga is default. Eg p c808 .0h00l.

h808 Combined 808 hihat. Use 'o' pattern flag to specify open hihat; closed is the default. Eg p h808 000o0.

t808 Combined 808 toms. Use 'h' and 'l' pattern flags to specify high and low toms; medium tom is default. Eg p t808 .0h00l.

tr808 Combined 808, all in one. Here pattern value does not specify accent, it specifies which 808 sound to play (similar to the play synth. For example, p tr808 xo plays 808 'bd' then 'sd'. Use the '^' pattern flag for accent. For example ss^ plays a 'ma', then an accented 'ma'. The following pattern values are supported (everything else defaults to closed hihat):
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

303 Library

303 library: include 'preset/303.limut'
Audio

tb303 Simulated 303 (acid) bass. The following custom control params are available, corresponding to the main control knobs on a 303:

wave 303 waveform. set to 'saw' or 'square'. The default is 'saw'.

cutoff 303 cutoff control 0 to 1; default 1/2. Set the filter cutoff. Higher values give a brighter sound with more harmonics. Note vel is not automatically included in cutoff for the 303 because cutoff is very specific in the 303.

resonance 303 resonance control 0 to 1; default 1/2. Set the filter resonance. Higher values give a squelchy acid sound.

envmod 303 envmod control 0 to 1; default 1/2. Set the depth of envelope modulation. Higher values change the filter cutoff more over the course of a single note.

decay 303 decay control 0 to 1; default 1/2. Set the envelope decay time. Set the envelope decay time for filter envelope. Higher values take longer to decay.

accent 303 accent control 0 to 1; default 1/2. Set how much effect accented notes have.

The following pattern flags can be used:

a accent flag. Makes the preceding note accented, making it louder and brighter.

u up octave. Makes the preceding note an octave higher.

d down octave. Makes the preceding note an octave lower.

s slide. This does not (yet) work the same as the 303 slide, which would slide the _next_ note from this one, and not retrigger the envelope for it. However currently in the limut 303, it slides the _current_ note, and the envelope still retriggers (no legato).

For exampple b tb303 0u0d0ua0 plays a pattern of four notes, one up an octave, the next down an octave, one up an octave and accented, then one without any flags.

909 Library

909 library: include 'preset/909.limut'
Synthesised

bd909 Simulated 909 kick. The value of the event gives the accent. Control params:

level Volume level from 0 to 1, default 1/2.

tune BD tune from 0 to 1, default 1/2. Changes the pitch sweep decay time rather than the actual pitch

attack Attack level from 0 to 1, default 1/2. Changes the level of initial click and noise hit rather than the actual attack.

decay Decay from 0 to 1, default 1/2.

cc909 Simulated 909 crash cymbal. The value of the event gives the accent. Control params:

level Volume level from 0 to 1, default 1/2.

tune Tune from 0 to 1, default 1/2.

cp909 Simulated 909 handclap. The value of the event gives the accent. Control params:

level Volume level from 0 to 1, default 1/2.

h909 Simulated 909 hihats. The value of the event gives the accent. Use an o pattern flag for an open hihat. A choke group is used so an open hihat will be cutoff by subsequent hihat hits. Control params:

level Volume level from 0 to 1, default 1/2.

chdecay Closed hihat decay from 0 to 1, default 1/2.

ohdecay Open hihat decay from 0 to 1, default 1/2.

rc909 Simulated 909 ride cymbal. The value of the event gives the accent. Control params:

level Volume level from 0 to 1, default 1/2.

tune Tune from 0 to 1, default 1/2.

rs909 Simulated 909 rimshot. The value of the event gives the accent. Control params:

level Volume level from 0 to 1, default 1/2.

sd909 Simulated 909 snare drum. The value of the event gives the accent. Control params:

level Volume level from 0 to 1, default 1/2.

tune Tune from 0 to 1, default 1/2.

tone Tone from 0 to 1, default 1/2.

snappy Snappy from 0 to 1, default 1/2.

t909 Simulated 909 toms. The value of the event gives the accent. Use an l pattern flag for a low tom. Use an h pattern flag for a high tom. Control params:

level Volume level from 0 to 1, default 1/2.

tune Tune from 0 to 1, default 1/2.

decay Decay from 0 to 1, default 1/2.

Sampled

There is also a sampled version of the 909 which does not sound quite as good, and is not as controllable, but which uses less processing power:

bds909 Sampled 909 kick.

ccs909 Sampled 909 crash cymbal.

cps909 Sampled 909 handclap.

hs909 Sampled 909 hihats.

rcs909 Sampled 909 ride cymbal.

rss909 Sampled 909 rimshot.

sds909 Sampled 909 snare drum.

ts909 Sampled 909 toms.

Trance Library

Trance library: include 'preset/trance.limut'
Audio

trance Classic supersaw trance lead. Note by default, the add param has a small chord setup for a fatter sound: add=(0,2). The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to an LFO sweep. Set the filter cutoff. Higher values give a brighter sound.

decay Decay control 0 to 1; defaults to an LFO sweep. Set the filter envelope decay time. Higher values take longer to decay.

detune Detune control 0 to 1; default 1/2. Set the amount of detune between the saw voices.

Techno Library

Techno library: include 'preset/techno.limut'
Audio

The Techno library also includes the 909, 303 and trance libraries.

growl Randomised growl/snarl sounds.

hollow Hollow sounding pad

psybass Plucky rolling psytrance/techno bass: saw with a very fast resonant filter pluck, over a sine sub an octave below. The following custom control params are available:

cutoff Cutoff control 0 to 1; default 1/2. Higher values give a brighter, more open pluck.

decay Decay control 0 to 1; default 1/2. Higher values make the filter pluck longer and less snappy.

sub Sub level 0 to 1; default 1. Set the level of the sine sub octave; 0 turns it off for just the saw.

robot Metallic saw with a talk-y filter.

sweep Slow saw bass with an LFO filter sweep.

cutoff Cutoff control 0 to 1; defaults to 1/2.

techbass Saw bass with a pluck filter envelope. The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to an LFO sweep. Set the filter cutoff. Higher values give a brighter sound.

decay Decay control 0 to 1; defaults to an LFO sweep. Set the filter envelope decay time. Higher values take longer to decay.

tinbass Flanged bass

cutoff Cutoff control 0 to 1; defaults to 1/2.

House Library

House library: include 'preset/house.limut'
Audio

The House library also includes the 909, 303 and trance libraries.

didgeridoo Didgeridoo drone synth.

donk Donk sound. The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to 1/2. Set the amount of FM metallic modulation. Higher values give a brighter sound.

decay Decay control 0 to 1; defaults to 1/2. Set the decay time. Higher values give a longer, brighter sound.

driftorgan Supersaw organ with glide and drift. The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to 1/2. Set the filter cutoff. Higher values give a brighter sound.

m1organ Simulated M1 Organ sound. The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to 1/2. Set the filter cutoff. Higher values give a brighter sound.

oreese Original style reese bass with detuned saws causing pitch dependent beating. The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to 1/2. Set the filter cutoff. Higher values give a brighter sound.

detune Detune level 0 to 1; defaults to 1/2. Set the detune between the saws. Higher values give a more unstable sound.

reese Growly reese bass with detuned saws. The following custom control params are available:

cutoff Cutoff control 0 to 1; defaults to 1/2. Set the filter cutoff. Higher values give a brighter sound.

detune Detune level 0 to 1; defaults to 1/2. Set the detune between the saws. Higher values give a more unstable sound.


Please report bugs, problems, issues and suggestions on https://github.com/sdclibbery/limut/issues

Latest release notes including breaking changes: https://github.com/sdclibbery/limut/releases/tag/v0.20.0-alpha