Aether Rhai Scripting Guide
Complete reference for scripting in Aether using the Rhai language.
Scripts automate graph construction, sequencer programming, and live
performance. Run scripts from the console (bottom panel), the script
editor (Cmd+E), or headless (aether-headless --script file.rhai).
Quick Start
// Build a simple synth patch
let sink = AudioSink.first();
let synth = Polysynth.add("synth");
let reverb = Reverb.add("reverb");
synth.output("Audio Out").port().connect(reverb.input("Input").port());
reverb.output("Output").port().connect(sink.input("Audio In").port());
synth.waveform("Sawtooth");
reverb.room_size(0.7);
reverb.mix(0.3);
play();
Key Concepts
- Node types are global constants -
Oscillator, Mixer, Filter, etc. Typos fail at parse time.
Type.add("label") creates a node. With a label, it's idempotent - rerunning returns the existing node.
Type.first() returns the first existing node of that type (e.g. AudioSink.first()).
- Typed property methods - each node type has generated methods like
osc.frequency(440.0).
node.set("Property", value) string-based setter also works for all properties.
output.port().connect(input.port()) wires nodes together. Duplicate connections are ignored.
- Typed port properties -
mix.input("Input 0").gain(0.5) for per-port properties.
Type System
// Build a simple synth patch
let sink = AudioSink.first();
let synth = Polysynth.add("synth");
let reverb = Reverb.add("reverb");
synth.output("Audio Out").port().connect(reverb.input("Input").port());
reverb.output("Output").port().connect(sink.input("Audio In").port());
synth.waveform("Sawtooth");
reverb.room_size(0.7);
reverb.mix(0.3);
play();
Key Concepts
- Node types are global constants -
Oscillator,Mixer,Filter, etc. Typos fail at parse time. Type.add("label")creates a node. With a label, it's idempotent - rerunning returns the existing node.Type.first()returns the first existing node of that type (e.g.AudioSink.first()).- Typed property methods - each node type has generated methods like
osc.frequency(440.0). node.set("Property", value)string-based setter also works for all properties.output.port().connect(input.port())wires nodes together. Duplicate connections are ignored.- Typed port properties -
mix.input("Input 0").gain(0.5)for per-port properties.
Type System
Every node type gets its own Rhai type, generated by the graph_node!
proc macro. Each type has per-property setter methods and per-port
property methods.
| Type | Created by | Example |
|---|---|---|
OscillatorScriptHandle | Oscillator.add("osc") | osc.frequency(440.0) |
MixerScriptHandle | Mixer.add("mix") | mix.master_gain(0.5) |
MixerInputPortHandle | mix.input("Input 0") | inp.gain(0.5) |
MixerOutputPortHandle | mix.output("Output") | out.port() for connect |
ScriptPortRef | port.port() | out.port().connect(inp.port()) |
ScriptTrackRef | node.add_track("name") | t.add_note("C4", 0.0, 1.0) |
ScriptRegionRef | track.add_region(0.0, 16.0) | r.set_label("Intro") |
ScriptNoteRef | track.add_note("C4", 0.0, 1.0) | n.set_velocity(100) |
ScriptEnvelope | adsr(), one_shot(), etc. | synth.shape(adsr(0.01, 0.1, 0.7, 0.2)) |
ScriptCurve | curve(), linear_curve(), etc. | set(node, "CurveData", c) |
Methods on All Node Handles
Every node handle (regardless of type) has these shared methods:
show()- print node details to consolehelp()- print properties, ports, and methods for this typeset(key, value)- set property by string nameget(key)- get property by string nameinput(name)- get typed input port handleoutput(name)- get typed output port handledelete()- remove the nodelabel(name)- set display labelselect()- select on canvas
Methods on All Port Handles
Every port handle has:
show()- print port detailshelp()- print port type, per-port properties, and methodsport()- convert toScriptPortReffor connectingdisconnect()- disconnect (input ports only)- Per-port-property methods (e.g.
gain(),pan()on Mixer inputs)
Discovering the API: .help()
.help()Every registered type - macro-generated handles, typed port handles,
and the hand-written types (ScriptCurve, ScriptEnvelope, etc.)
- responds to .help(). In the console it auto-prints; in scripts
pass it to print():
Oscillator.help(); // node type: add/find/first
let osc = Oscillator.add("osc");
osc.help(); // properties, ports, methods
osc.output("Audio Out").help(); // port type + methods
let c = curve();
c.help(); // ScriptCurve builder methods
Style Guide
Use Typed Property Methods
Use Typed Property Methods
Each node type has generated setter methods for its properties.
Method names are snake_case of the property variant name
(e.g. DelayTime → delay_time(), MasterGain → master_gain()).
This is the preferred way to set properties - it's type-safe and
validated per node type.
// Preferred - typed, validated per node type
osc.frequency(440.0);
osc.waveform("Sawtooth");
filter.cutoff(2400.0);
delay.delay_time(0.375);
delay.feedback(0.35);
mix.master_gain(0.5);
// Fallback - string-based, resolves by name
osc.set("Frequency", 440.0);
Label Everything
Always pass a label to .add(). This makes scripts idempotent and
self-documenting.
// Good - labeled, rerunnable
let filter = Biquad.add("main filter");
// Avoid - creates a new node every run
let filter = Biquad.add();
Use show() to Discover Names
show() to Discover NamesWhen unsure of a property or port name:
let osc = Oscillator.add("osc");
osc.show();
// Prints: type, all ports, all properties with current values
// For ArrangerEditor/SequencerEditor: also prints tracks, regions, notes
osc.input("Frequency Mod").show(); // port details
mix.input("Input 0").show(); // port properties (gain, pan)
Name Resolution
String-based set()/get() and port names use fuzzy resolution:
Port Names
- Exact match -
"Audio Out"
- Case-insensitive -
"audio out"
- Underscores as spaces -
"audio_out"
- Substring -
"audio" (matches the first port containing that text)
Property Names (for set()/get())
- Exact property ID -
"MasterGain"
- Case-insensitive property ID -
"mastergain"
- Case-insensitive display name -
"master gain"
Enum Properties
osc.waveform("Sawtooth"); // typed method
osc.set("Waveform", "Sawtooth"); // string-based
osc.set("Waveform", 2); // by index
Function Reference
Arranger Region
"Audio Out""audio out""audio_out""audio" (matches the first port containing that text)Property Names (for set()/get())
- Exact property ID -
"MasterGain" - Case-insensitive property ID -
"mastergain" - Case-insensitive display name -
"master gain"
Enum Properties
osc.waveform("Sawtooth"); // typed method
osc.set("Waveform", "Sawtooth"); // string-based
osc.set("Waveform", 2); // by index
Function Reference
Arranger Region
osc.waveform("Sawtooth"); // typed method
osc.set("Waveform", "Sawtooth"); // string-based
osc.set("Waveform", 2); // by index
Function Reference
Arranger Region
| Function | Description |
|---|---|
extend_back(region, beats, fade_beats?, shape?) | Extend the region's end later by N beats (resizing). Optional fade_beats adds a fade-out. Returns the region for chaining |
extend_front(region, beats, fade_beats?, shape?) | Move the region's start earlier by N beats (resizing). Optional fade_beats adds a fade-in. Returns the region for chaining |
fade_in(region, beats, shape?) | Apply a fade-in over N beats from the region's start (optional shape: linear/ease_in/ease_out/ease_in_out). Returns the region for chaining |
fade_out(region, beats, shape?) | Apply a fade-out over N beats to the region's end (optional shape). Returns the region for chaining |
info(region) | Get region info |
remove(region) | Remove region |
set_color(region, color) | Set region color |
set_curve(region, curve) | Set region amplitude curve shape |
set_duration(region, dur) | Set region duration |
set_label(region, label) | Set region label |
set_level(region, level) | Set region level |
set_start(region, start) | Set region start beat |
Arranger Track
| Function | Description |
|---|---|
add_region(track, start, dur) | Add region to track |
gate_port(track) | Return the output port name for this track's Gate output |
gate_threshold(track, value?) | Get/set per-track gate threshold (0..1). Track Gate output goes high when Level exceeds this value |
level_port(track) | Return the output port name for this track's Level (ControlNorm) output |
regions(track) | List regions in track |
Connections
| Function | Description |
|---|---|
connect(target_port) | port.connect(other_port) |
connected() | port.connected() - returns true if port has any connection |
disconnect() | port.disconnect() |
source() | input_port.source() - returns "Type(#id):PortName" of connected source, or () if disconnected |
targets() | output_port.targets() - returns "[Type(#id):Port, ...]" of connected targets |
Curve
| Function | Description |
|---|---|
add_linear_point(time, value) | Add a keyframe and patch the previous point so the segment is a true straight line |
add_point(time, value) | Add a keyframe with zero tangents (smoothstep S-curve into this point) |
add_smooth_point(time, value, in_tangent, out_tangent) | Add a keyframe with explicit in/out tangents |
bipolar() | Clamp output to -1..1 |
constant_curve(value, duration) | Constant value for duration seconds |
curve() | Create an empty curve - add keyframes with add_point() |
duration() | Total duration of the curve |
ease_in_curve(duration) | Slow start, fast end from 0 to 1 |
ease_in_out_curve(duration) | Slow start, slow end from 0 to 1 |
ease_out_curve(duration) | Fast start, slow end from 0 to 1 |
eval(time) | Evaluate the curve at a given time |
fade_in_out_curve(duration) | Fade in to midpoint then out over duration seconds |
fade_out_curve(duration) | Linear ramp from 1 to 0 over duration seconds |
len() | Number of keyframes |
linear_curve(duration) | Linear ramp from 0 to 1 over duration seconds |
linearize() | Rewrite all tangents so every segment is a straight line |
normalized() | Clamp output to 0..1 |
unbounded() | No output clamping |
Debugging
| Function | Description |
|---|---|
back_trace() | Return the current call stack as an array of maps (fn_name, args, source, line, position) |
Edit
| Function | Description |
|---|---|
clipboard(text) | Copy text to system clipboard |
export_graph() | Export current graph as Rhai script |
graph_clear() | Remove all nodes except AudioSink |
graph_new(name?) | Create a new graph and switch to it |
redo() | Redo last undone action |
undo() | Undo last action |
Envelope
| Function | Description |
|---|---|
adsr(attack, decay, sustain, release) | Classic ADSR: attack, decay, sustain level, release (seconds) |
decay_env(decay) | Instant peak, decay to zero |
gated_hold(attack, release) | Attack to peak, hold until release |
one_shot(attack, decay) | Attack-decay, no sustain hold |
Files
| Function | Description |
|---|---|
include(uri) | Load and execute a script from the project (preprocessor directive) |
Help
| Function | Description |
|---|---|
help(filter) | Search functions by keyword |
help() | List all functions |
Inspection
| Function | Description |
|---|---|
list_files() | List script files |
list_nodes() | List all nodes in graph |
list_types() | List registered node types |
Journal
| Function | Description |
|---|---|
journal(on_off) | Enable/disable command journal |
journal_status() | Print journal status |
replay(file) | Replay a journal file |
Nodes
| Function | Description |
|---|---|
add(label?) | Type.add() or Type.add("label") |
deselect() | Clear selection |
find() | Type.find() - all existing nodes of type |
first() | Type.first() - first existing node of type |
label(node, name) | Set display label |
node_color(r, g, b, a) | Set node title tint color (RGBA, 0.0–1.0) |
position(x, y) | Set node canvas position in pixels |
remove(node) | Delete a node |
select(node) | Select a node on canvas |
show(node) | Print node info |
Properties
| Function | Description |
|---|---|
get(prop) | Get a property value: node.get(prop) |
set(prop, value) | Set a property value: node.set(prop, value) |
Sequencer
| Function | Description |
|---|---|
get_bars(node) | Get sequencer bar count |
get_bpm(node) | Get sequencer BPM |
scroll_to_beat(node, beat) | Scroll sequencer to beat |
scroll_to_note(node, pitch) | Scroll sequencer to pitch |
zoom(node, x, y) | Set sequencer zoom |
Sequencer Note
| Function | Description |
|---|---|
info(note) | Get note info |
move_to(note, pitch, beat) | Move note (pitch + beat) |
remove(note) | Remove note |
set_duration(note, dur) | Set note duration |
set_pitch(note, pitch) | Set note pitch |
set_start(note, beat) | Set note start beat |
set_velocity(note, vel) | Set note velocity |
Sequencer Track
| Function | Description |
|---|---|
add_note(track, pitch, start, dur, velocity) | Add note with explicit velocity |
add_note(track, pitch, start, dur) | Add note to track |
add_notes(track, notes_array) | Add many notes in one call |
notes(track) | List notes in track |
set_channel(track, channel) | Set track MIDI channel |
Shared
| Function | Description |
|---|---|
add_track(node, name) | Add named track (arranger or sequencer), idempotent by name |
bars(node, bars) | Set bar count (arranger or sequencer) |
bpm(node, bpm) | Set BPM (arranger or sequencer) |
can_redo(node) | Check redo available (arranger or sequencer) |
can_undo(node) | Check undo available (arranger or sequencer) |
redo(node) | Redo edit (arranger or sequencer) |
set_loop(node, on_off) | Toggle loop (arranger or sequencer) |
timesig(node, num, den) | Set time signature (arranger or sequencer) |
tracks(node) | List tracks (arranger or sequencer) |
undo(node) | Undo edit (arranger or sequencer) |
Shared Track
| Function | Description |
|---|---|
clear(track) | Clear track (arranger or sequencer) |
enable(track, on_off) | Enable/disable track (arranger or sequencer) |
info(track) | Get track info (arranger or sequencer) |
mute(track, on_off) | Mute track (arranger or sequencer) |
remove(track) | Remove track (arranger or sequencer) |
set_name(track, name) | Set track name (arranger or sequencer) |
solo(track, on_off) | Solo track (arranger or sequencer) |
track_port(track) | Get the output port name for this track (e.g. "Track 0") for connecting to a playback node |
Timing
| Function | Description |
|---|---|
breakpoint(label) | Pause with a label shown in the console (file scripts only) |
breakpoint() | Pause script execution until manually resumed (file scripts only) |
sleep(ms) | Pause script execution (ms) |
Tour
| Function | Description |
|---|---|
tour(on_off) | Start/stop camera tour |
Transport
| Function | Description |
|---|---|
current_beat() | Query current transport beat position |
get_key() | Query musical key. Returns () if no key is set |
get_tempo() | Query global transport BPM |
is_playing() | Query whether transport is playing |
pause() | Pause transport playback |
play() | Start transport playback |
seek(beat) | Seek transport to absolute beat position |
set_key(key) | Set musical key (e.g. "C", "Am", "F#m"). Empty string clears |
set_tempo(bpm) | Set global transport BPM (clamped 1–999) |
set_time_signature(num, denom) | Set global time signature. denom is power of 2 (2=quarter, 3=eighth). Example: set_time_signature(3, 2) for 3/4 |
stop() | Stop transport and reset position |
Values
| Function | Description |
|---|---|
channel(n) | Shorthand for MidiChannel::create(n) |
color(r, g, b, a) | color(r, g, b, a) - RGBA color (0.0–1.0) |
vec2(x, y) | vec2(x, y) - 2D vector |
vec3(x, y, z) | vec3(x, y, z) - 3D vector |
View
| Function | Description |
|---|---|
autolayout(on_off) | Toggle auto-layout |
autozoom(on_off) | Toggle auto-zoom |
toast(message, severity?, position?) | Show a toast. severity: "info" (default), "error". position: "bottom_right" (default), "top", "center" |
toggle(element, on_off) | Toggle a UI element. elements: "grid", "palette", "properties", "menu", "status", "sequencer", "console", "auto_properties", "script_editor", "editor", "3d", "activity", "overlay" |
zoom_to_fit() | Fit graph in view |
zoom_to_node(node) | Center on a node |
Node Reference
Analysis
Chromatic Tuner (ChromaticTuner)
Analysis
Chromatic Tuner (ChromaticTuner)
ChromaticTuner)Real-time pitch detection with note name and cents deviation display
let node = ChromaticTuner.add("my_chromatictuner");
Real-time pitch detection and tuning display. Reads spectral magnitudes from an upstream SpectralAnalyze node and identifies the fundamental frequency via harmonic product spectrum (HPS).
When detection confidence exceeds the Sensitivity threshold, the detected pitch is smoothed over time and output as V/Oct on the Pitch port; the Gate port goes high. When confidence drops below the threshold, the smoothed frequency resets to zero, V/Oct outputs 0.0, and the gate goes low. The optional Tuning input sets divisions-per-octave for note naming (defaults to 12-TET).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Sensitivity | sensitivity() | Float | 0.5 | 0..1 |
| Smoothing | smoothing() | Float | 0.1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Linear spectral magnitudes from an upstream SpectralAnalyze node. Required -- the tuner has no internal FFT. | |
| Tuning | Data(Tuning) | Tuning configuration for note naming. Defaults to 12-TET when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Pitch | VOct | Detected pitch as V/Oct (0.0 = 440 Hz). Outputs 0.0 when no pitch is confidently detected. |
| Gate | Gate | High while a pitch is confidently detected (confidence >= Sensitivity and frequency > 0). Emits edge events only on state transitions. |
Clip Detector (ClipDetector)
ClipDetector)Detects audio clipping with control output
let node = ClipDetector.add("my_clipdetector");
Monitors audio for clipping by comparing per-sample amplitude against a configurable threshold. Passes audio through unchanged.
Both left and right channels are checked; if either exceeds the threshold the clip indicator fires. The Clip Signal output stays at 1.0 for the duration of the hold period after the last clipping sample, then drops to 0.0. Internally tracks total clip events, samples clipped, and peak amplitude for diagnostics. Returns early with no output if Audio In is disconnected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Threshold | threshold() | Float | 1 | 0.1..2 |
| Hold Time | hold_time() | Float | 10 | 0..1000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to monitor for clipping. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Unmodified pass-through of the input audio. |
| Clip Signal | ControlNorm | Control signal: 1.0 while clipping (including hold period), 0.0 otherwise. |
Envelope Follower (EnvelopeFollower)
EnvelopeFollower)Tracks amplitude envelope of audio signal
let node = EnvelopeFollower.add("my_envelopefollower");
Extracts the amplitude envelope from an audio signal, producing a smooth control voltage that tracks the signal's loudness over time.
Stereo input is mono-downmixed and fed sample-by-sample into a peak detector with independent attack/release time constants. The resulting envelope level drives the Envelope Out control signal. A threshold comparator on the envelope generates gate events: the gate goes high on the sample where the envelope first exceeds the threshold and low on the sample where it drops below. Returns early with no output if Audio In is disconnected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Attack Time | attack_time() | Float | 0.005 | 0.001..1 |
| Release Time | release_time() | Float | 0.1 | 0.01..5 |
| Threshold | threshold() | Float | 0.1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio whose amplitude envelope is tracked. Mono-downmixed internally before peak detection. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Envelope Out | ControlNorm | Smoothed amplitude envelope as a normalized control signal. Represents the last sample's envelope value in the current buffer. |
| Gate Out | Gate | Gate high when the envelope exceeds the threshold. Edge events carry sample-accurate offsets within the buffer. |
Oscilloscope (Oscilloscope)
Oscilloscope)Real-time waveform visualization with trigger modes
let node = Oscilloscope.add("my_oscilloscope");
Real-time waveform display for inspecting audio signals. Supports dual-channel comparison and configurable trigger modes.
Each stereo input is mono-downmixed for display. In Auto mode the trigger level is continuously derived from the signal's running min/max midpoint (works for bipolar and rectified signals); in Rising/Falling modes it uses the user-set Trigger Level. FreeRun captures continuously with no trigger gating. Discontinuity mode triggers on sudden sample-to-sample jumps by comparing the current delta against a running average - useful for catching clicks, pops, retrigger boundaries, and envelope attacks. When a trigger fires, samples fill the capture buffer until it reaches the configured time-scale width, then a snapshot is staged for the UI. In Auto and Discontinuity modes, a periodic fallback flush fires every 4096 frames if no trigger occurs. Audio is always passed through on both channels regardless of freeze or trigger state. Freezing pauses capture but not pass-through. Returns early with no output if Channel 1 is disconnected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Time Scale | time_scale() | Float | 10 | 1..100 |
| Trigger Mode | trigger_mode() | Enum | enum(0) | Auto, Rising, Falling, Free Run, Discontinuity |
| Trigger Level | trigger_level() | Float | 0 | -1..1 |
| Freeze | frozen() | Bool | false | |
| Y Scale | y_scale() | Float | 1 | 0.1..20 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Channel 1 | Audio | required; Primary stereo audio signal. Mono-downmixed for the Channel 1 waveform display and passed through to Channel 1 Out. | |
| Channel 2 | Audio | Optional second stereo audio signal for dual-channel comparison. Mono-downmixed for display and passed through to Channel 2 Out. Trigger always fires from Channel 1. | |
| Freeze | Gate | Gate input to freeze the waveform display. While high, capture pauses and the display holds its last frame. Audio pass-through continues regardless. Overrides the Frozen property when connected. | |
| Reset | Gate | Rising edge clears freeze and re-arms the trigger. Pairs with Discontinuity mode's auto-freeze: pulse Reset to hunt for the next artifact. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Channel 1 Out | Audio | Pass-through of Channel 1 audio. |
| Channel 2 Out | Audio | Pass-through of Channel 2 audio. |
Peak Meter (PeakMeter)
PeakMeter)Peak level measurement with hold and decay
let node = PeakMeter.add("my_peakmeter");
Measures peak audio levels with hold and ballistic decay, outputting a control signal for driving level meters and VU displays.
Left and right channels are metered independently through separate PeakMeter DSP instances. The Peak Level control output is the maximum of L/R peaks each buffer. A poll response carries per-channel linear peaks, combined dB level, and a clipping flag (true when either channel >= 1.0). Audio is passed through unchanged. Returns early with no output if Audio In is disconnected. Meters are fully re-initialized on setup() when the sample rate changes.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Hold Time | hold_time() | Float | 1 | 0..5 |
| Decay Time | decay_time() | Float | 2 | 0.1..5 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to measure. Left and right samples are fed to independent peak meters and also copied to Audio Out. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Unmodified pass-through of the input audio. |
| Peak Level | ControlNorm | Peak level as a control signal (max of L/R after hold and decay). Can exceed 1.0 if the input clips. |
Spectrum Analyzer (SpectrumAnalyzer)
SpectrumAnalyzer)FFT-based frequency spectrum visualization with configurable analysis
let node = SpectrumAnalyzer.add("my_spectrumanalyzer");
FFT-based frequency spectrum display. Shows the frequency content of an audio signal in real time with configurable resolution, windowing, and spectral averaging.
Accepts two input paths: a Spectral port and an Audio port. When Spectral is connected, magnitudes arrive pre-computed from an upstream SpectralAnalyze node and the internal FFT is bypassed. When only Audio is connected, stereo input is mono-downmixed and accumulated into an FFT buffer with 50% overlap, then processed on a dedicated analysis thread to keep the audio thread real-time safe. Audio is always passed through unchanged regardless of which analysis path is active. Freezing pauses spectral capture but does not interrupt audio pass-through. The optional Tuning input sets divisions-per-octave for the note overlay gridlines (defaults to 12-TET when disconnected).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| FFT Size | fft_size() | Int | 2 | 0..4 |
| Window | window_type() | Enum | enum(0) | Hann, Hamming, BlackmanHarris, Rectangular |
| Averaging | averaging_mode() | Enum | enum(2) | None, PeakHold, Exponential |
| Averaging Time | averaging_time() | Float | 0.3 | 0.05..5 |
| Min Frequency | min_frequency() | Float | 20 | 20..10000 |
| Max Frequency | max_frequency() | Float | 22050 | 100..22050 |
| Note Overlay | note_overlay() | Bool | false | |
| Freeze | frozen() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Pre-computed spectral magnitudes from an upstream SpectralAnalyze node. When connected, the internal FFT and averaging are bypassed entirely. | |
| Audio In | Audio | required; Stereo audio to analyze. Mono-downmixed and fed to the internal FFT when the Spectral port is disconnected. Also passed through to Audio Out regardless. | |
| Tuning | Data(Tuning) | Tuning configuration for note overlay gridlines. Defaults to 12-TET when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Unmodified pass-through of the input audio. |
Waterfall (Waterfall)
Waterfall)Scrolling spectrogram - frequency × time heatmap
let node = Waterfall.add("my_waterfall");
Scrolling spectrogram that plots frequency content over time as a color-mapped heatmap. Each row is one FFT slice; the display scrolls so the most recent spectrum appears at the top.
Accepts two input paths: a Spectral port and an Audio port. When Spectral is connected, magnitudes arrive pre-computed and the internal FFT is bypassed. When only Audio is connected, stereo input is mono-downmixed and accumulated with 75% overlap for smooth scrolling, then processed on a dedicated analysis thread. Averaging is always disabled (each row is a single un-smoothed FFT frame). Audio is passed through unchanged regardless of which analysis path is active. Freezing pauses spectrogram scrolling but does not interrupt audio pass-through.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| FFT Size | fft_size() | Int | 1 | 0..3 |
| Min Freq | min_frequency() | Float | 20 | 20..10000 |
| Max Freq | max_frequency() | Float | 22050 | 100..22050 |
| Freeze | frozen() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Pre-computed spectral magnitudes from an upstream SpectralAnalyze node. When connected, the internal FFT is bypassed entirely. | |
| Audio In | Audio | required; Stereo audio to analyze. Mono-downmixed and fed to the internal FFT when the Spectral port is disconnected. Also passed through to Audio Out regardless. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Unmodified pass-through of the input audio. |
Effect
Allpass Filter (AllpassFilter)
Allpass Filter (AllpassFilter)
Phase rotation without amplitude change - building block for phasers and reverbs
let node = AllpassFilter.add("my_allpassfilter");
Second-order allpass filter (RBJ biquad). Passes all frequencies at unity gain but rotates phase around the center frequency.
True stereo with independent filter state per channel. Frequency and Q are smoothed over 128 samples to prevent clicks during modulation.
Freq Mod overrides the Frequency property when connected (ControlFreq, value in Hz, clamped to 20-20,000 Hz). The CV fully replaces the knob value.
Chain several allpass filters in series to build a phaser. Place one in a feedback delay loop for dispersive reverb tails.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 1000 | 20..20000 |
| Q | q() | Float | 0.707 | 0.1..20 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio signal to phase-rotate. | |
| Freq Mod | ControlFreq | Overrides the Frequency property. Value is in Hz (ControlFreq), clamped to 20-20,000 Hz. Smoothed - safe for LFO-driven phaser sweeps. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Phase-rotated stereo audio output. |
Amp Envelope (AmpEnvelope)
AmpEnvelope)Amplitude envelope - shapes audio volume with a gate-triggered envelope
let node = AmpEnvelope.add("my_ampenvelope");
Combined envelope generator and VCA. Multiplies an audio input by a gate-triggered envelope curve, replacing the Envelope-into-VCA two-node pattern.
The Depth knob scales the envelope level before it is applied to the audio. DepthMod multiplies with Depth (disconnected defaults to 1.0), so effective depth = Depth * DepthMod. The Env output carries the effective envelope value clamped to 0.0-1.0 for downstream modulation. With no audio input connected, Audio Out produces silence but the envelope still advances.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Shape | shape() | Envelope | [envelope] | |
| Depth | depth() | Float | 1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Audio signal to shape. When disconnected, Audio Out is silent but the envelope still advances. | |
| Gate In | Gate | required; Rising edge triggers attack from curve start; falling edge triggers release from the hold point. | |
| Depth Mod | ControlNorm | Multiplied with the Depth knob. Disconnected defaults to 1.0 (no attenuation). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Input audio multiplied by the depth-scaled envelope, per sample. |
| Env Out | ControlNorm | Effective envelope level (envelope * depth * depth_mod) clamped to 0.0-1.0. |
Automation Curve (AutomationCurve)
AutomationCurve)Transport-synced drawn curve for parameter automation
let node = AutomationCurve.add("my_automationcurve");
Transport-synced modulation source driven by a user-drawn keyframe curve. Evaluates against the beat timeline to produce parameter automation across six output formats.
The curve position is derived from the transport beat, looping over the configured bar length. Position input overrides the transport playhead when connected (0.0 = curve start, 1.0 = curve end). Depth input scales all outputs (disconnected defaults to 1.0). Frequency outputs use log-scale mapping between FreqMin and FreqMax. VOct is derived from the same frequency mapping relative to middle C (261.63 Hz). Gate goes high when the depth-scaled curve value reaches 0.5.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Curve | curve_data() | Curve | [curve] | |
| Length | length_bars() | Int | 4 | 1..64 |
| Loop | loop() | Bool | true | |
| Freq Min | freq_min() | Float | 20 | 1..20000 |
| Freq Max | freq_max() | Float | 20000 | 1..20000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Position | ControlNorm | Overrides the transport playhead. 0.0 = curve start, 1.0 = curve end. Clamped to 0.0-1.0, then scaled to the beat range to re-evaluate the curve. | |
| Depth | ControlNorm | Multiplies all outputs by this value (clamped to 0.0-1.0). Disconnected defaults to 1.0. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Norm | ControlNorm | Depth-scaled curve value (0.0-1.0). |
| Bipolar | ControlBipolar | Depth-scaled curve value remapped via (scaled * 2 - 1) to -1.0 to 1.0. |
| Freq | ControlFreq | Depth-scaled curve value mapped to Hz via log interpolation between FreqMin and FreqMax. |
| Audio | Audio | Depth-scaled curve value as constant mono-to-stereo audio (same value for every sample in the buffer). |
| VOct | VOct | V/Oct derived from the same frequency mapping as Freq, relative to middle C (261.63 Hz). Constant per buffer. |
| Gate | Gate | Edge-detected gate high when the depth-scaled curve value is >= 0.5. |
Biquad Filter (Biquad)
Biquad)Second-order IIR filter with multiple types
let node = Biquad.add("my_biquad");
Second-order IIR filter with seven modes: lowpass, highpass, bandpass, notch, peak, low shelf, and high shelf.
True stereo with independent filter state per channel - no crosstalk. All parameters are smoothed over 128 samples to avoid clicks during modulation.
Cutoff Mod overrides the Frequency property when connected (ControlFreq, value in Hz). Q Mod overrides the Q property (ControlNorm, 0.0-1.0 mapped linearly to Q 0.1-20.0). Both CV inputs fully replace the knob value - there is no additive modulation.
Gain only affects Peak, Low Shelf, and High Shelf modes. For LP/HP/BP/Notch it has no effect.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Type | filter_type() | Enum | enum(0) | Lowpass, Highpass, Bandpass, Notch, Peak, Lowshelf, Highshelf |
| Frequency | frequency() | Float | 1000 | 20..20000 |
| Q | q() | Float | 0.707 | 0.1..20 |
| Gain | gain_db() | Float | 0 | -24..24 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to filter. | |
| Cutoff Mod | ControlFreq | Overrides the Frequency property. Value is in Hz (ControlFreq), clamped to 20 Hz through Nyquist. Smoothed - safe for LFO or envelope-driven filter sweeps. | |
| Q Mod | ControlNorm | Overrides the Q property. 0.0 maps to Q 0.1, 1.0 maps to Q 20.0 (linear scaling). Modulating Q with an envelope creates wah-like effects. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Filtered stereo audio. |
Chorus (Chorus)
Chorus)Multi-voice chorus effect with LFO modulation
let node = Chorus.add("my_chorus");
Multi-voice chorus with LFO-modulated delay lines. Thickens and widens the input by mixing it with detuned, time-varying copies.
Each voice has its own sine LFO with a phase offset evenly distributed across the cycle and a rate that increases by 10% per voice (voice 0 = base rate, voice 1 = 1.1x, etc.). Base delay also staggers by 3 ms per voice. The output is the average of all voices mixed with the dry signal. No feedback path, so the effect stays clean. RateMod CV uses an exponential 0.1..10 Hz mapping; DepthMod maps linearly to 0..20 ms; both replace the property value when connected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Voices | num_voices() | Int | 3 | 1..8 |
| Delay | base_delay() | Float | 25 | 5..100 |
| Depth | depth() | Float | 3 | 0..20 |
| Rate | rate() | Float | 0.5 | 0.1..10 |
| Mix | mix() | Float | 0.4 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio | Stereo audio to chorus. Outputs silence when disconnected. | |
| Rate Mod | ControlNorm | ControlNorm 0..1 mapped exponentially to 0.1..10 Hz, replacing the Rate property value when connected. | |
| Depth Mod | ControlNorm | ControlNorm 0..1 mapped linearly to 0..20 ms depth, replacing the Depth property value when connected. | |
| Mix Mod | ControlNorm | ControlNorm 0..1 mapped directly to wet/dry mix, replacing the property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | Stereo output: dry * (1 - mix) + chorus_average * mix, scaled by OutputGain. |
Comb Filter (CombFilter)
CombFilter)Short delay with feedback for metallic resonances, flanging, and bell tones
let node = CombFilter.add("my_combfilter");
Comb filter with feedback, feedforward, and combined modes. A short tuned delay line that reinforces harmonics of its fundamental frequency, producing metallic resonances, bell tones, and Karplus-Strong-style plucks.
True stereo with independent delay buffers per channel. The delay line uses linear interpolation for fractional sample lengths. Frequency, feedback, and mix are all smoothed to prevent clicks.
Frequency Mod accepts both ControlFreq (direct Hz) and V/Oct inputs. V/Oct is relative to the Frequency property (0 = unison, +1 = octave up). If both types are connected, V/Oct takes priority.
Feedback Mod overrides the Feedback property. ControlNorm 0.0-1.0 is scaled to 0.0-0.99.
Damping places a one-pole lowpass in the feedback path. Higher values darken the decay - simulates string damping for physical modeling. Only affects Feedback and Both modes.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..10000 |
| Feedback | feedback() | Float | 0.7 | 0..0.99 |
| Mix | mix() | Float | 0.5 | 0..1 |
| Mode | mode() | Enum | enum(0) | Feedback, Feedforward, Both |
| Damping | damping() | Float | 0 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio input to the comb filter. | |
| Frequency Mod | ControlFreq, VOct | Overrides comb frequency. Accepts ControlFreq (direct Hz) or V/Oct (relative to Frequency property, +1 = octave up). V/Oct takes priority when both are connected. | |
| Feedback Mod | ControlNorm | Overrides the Feedback property. ControlNorm 0.0-1.0 is scaled to feedback 0.0-0.99. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Filtered stereo audio output. |
Compressor (Compressor)
Compressor)Dynamic range compressor with optional sidechain
let node = Compressor.add("my_compressor");
Dynamic range compressor with stereo-linked detection. Reduces the volume of signals that exceed the threshold, taming peaks and adding punch.
When the Sidechain input is connected, compression is keyed from that signal (converted to mono) instead of the main audio. The Sidechain takes full priority - there is no blend between internal and external detection. Use this for ducking (kick→bass) and pumping effects.
Threshold Mod overrides the Threshold property when connected. The CV is scaled linearly: 0.0 maps to -60 dB, 1.0 maps to 0 dB. The override is smoothed to prevent clicks.
GR Out reports the maximum gain reduction across L/R channels, normalized so 60 dB of reduction reads as 1.0.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Threshold | threshold_db() | Float | -20 | -60..0 |
| Ratio | ratio() | Float | 4 | 1..20 |
| Attack | attack_ms() | Float | 10 | 0.1..100 |
| Release | release_ms() | Float | 100 | 10..1000 |
| Knee | knee_db() | Float | 0 | 0..12 |
| Makeup Gain | makeup_gain_db() | Float | 0 | 0..24 |
| Auto Makeup | auto_makeup() | Bool | false | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Audio signal to compress. | |
| Sidechain | Audio | External sidechain signal. When connected, the compressor detects level from this signal (summed to mono) instead of the main audio input. Typical use: feed a kick drum here to duck a bassline. | |
| Threshold Mod | ControlNorm | Overrides the Threshold property. 0.0 maps to -60 dB, 1.0 maps to 0 dB (linear scaling). Smoothed to prevent clicks. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Compressed audio output. |
| GR Out | ControlNorm | Maximum gain reduction across L/R channels. 0.0 = no reduction, 1.0 = 60 dB of reduction. Useful for driving meters or modulating other parameters. |
DeClicker (DeClicker)
DeClicker)Active click and pop removal via derivative detection and interpolation repair
let node = DeClicker.add("my_declicker");
Active click and pop removal via derivative-based detection and interpolation repair.
Incoming audio is delayed by Lookahead samples through a ring buffer. First- and second-order derivatives are computed per sample; spikes exceeding the Threshold are marked as damaged. If a consecutive damage run exceeds the Sensitivity duration cutoff (Gentle=2, Normal=4, Aggressive=8 samples), it is classified as a musical transient and left untouched. Damaged regions within the cutoff are repaired using Hermite or linear interpolation from the surrounding undamaged context. The Monitor output carries only the removed material (original minus repaired) for audible verification. The Gate output pulses high whenever clicks are detected in a buffer.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Threshold | threshold() | Float | 0.1 | 0.001..1 |
| Sensitivity | sensitivity() | Enum | enum(1) | Gentle, Normal, Aggressive |
| Lookahead | lookahead() | Int | 32 | 8..128 |
| Max Window | max_window() | Int | 8 | 1..32 |
| Mode | repair_mode() | Enum | enum(0) | Hermite, Linear |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio to process. Required; produces silence when disconnected. Each channel has independent detection and repair state. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Repaired stereo audio, delayed by Lookahead samples. Outputs silence during the initial priming period. |
| Monitor | Audio | Difference signal (original minus repaired): only the removed click material. Useful for monitoring what the algorithm is cutting. |
| Gate | Gate | Gate high while clicks are detected in the current buffer, low otherwise. Fires edge events, not per-click pulses. |
Delay (Delay)
Delay)Audio delay with optional time modulation
let node = Delay.add("my_delay");
Stereo delay line with feedback and wet/dry mix. Creates echoes and repeats of the input signal.
Feedback feeds the delayed output back into the delay buffer (input + delayed * feedback), producing decaying repeats. The range is capped at 99% to prevent runaway oscillation, but values above ~90% sustain almost indefinitely. Delay time changes are smoothed over 256 samples to avoid pitch glitches from read-head jumps; connect a slow CV source to the DelayTimeMod port for chorus and vibrato effects. When the DelayTimeMod CV is connected it replaces the property value directly (in seconds), while FeedbackMod and MixMod scale their respective ControlNorm 0..1 ranges into the parameter range.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Delay Time | delay_time() | Float | 0.5 | 0.001..2 |
| Feedback | feedback() | Float | 0 | 0..0.99 |
| Mix | mix() | Float | 1 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to delay. Produces silence when disconnected. | |
| Delay Time Mod | Control | Delay time in seconds (Control, not ControlNorm). When connected, replaces the DelayTime property value directly. | |
| Feedback | ControlNorm | ControlNorm 0..1 scaled to 0..99% feedback, replacing the property value when connected. | |
| Mix | ControlNorm | ControlNorm 0..1 mapped directly to wet/dry mix, replacing the property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Stereo output: dry * (1 - mix) + delayed * mix, scaled by OutputGain. |
Distortion (Distortion)
Distortion)Waveshaping and harmonic distortion with multiple types
let node = Distortion.add("my_distortion");
Waveshaping distortion with seven selectable algorithms. Drive pushes the signal into saturation; tone and mix shape the final character.
The signal is multiplied by Drive before entering the selected waveshaper. Algorithm differences: HardClip clips at +/-1.0 (harsh, buzzy); SoftClip and Tanh both use soft_clip (smooth saturation); Tube uses asymmetric tube simulation; Fuzz doubles the drive into soft_clip for aggressive saturation; Wavefold folds the signal back at +/-1.0 (adds dense upper harmonics); Bitcrush quantizes to the Bits setting (lo-fi, stepped). The Bits property only affects the Bitcrush algorithm. DriveMod CV maps 0..1 to 1..20x drive; MixMod maps 0..1 directly to wet/dry.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Type | distortion_type() | Enum | enum(2) | HardClip, SoftClip, Tanh, Tube, Fuzz, Wavefold, Bitcrush |
| Drive | drive() | Float | 2 | 1..20 |
| Mix | mix() | Float | 1 | 0..1 |
| Tone | tone() | Float | 5000 | 1000..10000 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
| DC Blocking | dc_blocking() | Bool | true | |
| Bits | bits() | Int | 8 | 1..16 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to distort. Required; produces silence when disconnected. | |
| Drive Mod | ControlNorm | ControlNorm 0..1 mapped linearly to 1..20x drive, replacing the property value when connected. | |
| Mix Mod | ControlNorm | ControlNorm 0..1 mapped directly to wet/dry mix, replacing the property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Stereo output: clean * (1 - mix) + distorted * mix, then scaled by OutputGain. Per-channel processing. |
Envelope (Envelope)
Envelope)ADSR envelope generator
let node = Envelope.add("my_envelope");
General-purpose gate-triggered envelope generator. Outputs the envelope level as a stereo audio signal for use as a VCA control or any parameter modulation source.
The envelope shape is drawn via keyframes and tangents, with an optional hold point that sustains the level while the gate is held. Gate rising edge triggers attack from the start of the curve; falling edge triggers release from the hold point onward. The envelope advances per sample for smooth output.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Shape | shape() | Envelope | [envelope] |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate In | Gate | required; Rising edge triggers attack from curve start; falling edge triggers release from the hold point. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Env Out | Audio | Envelope level as mono-to-stereo audio. Multiply with an audio signal for VCA-style amplitude shaping. |
Filter (Filter)
Filter)One-pole lowpass filter with cutoff modulation
let node = Filter.add("my_filter");
Simple one-pole lowpass filter. Smoothly attenuates frequencies above the cutoff with a gentle 6 dB/octave slope.
True stereo with independent filter state per channel. The cutoff is smoothed to prevent clicks during modulation.
Cutoff Mod overrides the Cutoff property when connected (ControlFreq, value in Hz). The CV fully replaces the knob value - there is no additive modulation. When disconnected, the property value is used.
For steeper filtering or resonance control, use the Biquad Filter.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Cutoff | cutoff() | Float | 1000 | 20..20000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to filter. | |
| Cutoff Mod | ControlFreq | Overrides the Cutoff property. Value is in Hz (ControlFreq). Smoothed - safe for LFO-driven sweeps. When disconnected, the Cutoff property value is used. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Lowpass-filtered stereo audio output. |
Flanger (Flanger)
Flanger)Short modulated delay with feedback - classic flanger effect
let node = Flanger.add("my_flanger");
Classic flanger effect. A short delay modulated by a sine LFO and fed back into itself, producing a sweeping comb-filter tone.
The modulated delay time equals base_delay * (1 + LFO * depth), clamped to the 10 ms hardware maximum. Feedback feeds the delayed output back into the delay buffer (input + delayed * feedback). Negative feedback values invert the signal before feeding back, shifting the comb peaks to produce a hollower, more nasal character. No CV modulation inputs; use the property controls. No parameter smoothing, so abrupt changes to delay or rate may click.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Rate | rate() | Float | 0.3 | 0.01..10 |
| Depth | depth() | Float | 0.7 | 0..1 |
| Delay | delay() | Float | 3 | 0.1..10 |
| Feedback | feedback() | Float | 0.5 | -0.95..0.95 |
| Mix | mix() | Float | 0.5 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio to flange. Required; produces silence when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo output: dry * (1 - mix) + delayed * mix, scaled by OutputGain. |
Granular (GranularProcessor)
GranularProcessor)Live granular effects processor with freeze and pitch control
let node = GranularProcessor.add("my_granularprocessor");
Live granular effects processor. Records audio into a circular buffer and spawns overlapping grains that read back from configurable positions with pitch shifting and stereo spread.
Grains are scheduled at the Density rate (Hz). Each grain reads from Position (0 = most recent, 1 = oldest) in the circular buffer, with Jitter randomizing position, size, and pitch per grain. Grains use linear interpolation for fractional read positions and a morphable window envelope (Texture: 0 = rectangular, 0.5 = Hann, 1.0 = narrow Gaussian). The Freeze gate stops buffer recording so grains read from a frozen snapshot; unfreezing resumes recording from where the write head left off. Active grains are averaged (not summed) when more than one is playing. CV inputs are additive offsets to the property values, not replacements. PitchMod is ControlBipolar and scales by the full +/-24 semitone range.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Buffer | buffer_length() | Float | 4 | 1..8 |
| Density | density() | Float | 10 | 0.5..100 |
| Size | size() | Float | 50 | 5..500 |
| Position | position() | Float | 0 | 0..1 |
| Pitch | pitch() | Float | 0 | -24..24 |
| Spread | spread() | Float | 0.3 | 0..1 |
| Jitter | jitter() | Float | 0.1 | 0..1 |
| Max Grains | max_grains() | Int | 16 | 1..64 |
| Texture | texture() | Float | 0.5 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Live stereo audio, continuously recorded into the circular buffer (unless frozen). Required; produces silence when disconnected. | |
| Freeze | Gate | required; Rising edge freezes buffer recording; falling edge resumes. Grains continue reading from the frozen buffer while frozen. | |
| Position Mod | ControlNorm | required; ControlNorm 0..1 added to the Position property value. The sum is clamped to 0..1. | |
| Density Mod | ControlNorm | required; ControlNorm 0..1 scaled by the full density range (0.5..100 Hz) and added to the property value. | |
| Size Mod | ControlNorm | required; ControlNorm 0..1 scaled by the full size range (5..500 ms) and added to the property value. | |
| Pitch Mod | ControlBipolar | required; ControlBipolar -1..1 scaled by the full pitch range (+/-24 semitones) and added to the property value. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Averaged grain output, scaled by OutputGain. No dry signal is mixed in; use an external mixer for dry/wet blending. |
LFO (LFO)
LFO)Low-frequency oscillator for modulation
let node = LFO.add("my_lfo");
Low-frequency oscillator for cyclic modulation of any parameter. Produces synchronized outputs in multiple formats: audio, normalized, bipolar, and gate.
FrequencyMod overrides the Frequency knob entirely when connected (it does not add to it). DepthMod overrides the Depth knob's smoother target when connected. All outputs are scaled by the effective depth. The Gate output goes high on the positive half of the waveform cycle.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 5 | 0.01..20 |
| Depth | depth() | Float | 0.5 | 0..1 |
| Waveform | waveform() | Enum | enum(0) | Sine, Square, Sawtooth, Triangle |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Frequency | ControlFreq | Overrides the Frequency knob with an external signal in Hz. Uses the raw control value directly. | |
| Reset | Gate | Rising edge resets phase to zero for tempo sync or retriggering. | |
| Depth Mod | ControlNorm | Overrides the Depth knob's smoother target. Smoothing still applies to avoid clicks. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Bipolar waveform as stereo audio, scaled by depth. Per-sample generation. |
| Norm | ControlNorm | Last sample of the buffer remapped to 0.0-1.0, scaled by depth. |
| Bipolar | ControlBipolar | Last sample of the buffer as bipolar control, scaled by depth. |
| Gate | Gate | Edge-detected gate high when the depth-scaled waveform is positive. |
Low-Pass Gate (LowPassGate)
LowPassGate)Combined VCA + lowpass with vactrol-style decay - Buchla LPG
let node = LowPassGate.add("my_lowpassgate");
Combined VCA and lowpass filter with vactrol-style decay. The Buchla signature sound: strike it with a gate and it opens briefly, then closes with an organic, woody decay.
A gate rising edge snaps the internal vactrol level to 1.0. It then decays exponentially, with a sqrt curve applied to model the fast-open, slow-close photoresistor response. The CV Level input can also drive the vactrol; it takes the maximum of the current vactrol level and the CV value, so holding CV high keeps the gate open. VCA Amount controls how much the vactrol affects amplitude (0 = unity gain, 1 = full gating). LPF Amount controls cutoff sweep depth; the cutoff tracks the vactrol level on a log scale from 20 Hz to just below Nyquist. With both VCA and LPF at 1.0 the sound starts bright and loud, then dims and quiets together.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Decay | decay() | Float | 200 | 10..2000 |
| Resonance | resonance() | Float | 0 | 0..0.95 |
| VCA | vca_amount() | Float | 0.5 | 0..1 |
| LPF | lpf_amount() | Float | 0.5 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio to pass through the gate. Required; produces silence when disconnected. | |
| Gate | Gate | Gate signal. Each rising edge resets the vactrol level to 1.0, starting a fresh decay. Has no effect while the vactrol is already at 1.0. | |
| CV Level | ControlNorm | ControlNorm 0..1 that holds the vactrol open. The vactrol level becomes max(decaying_level, cv), so holding CV high prevents the gate from closing. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Audio shaped by the combined VCA gain and lowpass filter, both driven by the vactrol level. |
Phaser (Phaser)
Phaser)Multi-stage allpass sweep - classic phaser effect
let node = Phaser.add("my_phaser");
Multi-stage phaser effect. Chains allpass filters whose center frequency sweeps via an internal LFO, carving moving notches through the spectrum.
The LFO sweeps logarithmically between MinFreq and MaxFreq, scaled by Depth. The allpass chain output feeds back into its own input (input + chain_output * feedback) to deepen the notches. More stages produce more notches; the step size is 2 so the count is always even. RateMod CV scales the base rate multiplicatively: effective_rate = rate * (1 + cv * 4), so at CV=1.0 the rate is 5x faster.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Rate | rate() | Float | 0.5 | 0.01..10 |
| Depth | depth() | Float | 0.8 | 0..1 |
| Feedback | feedback() | Float | 0.5 | 0..0.95 |
| Stages | stages() | Int | 4 | 2..12 |
| Mix | mix() | Float | 0.5 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio to phase. Required; produces silence when disconnected. | |
| Rate Mod | ControlNorm | ControlNorm 0..1 that multiplies the base rate: effective = rate * (1 + cv * 4). At cv=0 uses the base rate; at cv=1 the rate is 5x faster. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo output: dry * (1 - mix) + allpass_chain * mix, scaled by OutputGain. |
Pitch Envelope (PitchEnvelope)
PitchEnvelope)Gate-triggered frequency sweep for percussive pitch control
let node = PitchEnvelope.add("my_pitchenvelope");
Gate-triggered frequency sweep for percussive pitch effects. Outputs ControlFreq directly, eliminating the need for an Envelope into a frequency mapper chain.
The drawn curve maps 0.0 to StartFreq and 1.0 to EndFreq using log-space interpolation for perceptually even pitch movement. The default curve fires a fast attack to EndFreq then decays back to StartFreq with no hold point (fire-and-forget). The envelope is sampled once per buffer, not per sample.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Start Freq | start_freq() | Float | 55 | 20..20000 |
| End Freq | end_freq() | Float | 300 | 20..20000 |
| Shape | shape() | Envelope | [envelope] |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate In | Gate | required; Rising edge fires the pitch sweep from curve start. Falling edge triggers release if a hold point is set. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Freq Out | ControlFreq | Frequency in Hz, log-interpolated between StartFreq and EndFreq based on the envelope curve value. |
Reverb (Reverb)
Reverb)Room simulation reverb with Schroeder algorithm
let node = Reverb.add("my_reverb");
Schroeder reverb with true stereo output. Simulates room reflections from tight rooms to vast halls.
The input is downmixed to mono before entering the reverb engine. Parallel damped comb filters produce late reflections, then series allpass filters add diffusion. Left and right channels use decorrelated delay lines for stereo imaging, controlled by the Width parameter. Pre-delay inserts a fixed gap between the dry sound and the onset of the reverb tail; changing pre-delay or room size rebuilds the internal filter network (may click during live adjustment). CV inputs for room size, damping, and mix all replace the property value directly via smoothers.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Room Size | room_size() | Float | 0.5 | 0..1 |
| Damping | damping() | Float | 0.5 | 0..1 |
| Pre-Delay | pre_delay_ms() | Float | 0 | 0..100 |
| Diffusion | diffusion() | Float | 0.5 | 0..1 |
| Width | width() | Float | 1 | 0..1 |
| Mix | mix() | Float | 0.3 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio | Stereo audio input. Both channels are summed to mono before entering the reverb engine. Outputs silence when disconnected (the reverb tail still decays naturally). | |
| Mix Mod | ControlNorm | ControlNorm 0..1 mapped directly to wet/dry mix, replacing the property value when connected. | |
| Room Size Mod | ControlNorm | ControlNorm 0..1 mapped directly to room size, replacing the property value when connected. Smoothed, does not trigger filter rebuild. | |
| Damping Mod | ControlNorm | ControlNorm 0..1 mapped directly to damping, replacing the property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | Stereo reverb output with decorrelated L/R channels, mixed with the dry input per the Mix setting. |
Ring Modulator (RingModulator)
RingModulator)Carrier × modulator ring modulation with dry/wet mix
let node = RingModulator.add("my_ringmodulator");
Ring modulator -- multiplies a carrier and modulator signal together, producing sum and difference frequencies for inharmonic, bell-like, or robotic timbres.
Both Carrier and Modulator audio inputs are required; disconnecting either silences the output. The wet signal is carrier * modulator (per-channel); the Mix control crossfades between the dry carrier and the ring-modulated result. Unlike SignalMath's Multiply mode, this node provides explicit carrier/modulator labeling and a dedicated dry/wet mix.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mix | mix() | Float | 1 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Carrier | Audio | required; Primary audio signal. Used as the "dry" signal for the Mix crossfade. Required; silences output when disconnected. | |
| Modulator | Audio | required; Audio signal multiplied with the carrier per-channel. Required; silences output when disconnected. | |
| Mix Mod | ControlNorm | ControlNorm 0..1 mapped directly to wet/dry mix, replacing the property value when connected. Clamped to 0..1. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo output: carrier * (1 - mix) + (carrier * modulator) * mix, scaled by OutputGain. |
Sample Rate Reducer (SampleRateReducer)
SampleRateReducer)Lo-fi aliasing effect - reduces effective sample rate
let node = SampleRateReducer.add("my_sampleratereducer");
Lo-fi aliasing effect that reduces the effective sample rate by holding each input sample for multiple output cycles.
Uses a phase accumulator: the input is sampled only when the phase wraps past 1.0, then that value is held until the next wrap. This creates staircase artifacts and harsh aliasing distinct from bitcrushing. The RateMod CV scales linearly from 100 Hz (at cv=0) to the configured target rate (at cv=1), replacing the property value when connected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Rate | rate() | Float | 8000 | 100..44100 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio to decimate. Required; produces silence when disconnected. | |
| Rate Mod | ControlNorm | ControlNorm 0..1 mapped linearly from 100 Hz (at 0) to the configured target rate (at 1). Replaces the property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo output with sample-and-hold decimation applied. |
Transient Shaper (TransientShaper)
TransientShaper)Independent attack and sustain gain reshaping
let node = TransientShaper.add("my_transientshaper");
Reshapes the attack and sustain portions of a signal independently. Boost the attack to add punch, or pull it back to soften transients.
Uses dual envelope followers per channel: a fast follower (~1 ms attack) that catches transients, and a slow follower (4x the Speed setting) that tracks the sustain level. The difference (fast - slow) isolates the transient component. Each sample's gain is a weighted blend of the Attack and Sustain gains based on the transient ratio. Both gain controls are in dB and converted to linear internally. No CV modulation inputs. Speed affects both the fast follower's release and the slow follower's time constant, so increasing it widens the transient detection window.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Attack | attack() | Float | 0 | -12..12 |
| Sustain | sustain() | Float | 0 | -12..12 |
| Speed | speed() | Float | 20 | 5..100 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio to reshape. Required; produces silence when disconnected. Each channel is processed independently with its own envelope followers. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo audio with reshaped transient and sustain gain, scaled by OutputGain. |
Vocoder (Vocoder)
Vocoder)Channel vocoder - spectral envelope transfer from modulator to carrier
let node = Vocoder.add("my_vocoder");
Channel vocoder that transfers the spectral envelope of a modulator onto a carrier signal, producing the iconic robot-voice effect.
The modulator (voice, drums) is split into frequency bands; an envelope follower on each band extracts how loud that frequency region is. Those envelopes are applied to the same bands of the carrier (synth, noise), so the carrier "speaks" with the modulator's tonal shape.
Both inputs are required. Without a modulator the output is silence because the envelope followers produce zero. A carrier alone produces nothing - the modulator's envelope gates it.
Sibilance detection measures the energy ratio of the top quarter of bands to the total. When high-frequency energy dominates, unfiltered modulator audio is blended into the output, preserving consonant sounds like "s", "t", and "sh" that the band filters would otherwise smear.
The Freq Tilt curve applies per-band gain using a log-frequency axis (20 Hz to 20 kHz). Each band's center frequency is mapped onto this curve. A rising curve brightens the output; a falling curve warms it.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Bands | bands() | Int | 16 | 4..32 |
| Low Freq | low_freq() | Float | 80 | 20..500 |
| High Freq | high_freq() | Float | 8000 | 2000..16000 |
| Attack | attack() | Float | 5 | 0.1..50 |
| Release | release() | Float | 20 | 1..200 |
| Q | q() | Float | 5 | 1..20 |
| Noise Gate | noise_gate() | Float | 0 | 0..1 |
| Sibilance | sibilance() | Float | 0 | 0..1 |
| Freq Tilt | tilt() | Curve | [curve] | |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Carrier | Audio | required; Harmonically rich signal (sawtooth, noise, chord) whose spectral content is shaped by the modulator. This is what you hear. | |
| Modulator | Audio | required; Signal whose spectral envelope controls the carrier (voice, drums). Without a modulator the output is silence. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Carrier audio shaped by the modulator's spectral envelope, scaled by Freq Tilt and Output Gain. |
fBm Noise (FbmNoise)
FbmNoise)Fractal Brownian Motion noise - organic modulation source
let node = FbmNoise.add("my_fbmnoise");
Fractal Brownian Motion noise source for organic, evolving modulation. Sums multiple octaves of smooth value noise to produce natural-feeling movement.
Audio and control outputs are the raw fBm value scaled by Amplitude. RateMod maps 0.0-1.0 to a 1x-4x rate multiplier (exponential via 2^(mod*2)); disconnected defaults to 1x. Gate rising edge jumps to a new region of the noise field (each retrigger lands at a different position). The Gate output goes high on the positive half of the noise signal.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Rate | rate() | Float | 1 | 0.01..100 |
| Octaves | octaves() | Int | 4 | 1..8 |
| Persistence | persistence() | Float | 0.5 | 0..1 |
| Lacunarity | lacunarity() | Float | 2 | 1.5..3 |
| Amplitude | amplitude() | Float | 1 | 0..1 |
| Seed | seed() | Int | 42 | 0..9999 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate | Gate | Rising edge jumps to a new region of the noise field. Each retrigger lands at a different position, so rapid retriggering does not freeze the output. | |
| Rate Mod | ControlNorm | Multiplies the Rate knob via 2^(mod*2). At 0.0 = 1x, at 1.0 = 4x. Disconnected defaults to 1x. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Noise as mono-to-stereo audio, bipolar and scaled by Amplitude. Per-sample generation. |
| Norm | ControlNorm | Last sample of the buffer remapped from bipolar to 0.0-1.0. |
| Bipolar | ControlBipolar | Last sample of the buffer clamped to -1.0 to 1.0. |
| Control | Control | Raw noise value without clamping, for custom downstream scaling. |
| Gate | Gate | Edge-detected gate high when the amplitude-scaled noise value is positive. |
Interface
Bipolar Knob (BipolarKnob)
Bipolar Knob (BipolarKnob)
Constant ControlBipolar (-1..+1) source - dial a value and patch it
let node = BipolarKnob.add("my_bipolarknob");
Bipolar knob that outputs a constant value in the -1..+1 range.
The value is set via the on-node slider, a property change, or a ControlKnobCommand from the UI. The output is written every process() call as a ControlBipolar. A state snapshot is sent back to the UI each frame for the custom renderer.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Value | value() | Float | 0 | -1..1 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | ControlBipolar | Constant bipolar control signal (-1.0–+1.0). |
Control Knob (ControlKnob)
ControlKnob)Constant ControlNorm (0..1) source - dial a value and patch it
let node = ControlKnob.add("my_controlknob");
Unipolar knob that outputs a constant value in the 0..1 range.
The value is set via the on-node slider, a property change, or a ControlKnobCommand from the UI. The output is written every process() call as a ControlNorm. A state snapshot is sent back to the UI each frame for the custom renderer.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Value | value() | Float | 0.5 | 0..1 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | ControlNorm | Constant unipolar control signal (0.0–1.0). |
Control Panel (ControlPanel)
ControlPanel)Configurable bank of labeled control and gate outputs with per-channel type selection
let node = ControlPanel.add("my_controlpanel");
Configurable bank of labeled control and gate outputs. Each channel independently selects its type via an inline dropdown on the node face.
Uses custom topology -- output port count and types are dynamic. Control-type channels produce one ControlNorm, ControlBipolar, or Control port. Gate-type channels produce a Gate + Velocity (ControlNorm) port pair. Changing a channel's kind or adjusting the channel count triggers a port remap that the editor uses to rewire existing connections. Gate edge events are emitted only on state transitions (toggled on/off via SetToggle or pad press/release via SetGate). Always processes so initial state reaches the UI even without downstream connections.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Channels | channels() | Int | 4 | 1..16 |
Control Source (ControlSource)
ControlSource)Constant Control (unbounded) source - for tempo, delay time, or any raw value
let node = ControlSource.add("my_controlsource");
Unbounded control source that outputs a raw floating-point value.
Unlike the normalized knobs, this carries real-world units (BPM, milliseconds, semitones, etc.). The value is not clamped on set; ControlKnobCommand writes the raw float directly. The output is written every process() call as a Control value. Min and max config fields exist for UI display hints but do not constrain the output.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Value | value() | Float | 0 | -10000..10000 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Control | Unbounded control signal carrying the raw value. |
Gate Button (GateButton)
GateButton)UI-controlled gate source with momentary and toggle modes. Outputs velocity on a separate control port.
let node = GateButton.add("my_gatebutton");
Manual gate source with momentary and toggle modes. Click or hold the button to fire a gate signal; velocity is output on a separate control port.
Two input paths: UI commands (SetGate) set the gate directly with velocity 1.0, while MIDI-style property writes (Gate property) follow a toggle state machine. On a rising edge, if the value >= ToggleThreshold the gate latches on; below threshold it acts as momentary (gate follows press/release). A second press while latched marks the gate for unlatch on release. The Velocity output holds the press value while the gate is high and resets to 0.0 on gate-off. Gate edge events are emitted only on state transitions.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Gate | gate() | Float | 0 | 0..1 |
| Toggle Vel | toggle_threshold() | Float | 0.5 | 0..1 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate Out | Gate | Gate on/off edge events. High while the button is held (momentary) or latched (toggle). Events are emitted only on state transitions, not every frame. |
| Velocity | ControlNorm | Velocity of the press that opened the gate, held at the trigger value while high. Resets to 0.0 on gate-off. UI commands (SetGate) always produce velocity 1.0. |
Keyboard (Keyboard)
Keyboard)Interactive MIDI keyboard with monitoring and visualization
let node = Keyboard.add("my_keyboard");
Interactive 88-key MIDI keyboard that generates and monitors MIDI note events with optional visual effects.
Operates in three modes depending on connections: generator (no MIDI In -- UI clicks produce notes), monitor (MIDI In connected -- input events are visualized and passed through), or hybrid (both). In Hold mode, clicking a key latches it on; clicking again toggles it off. Latched keys persist across save/load via the held_notes config. When Hold is active and MIDI In is connected, incoming note-offs for UI-latched keys are suppressed in both the visualization and the MIDI output. Disabling Hold clears all latched notes immediately. Visual effects (particles, fire, etc.) are selected per-node and reported to the UI each frame alongside active key state.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Visual Effect | visual_effect() | Enum | enum(3) | None, Firework, Bottle Rocket, Starburst, Note Spark, Fire, Laser, Laser Beam, Piano Beam |
| Hold | hold() | Bool | false | |
| Note Labels | show_note_labels() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | Incoming MIDI to monitor. Notes are visualized on the keyboard and merged into the output alongside UI-generated events. When Hold is active, note-offs for UI-latched keys are filtered out. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Merged MIDI output: pass-through of input events (with Hold filtering applied) plus UI-generated note on/off events. |
Patch Input (PatchInput)
PatchInput)Source node inside a patch - data injected by PatchInstance
let node = PatchInput.add("my_patchinput");
Entry point for signals coming into a patch. Lives inside the patch's inner graph and exposes one output per port defined on the patch interface.
Has no audio-thread inputs of its own. Before each inner-graph pull, PatchInstanceNode calls inject() to stage port data from the parent graph. process() then copies the staged data to outputs. Uses custom topology -- output count and types are derived from the ports config, which can be mutated at runtime via PatchInputCommand (add, remove, rename, reorder ports).
Patch Instance (PatchInstance)
PatchInstance)Instantiated patch - contains a nested subgraph
let node = PatchInstance.add("my_patchinstance");
A single node in the parent graph that encapsulates an entire subgraph, making the patch behave like one composite node.
Each process() call runs a four-phase cycle: (1) collect parent input port data and inject it into the inner PatchInputNode, (2) advance the inner engine's time via process_subgraph, (3) pull the inner PatchOutputNode to drive inner-graph processing, (4) extract captured output data and copy it to this node's outputs. Uses custom topology -- input and output port counts and types mirror the PatchInterface definition. The inner engine's default AudioSink is removed on setup since PatchOutput serves as the pull target. If the inner engine, PatchInput ID, or PatchOutput ID is missing, process() returns immediately with no output.
Patch Output (PatchOutput)
PatchOutput)Sink node inside a patch - data extracted by PatchInstance
let node = PatchOutput.add("my_patchoutput");
Exit point for signals leaving a patch. Lives inside the patch's inner graph and exposes one input per port defined on the patch interface.
process() captures all input port data into a staging buffer. After the inner-graph pull, PatchInstanceNode calls extract() to retrieve the captured data and copy it to the parent graph's outputs. The Sink output port serves as the pull target for the inner graph engine (same pattern as AudioSink). Uses custom topology -- input count and types are derived from the ports config, mutated at runtime via PatchOutputCommand.
Outputs:
| Port | Type | Notes |
|---|---|---|
| Sink | Sink | Pull target for the inner graph engine. The inner engine pulls this node to drive processing of all upstream nodes in the patch. |
MIDI
ABC File Loader (AbcFileLoader)
ABC File Loader (AbcFileLoader)
Loads ABC notation files and converts to sequencer configuration. Supports file paths or raw ABC text.
let node = AbcFileLoader.add("my_abcfileloader");
Loads ABC notation and produces a MidiSequencer on its output for downstream playback.
Accepts an AbcSource (file path or raw text), a ConstantValue file path, or a full AbcFileLoaderConfig via the Config input. When no Config input is connected, falls back to the embedded source property. Parsing only runs when the config changes (detected via a string key). The Sequencer Out streams the cached MidiSequencer every frame. Timing Out carries BPM and time signature extracted from the ABC notation.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Config In | Data(Unknown) | Accepts AbcSource, ConstantValue::FilePath, or full AbcFileLoaderConfig. Overrides the embedded source property when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Sequencer Out | Data(Sequence) | Cached MidiSequencer data, streamed every frame. Connect to a Sequencer or Sequencer Editor. |
| Timing | Data(Timing) | BPM and time signature extracted from the ABC notation. Defaults to 4/4 if not specified in the source. |
Arpeggiator (Arpeggiator)
Arpeggiator)Converts held MIDI notes into arpeggiated patterns
let node = Arpeggiator.add("my_arpeggiator");
Turns held MIDI chords into rhythmic arpeggiated patterns using an internal sample-accurate clock.
Feed it notes and it cycles through them at a configurable rate with selectable direction (up, down, up-down, random), octave range, gate length, and swing. Uses NoteOn velocity=0 as NoteOff. The internal clock derives step timing from the Rate property; swing delays every other step by a fraction of the step duration. Gate length determines when each note-off fires within the step. The Reset input resets the arpeggiator to the first note and kills any currently sounding note. With Latch enabled, notes remain in the pattern even after key release -- new notes replace the latched set. All output MIDI is on channel 1.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Rate | rate() | Float | 8 | 0.1..20 |
| Pattern | pattern() | Enum | enum(0) | Up, Down, Up-Down, Random, As Played, Chord |
| Octaves | octaves() | Int | 1 | 1..4 |
| Gate Length | gate_length() | Float | 0.75 | 0.1..0.95 |
| Swing | swing() | Float | 0 | 0..0.75 |
| Latch | latch() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Incoming MIDI notes to arpeggiate. | |
| Reset | Gate | Rising edge resets the pattern to the first note, zeroes the accumulator, and kills any sounding note. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Arpeggiated MIDI note-on/note-off events on channel 1. Velocity is the most recent input velocity. |
| Gate Out | Gate | High for the gate-length fraction of each step, synchronized with MIDI note-on/off. |
Arranger (Arranger)
Arranger)Song arrangement with per-track ControlNorm outputs for enabling/controlling downstream nodes
let node = Arranger.add("my_arranger");
Song-level arrangement player that outputs per-track level controls synchronized to the graph transport.
Receives an Arrangement via Config In and advances a tick-based playhead. Each arrangement track maps to an unbounded ControlNorm output: the value is the region's level (0.0--1.0) when the playhead is inside a region, otherwise 0.0. Muted, disabled, or non-soloed (when any track is soloed) tracks output 0.0. The Timing input overrides BPM; tempo changes reset the sample accumulator. Enabled gate (disconnected = enabled) forces all outputs to 0.0 when low. Transport seek events resync the tick position. Loop wraps to the start when the arrangement ends. The End gate pulses on loop or completion. Hot-swapping the Config input preserves playhead position and state. Tracks property sets a floor on output count; it also grows to match the arrangement's track count.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Tracks | tracks() | Int | 4 | 1..64 |
| Loop | loop() | Bool | true | |
| Content BPM | content_bpm() | Float | 0 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Restart | Gate | Rising edge restarts the arrangement from tick 0. Processed even when disabled. | |
| Timing | Data(Timing) | Overrides BPM and time signature from the transport. Tempo changes reset the sample accumulator. | |
| Config In | Data(Arrangement) | Arrangement data from an Arranger Editor. Hot-swapped each frame: tracks and timing update but playhead is preserved. | |
| Enabled | Gate | Gates playback. Disconnected = enabled. When low, all per-track outputs are forced to 0.0 and tick advancement stops. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Tempo | Control | Effective BPM after Timing port override or transport fallback. Output even when disabled. |
| Bar | Control | Current bar number (0-based) as a raw control value. Derived from current_beat / beats_per_bar. |
| End | Gate | Single-sample gate pulse on arrangement end or loop wrap. |
| Track | ControlNorm | unbounded; Region level (0.0--1.0) when the playhead is inside a region, 0.0 otherwise. Muted/disabled/non-soloed tracks output 0.0. |
Arranger Editor (ArrangerEditor)
ArrangerEditor)Visual arrangement editor. Click to open arranger panel.
let node = ArrangerEditor.add("my_arrangereditor");
Visual editor for song-level arrangements.
Holds the authoritative Arrangement data (tracks, regions, tempo, time signature) and outputs it via Data port to an Arranger node each frame. The UI sends incremental ArrangerCommand edits (add/remove/move regions, track management, BPM, time signature, loop) through the message bus. In-flight regions (being dragged) are tracked but do not block region output. The Update gate pulses whenever an edit is applied. Timing output carries BPM and time signature. The Import input accepts an external Arrangement for future import support.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Tempo | tempo() | Float | 120 | 20..300 |
| Length | length_bars() | Int | 16 | 1..256 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Import | Data(Arrangement) | External Arrangement data for future import support. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Arrangement | Data(Arrangement) | Full Arrangement data, output every frame. Connect to an Arranger node's Config In. |
| Update | Gate | Single-sample gate pulse after each ArrangerCommand edit is applied. |
| Timing | Data(Timing) | BPM and time signature from the editor's properties. Connect to an Arranger's Timing input. |
Chord Generator (ChordGenerator)
ChordGenerator)Generates chords from single MIDI notes
let node = ChordGenerator.add("my_chordgenerator");
Harmonizer that turns single MIDI notes into full chords.
Each incoming note-on becomes the root of a chord built from the selected type (major, minor, seventh, etc.) with configurable voicing and inversions. On note-off, all chord tones for that root are released. Non-note MIDI messages pass through unchanged. Active chords are tracked per root note, so multiple simultaneous roots produce independent chords. Notes are clamped to 0--127 after transposition; inversions raise lower tones by an octave. Open voicing raises inner tones +12; Drop-2 lowers the second-from-top tone by 12. Third inversion only has an effect on seventh chords or larger.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Chord Type | chord_type() | Enum | enum(0) | Major, Minor, Diminished, Augmented, Sus2, Sus4, Major7, Minor7, Dominant7, Diminished7, HalfDiminished7, MinorMajor7, Augmented7, Major6, Minor6, Major9, Minor9, Dominant9 |
| Voicing | voicing() | Int | 0 | 0..2 |
| Inversion | inversion() | Int | 0 | 0..3 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Single-note MIDI input - each note becomes the chord root. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Multiple simultaneous note-ons per input note. Non-note messages pass through unchanged. |
Chord Progression (ChordProgressionGenerator)
ChordProgressionGenerator)Per-layer curve-driven chord progression generator with polymetric support
let node = ChordProgressionGenerator.add("my_chordprogressiongenerator");
Generates chord progressions with per-layer curves for tension, velocity, and style.
Each layer defines its own bar count and chords-per-bar, enabling polymetric progressions. Generation blends user-drawn curves with fBm noise (controlled by the Mix knob) to select chord degree, velocity, and voicing style per chord slot. Generation runs on a background thread -- property changes queue a request (when auto-regen is enabled) and only the latest request is processed. Connect Sequencer Out to a Sequencer Editor to import and edit the result. The Loaded gate pulses once after each completed generation triggered by the Regenerate command. The Timing input overrides BPM and time signature from the transport.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Key | key() | Int | 60 | 0..127 |
| Scale | scale() | Enum | enum(1) | Chromatic, Major, Minor, HarmonicMinor, MelodicMinor, Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian, MajorPentatonic, MinorPentatonic, Blues, WholeTone, Diminished |
| Extensions | chord_extensions() | Enum | enum(0) | Triads, Sevenths, Extended |
| Mix | mix() | Float | 0 | 0..1 |
| Complexity | complexity() | Int | 4 | 1..8 |
| Persistence | persistence() | Float | 0.5 | 0..1 |
| Rate | rate() | Float | 1 | 0.1..10 |
| Seed | seed() | Int | 0 | 0..9999 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Timing | Data(Timing) | Overrides BPM and time signature from the transport. When disconnected, values come from the graph transport. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Sequencer Out | Data(Sequence) | Generated MidiSequencer data, streamed every frame. Connect to a Sequencer Editor's Import port to edit. |
| Loaded | Gate | Single-frame gate pulse emitted when a Regenerate command completes (not on auto-regen property changes). |
Generative Melody (GenerativeMelody)
GenerativeMelody)Marbles-style clocked random note/gate generator with scale quantization
let node = GenerativeMelody.add("my_generativemelody");
Clocked random note and gate generator with scale quantization, inspired by Mutable Instruments Marbles.
Each rising edge on the Clock input generates a new pitch from a bell-curve distribution shaped by Bias (center) and Spread (width), quantized to the selected scale. Gate density controls the probability that a clock step produces a note; gate length sets the duration as a fraction of the measured clock period. Jitter adds random timing offset to gate events. The Lock parameter controls sequence recall: at 0.0 every step is fully random; at 1.0 the cached sequence replays deterministically. Intermediate values probabilistically replace cached steps. The cache size equals the Steps property. Bias, Spread, and Density can be modulated via CV inputs that override the corresponding properties. VOct output is constant across the buffer (updated on each clock step). The Tuning port overrides 12-EDO divisions and applies a V/Oct offset.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Bias | bias() | Float | 0.5 | 0..1 |
| Spread | spread() | Float | 0.5 | 0..1 |
| Steps | steps() | Int | 8 | 2..32 |
| Scale | scale() | Enum | enum(1) | Chromatic, Major, Minor, HarmonicMinor, MelodicMinor, Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian, MajorPentatonic, MinorPentatonic, Blues, WholeTone, Diminished |
| Root | root() | Int | 60 | 0..127 |
| Octave Range | octave_range() | Int | 2 | 1..5 |
| Lock | lock() | Float | 0 | 0..1 |
| Gate Density | gate_density() | Float | 0.5 | 0..1 |
| Gate Length | gate_length() | Float | 0.5 | 0.1..1 |
| Jitter | jitter() | Float | 0 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Clock | Gate | required; Each rising edge advances the step index and generates a new pitch/gate/velocity decision. Clock period is measured between edges. | |
| Bias CV | ControlNorm | required; Overrides the Bias property when connected. Clamped to 0.0--1.0. | |
| Spread CV | ControlNorm | required; Overrides the Spread property when connected. Clamped to 0.0--1.0. | |
| Density CV | ControlNorm | required; Overrides the Gate Density property when connected. Clamped to 0.0--1.0. | |
| Tuning | Data(Tuning) | Overrides the default 12-EDO divisions-per-octave and applies a V/Oct offset to the pitch output. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| VOct | VOct | Scale-quantized V/Oct pitch, constant across the buffer. Updated on each clock step that produces a gate. |
| Gate | Gate | High for the gate-length fraction of the clock period. Previous gate is closed before a new one opens. |
| Velocity | ControlNorm | Random velocity shaped by Bias (higher bias shifts the distribution upward). Updated per clock step. |
Lookahead Sequencer (LookAheadSequencer)
LookAheadSequencer)MIDI sequencer with lookahead for visualization and practice features
let node = LookAheadSequencer.add("my_lookaheadsequencer");
MIDI sequencer with a lookahead window for score visualization and practice features.
Plays back a MidiSequencer with sample-accurate timing while also computing upcoming notes (1--16 bars ahead) that the UI renders as a grand staff or scrolling notation. The Timing input overrides BPM from the transport. An optional count-in metronome plays before the sequence starts. The lookahead buffer is sent to the UI via poll_response each process cycle. Config In accepts a MidiSequencer from a file loader or sequencer editor; when first loaded the sequence auto-plays. The Restart gate restarts from the beginning; End of Sequence pulses when the sequence finishes.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Key Signature | key_signature() | Enum | enum(0) | CMajor, GMajor, DMajor, AMajor, EMajor, BMajor, FSharpMajor, CSharpMajor, FMajor, BFlatMajor, EFlatMajor, AFlatMajor, DFlatMajor, GFlatMajor, CFlatMajor |
| Look-Ahead Bars | look_ahead_bars() | Int | 4 | 1..16 |
| Count-In Bars | count_in_bars() | Int | 0 | 0..16 |
| Staff Offset | staff_offset() | Vec2 | (0, -140) | |
| Staff Scale | staff_scale() | Vec2 | (400, 120) |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Restart | Gate | Rising edge restarts the sequence from tick 0. | |
| Timing | Data(Timing) | Overrides BPM and time signature from the transport. When disconnected, values come from the graph transport. | |
| Config In | Data(Sequence) | MidiSequencer data from a file loader or sequencer editor. Auto-plays on first load. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Sample-accurate MIDI events from the current playback position. |
| Look-Ahead Out | Data(Sequence) | Lookahead buffer for grand staff / notation visualization. Also sent via poll_response for the UI. |
| End of Sequence | Gate | Gate pulse emitted when the sequence reaches its end. |
MIDI Channel Splitter (MidiChannelSplitter)
MidiChannelSplitter)Routes MIDI events by channel to separate outputs. Each output receives events from one MIDI channel.
let node = MidiChannelSplitter.add("my_midichannelsplitter");
Demultiplexes a multi-channel MIDI stream into separate per-channel outputs.
Each event is routed to the output matching its MIDI channel (channel 0 to output 0, channel 1 to output 1, etc.). Events on channels beyond the configured output count are silently dropped. The number of outputs is controlled by the Channels property and capped at 16.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Channels | channels() | Int | 16 | 1..16 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Combined multi-channel MIDI stream to split. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Ch | Midi | unbounded; Unbounded per-channel MIDI output. Output index matches the MIDI channel number (wire format). |
MIDI File Loader (MidiFileLoader)
MidiFileLoader)Loads Standard MIDI Files and converts to sequencer configuration. Supports file paths or raw bytes with optional tempo override.
let node = MidiFileLoader.add("my_midifileloader");
Loads a Standard MIDI File (.mid) and produces a MidiSequencer on its output for downstream playback.
Accepts a MidiSource (file path or raw bytes), a ConstantValue file path, or a full MidiFileLoaderConfig via the Config input. When no Config input is connected, falls back to the embedded source and tempo properties. An optional tempo override rescales all event timestamps proportionally (original_tempo / new_tempo). Parsing only runs when the config changes (detected via a string key including tempo). The Loaded gate pulses once when a file is successfully parsed. Timing Out carries BPM and time signature from the loaded file.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Config In | Data(Unknown) | Accepts MidiSource, ConstantValue::FilePath, or full MidiFileLoaderConfig. Overrides the embedded source property when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Sequencer Out | Data(Sequence) | Cached MidiSequencer data, streamed every frame. Connect to a Sequencer or Sequencer Editor. |
| Loaded | Gate | Single-frame gate pulse emitted when a file is successfully parsed (including on initial embedded load). |
| Timing | Data(Timing) | BPM and time signature from the loaded MIDI file. Defaults to 4/4 if not specified in the file. |
MIDI Input (MidiInput)
MidiInput)Receives MIDI events from a virtual MIDI device
let node = MidiInput.add("my_midiinput");
Receives live MIDI from a hardware controller or virtual port via the MIDI Device Manager pub/sub system.
Set the Device property to a VirtualDeviceId to subscribe; the audio thread establishes the subscription and injects a crossbeam receiver. Events are drained from the receiver each process cycle. System messages (clock, start, etc.) always pass through; channel messages are filtered by the Channel property (0 = all). When Device is 0 (none) the node produces no output. Changing the Device property clears stale subscriptions immediately.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Device | device() | Int | 0 | 0..65535 |
| Channel | channel() | Int | 0 | 0..16 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | MIDI events drained from the subscription receiver each process cycle. Empty when no device is subscribed. |
MIDI Mixer (MidiMixer)
MidiMixer)Merges multiple MIDI input streams into a single output stream.
let node = MidiMixer.add("my_midimixer");
Merges multiple MIDI streams into a single time-ordered output.
All events from all connected inputs are collected, sorted by sample offset, and written to a single output buffer. The number of input ports is controlled by the Inputs property and also grows dynamically via topology overrides as cables are connected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Inputs | inputs() | Int | 4 | 1..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | unbounded; Unbounded MIDI input. Each connected port contributes events to the merged output. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | All input events merged and sorted by sample offset. |
MIDI Remap (MidiRemap)
MidiRemap)Remaps MIDI channel, transposes notes, scales velocity, and filters note range
let node = MidiRemap.add("my_midiremap");
MIDI transformer that remaps channels, transposes notes, scales velocity, and filters by note range.
Processing order: source channel filter, then transpose, then note range filter, then velocity scale, then target channel remap. Events that fail the source channel or note range filters are silently dropped (not passed through). Transpose is applied to NoteOn, NoteOff, and polyphonic Aftertouch messages. Velocity scaling clamps to 1--127 (never produces velocity 0). Non-note messages (CC, pitch bend, etc.) pass through with only channel remapping applied.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Source Ch | source_channel() | Int | 0 | 0..16 |
| Target Ch | target_channel() | Int | 0 | 0..16 |
| Transpose | transpose() | Int | 0 | -48..48 |
| Note Min | note_min() | Int | 0 | 0..127 |
| Note Max | note_max() | Int | 127 | 0..127 |
| Vel Scale | velocity_scale() | Int | 100 | 0..200 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Incoming MIDI events to remap. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Remapped MIDI events (transposed, channel-shifted, velocity-scaled). |
MIDI Sample & Hold (MidiSampleHold)
MidiSampleHold)Latch polyphonic MIDI note state on gate trigger
let node = MidiSampleHold.add("my_midisamplehold");
Latches polyphonic MIDI note state on each gate trigger.
In Trigger mode, incoming MIDI notes are tracked silently via MidiNoteTracker. On each Trigger rising edge, the tracker diffs the current input state against the held output state and emits the minimal note-off/note-on set to transition cleanly. Between triggers the output holds the captured snapshot -- stuck notes are impossible because the tracker manages all diffing. The Reset input flushes all held notes (all note-offs). In Pass mode, events pass through transparently (bypass). Disconnecting the MIDI input flushes the tracker silently. Max Notes limits the snapshot size; oldest notes are dropped when full.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Max Notes | max_notes() | Int | 8 | 1..16 |
| Mode | mode() | Enum | enum(0) | Trigger, Pass |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Source MIDI stream. In Trigger mode notes are tracked silently until capture. In Pass mode events flow straight through. | |
| Trigger | Gate | required; Rising edge captures the current input note state and emits the diff to the output. | |
| Reset | Gate | required; Rising edge flushes all held notes (emits note-offs for everything in the snapshot). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | In Trigger mode: the minimal diff events (note-offs then note-ons) on each capture. In Pass mode: all input events. |
MIDI Voice Splitter (MidiVoiceSplitter)
MidiVoiceSplitter)Splits polyphonic MIDI into monophonic voices. Voice count determined by connected outputs.
let node = MidiVoiceSplitter.add("my_midivoicesplitter");
Splits polyphonic MIDI into separate monophonic voice outputs with oldest-note voice stealing.
Each NoteOn is assigned to the first idle output; when all outputs are busy, the oldest voice is stolen (NoteOff sent before the new NoteOn). NoteOff is routed to whichever output holds that note. Non-note messages (CC, pitch bend, etc.) are broadcast to all outputs. All output events are on channel 1 regardless of the input channel. The Voices property controls the number of outputs.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Voices | voices() | Int | 4 | 1..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Polyphonic MIDI input to split across voices. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Voice Out | Midi | unbounded; Each output carries one monophonic voice on channel 1. Non-note messages are broadcast to all outputs. |
MIDI ➔ CV (MidiToCv)
MidiToCv)Converts MIDI notes to VOct pitch CV and gate signals
let node = MidiToCv.add("my_miditocv");
Converts MIDI note events into modular-style CV signals for driving oscillators, envelopes, and modulation sources.
Outputs volt-per-octave pitch, a gate, normalized velocity, channel/poly aftertouch, and mod wheel (CC 1). In Last mode every note passes through (classic mono last-note priority with note stack -- releasing a note restores the previous pitch). In Exclusive mode the node claims one note at a time and strips it from the Thru output; chain several Exclusive nodes for manual polyphonic voice allocation. The Channel MIDI output always carries all channel-matched events regardless of voice mode. Non-note events (CC, aftertouch) are never claimed in Exclusive mode and always appear on Thru. When the Tuning port is connected it overrides divisions-per-octave and applies a V/Oct offset to the pitch output.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Voice Mode | voice_mode() | Enum | enum(0) | Last, Exclusive |
| Reference Note | reference_note() | Int | 60 | 0..127 |
| Channel | channel() | Int | 0 | 0..16 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; MIDI events to convert. Only note, aftertouch, and CC 1 messages are extracted; other events pass through. | |
| Tuning | Data(Tuning) | Overrides the default 12-EDO divisions-per-octave and applies a V/Oct offset to the pitch output. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| VOct | VOct | Volt-per-octave pitch. Constant across the buffer, updated on each note-on. 0V at the reference note. |
| Gate | Gate | High while any note is held. In Last mode, stays high across note transitions; falls low only when the note stack empties. |
| Velocity | ControlNorm | Most recent note-on velocity, normalized 0.0--1.0. Resets to 0 when all notes release. |
| Aftertouch | ControlNorm | Channel or polyphonic aftertouch pressure, normalized 0.0--1.0. Both aftertouch types write to this output. |
| Mod Wheel | ControlNorm | Mod wheel (CC 1) as normalized 0.0--1.0. |
| Channel MIDI | Midi | All channel-matched MIDI events, forwarded regardless of voice mode or claim status. |
| Thru MIDI | Midi | In Last mode: all channel-matched events. In Exclusive mode: only unclaimed events. Non-matching channels always pass through. |
Polysynth (Polysynth)
Polysynth)Polyphonic MIDI synthesizer with configurable waveform and envelope
let node = Polysynth.add("my_polysynth");
Self-contained polyphonic synthesizer that receives MIDI and outputs stereo audio directly.
Up to 8 simultaneous voices, each with its own oscillator and ADSR envelope. Voice allocation prefers idle voices; when all are busy the oldest active voice is stolen with velocity smoothing and a cosine fade-in to prevent clicks. NoteOff is reference-counted: if the same note is triggered multiple times it only releases when all matching note-offs arrive. Output is the sum of all voices divided by max_voices, then scaled by Master Gain. Disconnecting the MIDI input releases all active voices. The Tuning port overrides 12-EDO divisions and applies a V/Oct offset.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Waveform | waveform() | Enum | enum(0) | Sine, Square, Sawtooth, Triangle |
| Shape | shape() | Envelope | [envelope] |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | NoteOn triggers a voice; NoteOn with velocity 0 acts as NoteOff. Disconnecting releases all voices. | |
| Tuning | Data(Tuning) | Overrides the default 12-EDO divisions-per-octave and applies a frequency offset to all voices. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Sum of all voices divided by max_voices, scaled by Master Gain, as mono-to-stereo. |
Polysynth V2 (PolysynthV2)
PolysynthV2)Polyphonic MIDI synthesizer with configurable waveform and envelope
let node = PolysynthV2.add("my_polysynthv2");
Polyphonic synthesizer with up to 16 voices and smooth frequency crossfades on voice steal.
Same voice allocation strategy as Polysynth (idle-first, then oldest-note steal) but adds a 16-sample linear crossfade when stealing to smooth frequency discontinuities. Voices are re-initialized at the audio thread's actual sample rate in setup(). NoteOff is reference-counted. Output is the sum of all voices divided by voice count, then scaled by Master Gain. Disconnecting the MIDI input releases all active voices. The Tuning port overrides 12-EDO divisions and applies a frequency offset at note-on time.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Waveform | waveform() | Enum | enum(0) | Sine, Square, Sawtooth, Triangle |
| Shape | shape() | Envelope | [envelope] |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | NoteOn triggers a voice; NoteOn with velocity 0 acts as NoteOff. Disconnecting releases all voices. | |
| Tuning | Data(Tuning) | Overrides the default 12-EDO divisions-per-octave and applies a frequency offset at note-on time. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Sum of all voices divided by voice count, scaled by Master Gain, as mono-to-stereo. |
Scale Quantizer (ScaleQuantizer)
ScaleQuantizer)Quantizes MIDI notes to a musical scale
let node = ScaleQuantizer.add("my_scalequantizer");
Snaps MIDI note pitches to the nearest degree in a selected musical scale.
NoteOn pitches are quantized and the original-to-quantized mapping is cached for the corresponding NoteOff, so note-offs always match their note-ons. Changing the Scale or Root Note property flushes all active notes (sends note-offs on channel 1 at sample offset 0) before clearing the mapping cache. Non-note messages pass through unchanged. Snap mode determines the quantization direction: Nearest picks the closest scale degree, Up always rounds up, Down always rounds down. The mapping cache avoids recomputing scale lookups for repeated pitches.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Scale | scale_type() | Enum | enum(1) | Chromatic, Major, Minor, HarmonicMinor, MelodicMinor, Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian, MajorPentatonic, MinorPentatonic, Blues, WholeTone, Diminished |
| Root Note | root_note() | Enum | enum(60) | C-1, C#-1, D-1, D#-1, E-1, F-1, F#-1, G-1, G#-1, A-1, A#-1, B-1, C0, C#0, D0, D#0, E0, F0, F#0, G0, G#0, A0, A#0, B0, C1, C#1, D1, D#1, E1, F1, F#1, G1, G#1, A1, A#1, B1, C2, C#2, D2, D#2, E2, F2, F#2, G2, G#2, A2, A#2, B2, C3, C#3, D3, D#3, E3, F3, F#3, G3, G#3, A3, A#3, B3, C4, C#4, D4, D#4, E4, F4, F#4, G4, G#4, A4, A#4, B4, C5, C#5, D5, D#5, E5, F5, F#5, G5, G#5, A5, A#5, B5, C6, C#6, D6, D#6, E6, F6, F#6, G6, G#6, A6, A#6, B6, C7, C#7, D7, D#7, E7, F7, F#7, G7, G#7, A7, A#7, B7, C8, C#8, D8, D#8, E8, F8, F#8, G8, G#8, A8, A#8, B8, C9, C#9, D9, D#9, E9, F9, F#9, G9 |
| Snap Mode | snap_mode() | Int | 0 | 0..2 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Incoming MIDI. Only NoteOn/NoteOff pitches are altered; all other messages pass through. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | MIDI events with note pitches snapped to the scale. NoteOff pitches match their corresponding quantized NoteOn. |
Sequencer (Sequencer)
Sequencer)MIDI sequencer that outputs timed MIDI events. Uses graph transport for tempo and play state.
let node = Sequencer.add("my_sequencer");
MIDI playback engine synchronized to the graph transport.
Receives a MidiSequencer via Config In and plays back timed MIDI events. The merged MIDI Out carries events with their original channel assignments; per-track outputs (unbounded) carry the same events remapped to channel 1. Tempo comes from the Timing input when connected, otherwise from the graph transport; tempo changes flush stuck notes. The Enabled gate (disconnected = enabled) gates playback and flushes notes on transitions. Restart resets the playhead; Reload hot-swaps the config without resetting position. When Reload is not connected, Config In is polled continuously. Transport seek events cause an immediate seek-and-flush. Loop wraps playback to the loop region when enabled; if loop_end is at the default it auto-extends to cover the entire sequence. The Tracks property sets a floor on output count -- it also grows to match the loaded sequence's track count.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Tracks | tracks() | Int | 4 | 1..64 |
| Loop | loop() | Bool | false | |
| Content BPM | content_bpm() | Float | 0 | |
| Gating | gating() | Enum | enum(0) | None, OneShot, Loop |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Restart | Gate | Rising edge restarts the sequence from tick 0, flushing all active notes. | |
| Timing | Data(Timing) | Overrides BPM and time signature from the transport. Tempo changes flush stuck notes. | |
| Reload | Gate | Rising edge hot-swaps the Config In data without resetting the playhead. When disconnected, Config In is polled continuously. | |
| Config In | Data(Sequence) | MidiSequencer data from a file loader or sequencer editor. Loaded continuously unless Reload is connected. | |
| Enabled | Gate | Gates playback. Disconnected = enabled. Transitions flush all active notes. | |
| Gate In | Gate | Clip-launcher gate. In OneShot / Loop modes, a rising edge resets the local playhead to 0 and starts playback. Loop stops on falling edge; OneShot plays through. Ignored in None mode. | |
| Speed Mod | Control | Multiplier on playback rate. 1.0 = normal, 0.5 = half speed, 2.0 = double, 0.0 = freeze playhead (resumable from the same tick when the value comes back up). Negative values clamp to zero. Disconnected = 1.0 (no effect). Works in all gating modes. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Merged MIDI from all tracks with original channel assignments preserved. |
| Tempo | Control | Effective BPM after Timing port override or transport fallback. Useful for downstream tempo-dependent effects. |
| Track | Midi | unbounded; Per-track MIDI output with all events remapped to channel 1. One output per sequencer track. |
Sequencer Editor (SequencerEditor)
SequencerEditor)Visual MIDI sequencer editor with piano roll interface. Click to open editor panel.
let node = SequencerEditor.add("my_sequencereditor");
Visual MIDI sequencer editor with a piano-roll interface.
Holds the authoritative MidiSequencer state and outputs it via Data port to a Sequencer node each frame. The UI sends incremental SequencerCommand edits (add, move, resize, delete notes; track mute/solo/channel changes; BPM and time signature updates). The node maintains a note_map as the single source of truth and rebuilds track MIDI events from it. In-flight notes (being dragged) are excluded from events until they land. The Import input accepts a MidiSequencer from an external source (file loader, chord progression); use the ImportFromInput command to merge it. Audition MIDI (note preview during editing) is output on the MIDI Out port on the configured audition channel. The Update Gate pulses whenever an edit is applied. Timing output carries BPM and time signature from the editor's properties.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Audition Channel | audition_channel() | Int | 1 | 0..15 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Import | Data(Sequence) | External MidiSequencer to import. Connected data is cached; use the ImportFromInput command to merge it into the editor. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Sequencer Out | Data(Sequence) | Full MidiSequencer data, output every frame. Connect to a Sequencer node's Config In. |
| Update Gate | Gate | Single-frame gate pulse after each SequencerCommand edit is applied. |
| MIDI Out | Midi | Note audition/preview events on the configured audition channel. Triggered by SequencerAudition commands. |
| Timing | Data(Timing) | BPM, time signature, and key signature from the editor's properties. Connect to a Sequencer's Timing input. |
Voice Mixer (VoiceMixer)
VoiceMixer)MIDI-controlled polyphonic mixer with per-voice ADSR envelopes and voice stealing
let node = VoiceMixer.add("my_voicemixer");
MIDI-controlled polyphonic voice mixer with per-voice ADSR envelopes and voice stealing.
Allocates MIDI notes to voice slots (idle-first, then oldest-note steal). Each slot gates one unbounded audio input with an independent ADSR envelope, velocity smoothing, and cosine fade-in. All gated voices are summed, divided by the connected voice count (fixed denominator -- not the number of active voices), then scaled by Master Gain. NoteOff is reference-counted. The slot pool resizes dynamically to match the number of connected audio inputs. Disconnecting the MIDI input releases all active slots.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Voices | voices() | Int | 4 | 1..64 |
| Shape | shape() | Envelope | [envelope] | |
| Master Gain | master_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; MIDI notes for voice allocation. Disconnecting releases all active slots. | |
| Voice In | Audio | unbounded; Audio from a voice processing chain. Slot index maps directly to input index (after the MIDI port). Unconnected slots contribute silence. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Sum of all gated voices divided by voice count, scaled by Master Gain. |
Sink
Audio Sink (AudioSink)
Audio Sink (AudioSink)
Audio output point with optional well-known label
let node = AudioSink.add("my_audiosink");
Final destination for audio in the graph. Connect any stereo audio source here to route it to the hardware output.
Every graph has exactly one AudioSink that the engine pulls from each buffer cycle. process() copies the stereo input buffer directly to the Sink output with no transformation. If Audio In is disconnected the output buffer is left silent. The Sink port type marks this node as the pull target for the graph engine's demand-driven processing.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to send to the hardware output. Copied directly to the Sink output with no processing. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Sink Out | Sink | Sink port that the graph engine pulls to drive demand-driven processing of all upstream nodes. |
Source
Audio Input (AudioInput)
Audio Input (AudioInput)
Hardware audio capture (microphone, line in)
let node = AudioInput.add("my_audioinput");
Captures live audio from a hardware input device (microphone, line in, audio interface) and injects it into the graph.
Device names come from the OS audio subsystem. "Default" uses the system default input. Streams open at stereo regardless of the device's native channel count; multi-channel interfaces route through the OS stereo bus.
Gain uses exponential scaling above unity: 0.0 = silent, 0.5 = unity, 1.0 = +12 dB. Defaults to unity (0.5) when the Gain input is disconnected.
The internal ring buffer holds ~100 ms at 48 kHz. If the graph cannot keep up, the oldest input samples are dropped. This node does not record -- use Audio Looper or Sample Recorder downstream for capture.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Device | device() | StringList | Default | |
| Refresh Devices | refresh_devices() | Bool | false | |
| Channels | channels() | Enum | enum(1) | Mono, Stereo |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gain | ControlNorm | Input gain control. Exponential above unity: 0.0 = silent, 0.5 = unity, 1.0 = +12 dB. Defaults to unity when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Captured stereo audio from the selected hardware device. Outputs silence when no consumer is attached or the ring buffer is empty. |
| Level | ControlNorm | RMS level of the captured signal as ControlNorm, suitable for metering without an Envelope Follower. Smoothed to avoid jitter (only updates when the change exceeds 0.001). |
Audio Looper (AudioLooper)
AudioLooper)Gate-triggered audio loop recorder with overdub
let node = AudioLooper.add("my_audiolooper");
Gate-triggered loop recorder with multi-layer overdub. Hold Record to capture; the first recording defines the loop length and all subsequent overdubs wrap within it.
Two recording inputs: Record (hold to record, release to stop) and Record Latch (rising edge toggles record on/off for hands-free or foot pedal use). When Record Latch is connected, it overrides the Record gate.
With Layers set to 1, overdubs mix directly into the base buffer. Feedback is applied during playback (not just overdub): at 1.0 the loop plays forever, below 1.0 the buffer decays each pass like a delay line. With Layers > 1, each overdub is stored on a separate layer; Clear removes the most recent layer (undo), and Feedback is ignored to preserve undo integrity.
Freeze holds the current position, sustaining audio as a crossfaded micro-loop around the freeze point. Freeze Scrub picks where in the loop you are frozen (ignored without Freeze). Freeze Width sets the size of the micro-loop window.
Speed uses exponential scaling (0.25x-4x). Reverse plays the loop backwards. Quantize defers record start/stop to the next beat boundary using transport position. Monitor passes input audio through to the output during recording to prevent latency surprise. Max Duration pre-allocates the buffer and cannot be changed while a loop is loaded.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Max Duration | max_duration() | Float | 30 | 1..120 |
| Monitor | monitor() | Bool | true | |
| Quantize | quantize() | Bool | false | |
| Fade | fade() | Float | 5 | 0..50 |
| Layers | layers() | Int | 1 | 1..32 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to record into the loop. During playback without recording, this input is ignored unless Monitor is on. | |
| Record | Gate | Hold high to record/overdub, release to stop. The first recording pass defines the loop length; subsequent holds overdub within it. Ignored when Record Latch is connected. | |
| Record Latch | Gate | Rising edge toggles record on/off (hands-free / foot pedal). Overrides the Record gate when connected. | |
| Clear | Gate | Rising edge erases content. With Layers > 1, clears the most recent layer (undo). With 1 layer remaining, performs a full reset (erases buffer, stops playback, resets latch state). | |
| Restart | Gate | Rising edge resets playback position to the loop start. | |
| Speed | ControlNorm | Playback speed with exponential scaling: 0.0 = 0.25x, 0.5 = normal, 1.0 = 4x. Defaults to normal speed when disconnected. | |
| Reverse | Gate | Gate high reverses the playback direction. Resets to forward when disconnected. | |
| Feedback | ControlNorm | Loop buffer decay amount (Layers = 1 only). Applied during playback, not just overdub: at 1.0 (default when disconnected) the loop plays forever; below 1.0, the buffer decays each pass like a delay line. Ignored in multi-layer mode to preserve undo integrity. | |
| Freeze Scrub | ControlNorm | Picks where in the loop you are frozen (0.0 = start, 1.0 = end). Only active while Freeze is held; ignored otherwise. Smoothed to avoid clicks on abrupt CV changes. | |
| Freeze Width | ControlNorm | Size of the frozen micro-loop window as a fraction of the loop length. Defaults to ~1% (~20 ms at typical loop lengths) when disconnected. Smoothed to prevent clicks. | |
| Freeze | Gate | Gate high freezes playback at the current position. Audio sustains as a crossfaded micro-loop around the freeze point. Resets to normal playback when disconnected. | |
| Monitor | Gate | Gate to toggle input monitoring on/off. Overrides the Monitor property when connected. Falls back to the property value when disconnected. | |
| Volume | ControlNorm | Output volume scaling. Defaults to 1.0 (full) when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo mix of all recorded layers (interpolated). Includes the monitored input signal during overdub if Monitor is on. |
| Gate Out | Gate | High while the loop is playing back (after the first recording completes). Goes low when cleared. |
| Position | ControlNorm | Normalized playback position within the loop (0.0-1.0). |
| Length | Control | Loop length in beats (derived from sample count and transport BPM). Zero when no loop is loaded or BPM is zero. |
Drone Oscillator (DroneOscillator)
DroneOscillator)Detuned beating sines for ambient drone textures
let node = DroneOscillator.add("my_droneoscillator");
Layers multiple tightly-detuned sine waves to produce slow beating and interference patterns - the foundation for ambient pads and drones.
Voices are spread symmetrically around the center frequency with sub-Hz offsets determined by the Beat Rate. For N voices, offsets range from -beat_rate/2 to +beat_rate/2 Hz. Warmth blends in a second harmonic (one octave up) at half amplitude per voice. Beat Rate CV maps 0.0-1.0 linearly to 0.01-5.0 Hz and overrides the property. The output is normalized by voice count.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..2000 |
| Voices | voice_count() | Int | 4 | 2..8 |
| Beat Rate | beat_rate() | Float | 0.5 | 0.01..5 |
| Warmth | warmth() | Float | 0 | 0..1 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets center pitch, resets all voice phases on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets all voice phases. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Beat Rate Mod | ControlNorm | Overrides the Beat Rate property. 0.0 maps to 0.01 Hz, 1.0 maps to 5.0 Hz (linear). | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Summed drone voices, normalized by voice count. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Drum Voice (DrumVoice)
DrumVoice)All-in-one percussion synthesizer with multiple drum modes
let node = DrumVoice.add("my_drumvoice");
All-in-one percussion synthesizer with selectable drum modes. Each mode uses a different internal topology of oscillators, noise, filters, and envelopes tuned for that drum sound.
Triggering: a rising edge on the Gate input or a MIDI NoteOn resets all envelopes, oscillator phases, and filter state. Both trigger sources are additive - either one fires the drum. The voice auto-deactivates when its amplitude envelope completes, zeroing the remaining output buffer for efficiency.
Pitch is the sum of the Pitch property, MIDI note offset (note - 60 in octaves), and the V/Oct input. The combined value maps to a frequency multiplier via 2^pitch, so +1 = one octave up, -1 = one octave down. Decay CV, Tone Mod, and Snap Mod each replace their respective property value when connected (not additive). Drive applies tanh soft clipping with gain ranging from 1x to 9x.
Mode-specific topologies: Kick uses a sine oscillator with pitch sweep plus noise click. Snare combines a sine body with bandpass-filtered noise. Hihat runs white noise through highpass and bandpass filters. Tom is similar to kick but higher pitch and less sweep. Clap fires three rapid bandpass-filtered noise bursts. Rimshot blends two detuned sine oscillators with a noise transient. Cowbell uses two detuned clipped-sine oscillators through a bandpass filter.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | Kick, Snare, Hihat, Tom, Clap, Rimshot, Cowbell |
| Pitch | pitch() | Float | 0 | -1..1 |
| Decay | decay() | Float | 0.5 | 0..1 |
| Tone | tone() | Float | 0.5 | 0..1 |
| Snap | snap() | Float | 0.3 | 0..1 |
| Drive | drive() | Float | 0 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note input. NoteOn triggers the drum and sets a pitch offset based on the note number (C4/note 60 = zero offset). Gate and velocity events are forwarded to the output ports. | |
| Gate In | Gate | required; Rising edge triggers the drum hit. Only rising edges trigger; falling edges are ignored. Can fire alongside MIDI - both sources are independent triggers. | |
| V/Oct | VOct | Pitch offset in volts-per-octave, added to the Pitch property and any MIDI note offset. When disconnected, contributes zero offset. | |
| Decay CV | ControlNorm | Replaces the Decay property value when connected. The CV value is used directly as the 0-1 decay parameter. When disconnected, the property value is used. | |
| Accent | ControlNorm | Scales the output amplitude before drive processing. Typically driven by a sequencer velocity output. Defaults to 1.0 when disconnected. | |
| Tone Mod | ControlNorm | Replaces the Tone property value when connected. The CV value is used directly. When disconnected, the property value is used. | |
| Snap Mod | ControlNorm | Replaces the Snap property value when connected. The CV value is used directly. When disconnected, the property value is used. | |
| Tuning | Data(Tuning) | Tuning configuration for MIDI pitch quantization. Overrides the default 12-TET divisions per octave and V/Oct offset. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Mono drum audio (emitted as identical L/R). Includes accent scaling and drive processing. Silent when the voice is inactive. |
| Gate | Gate | Gate events forwarded from the MIDI input. Not emitted when triggered by the Gate input port alone. |
| Velocity | ControlNorm | MIDI velocity forwarded as normalized CV. Only emitted when MIDI input is connected. |
FM Operator (FmOperator)
FmOperator)Frequency modulation synthesis operator with external modulation and feedback
let node = FmOperator.add("my_fmoperator");
Single FM synthesis operator that works as both carrier and modulator. Chain operators by connecting one's Mod Out to another's Modulator In for classic DX-style patches.
Audio Out and Mod Out carry the same signal (both scaled by Output Level); use Audio Out for final output and Mod Out for chaining into other operators. Ratio multiplies the carrier frequency to set the operator's actual oscillation frequency. FM Index CV and Feedback CV each add to their respective property values via smoothers. The operator DSP is reconstructed each process call, so property changes take effect immediately. No dedicated Gate input - phase reset is triggered only via MIDI note-on.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Carrier Freq | carrier_frequency() | Float | 440 | 20..20000 |
| Ratio | ratio() | Float | 1 | 0.25..16 |
| FM Index | fm_index() | Float | 0 | 0..10 |
| Feedback | feedback() | Float | 0 | 0..1 |
| Waveform | waveform() | Enum | enum(0) | Sine, Square, Sawtooth, Triangle |
| Output Level | output_level() | Float | 1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets carrier pitch and drives Gate/Velocity outputs. The only way to trigger this node - there is no Gate input. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or Carrier Freq exponentially; ControlFreq overrides in Hz. | |
| Modulator In | Audio | Audio-rate modulator signal from another operator's Mod Out. Downmixed to mono, scaled by FM Index. | |
| FM Index Mod | ControlNorm | Adds to the FM Index property value via the smoother. | |
| Feedback Mod | ControlNorm | Adds to the Feedback property value via the smoother. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Final operator audio, scaled by Output Level. Identical signal to Mod Out. |
| Mod Out | Audio | Same signal as Audio Out, intended for chaining into another operator's Modulator In. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Formant Oscillator (Formant)
Formant)Vowel-like sounds via resonant formant filters with morph control
let node = Formant.add("my_formant");
Produces vowel-like vocal sounds by running a carrier waveform through resonant bandpass filters tuned to formant frequencies. Pick two vowel shapes and sweep between them with the Morph knob.
The carrier waveform (Saw, Pulse, or Noise) excites the formant filter bank; Resonance controls how sharp the vowel peaks are. Morph CV adds to the property value, clamped 0.0-1.0, and bypasses the smoother when connected. The Resonance property has no CV input and applies uniformly to all formant bands.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 220 | 20..5000 |
| Carrier | carrier() | Enum | enum(0) | Saw, Pulse, Noise |
| Vowel A | vowel_a() | Enum | enum(0) | A, E, I, O, U |
| Vowel B | vowel_b() | Enum | enum(3) | A, E, I, O, U |
| Morph | morph() | Float | 0 | 0..1 |
| Resonance | resonance() | Float | 0.5 | 0..1 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets carrier pitch, resets phase on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets the carrier phase and filter state. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Morph Mod | ControlNorm | Adds to the Morph property value, clamped 0.0-1.0. Bypasses the smoother when connected. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Formant-filtered audio. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Frequency Control (FrequencyControl)
FrequencyControl)Constant frequency control source in Hz
let node = FrequencyControl.add("my_frequencycontrol");
Outputs a constant frequency value in Hz as a ControlFreq signal. Use it to drive filter cutoffs, oscillator frequencies, or any parameter that accepts ControlFreq.
Pure source node with no inputs - the output is the Frequency property value written directly to the control port each frame.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 1000 | 20..20000 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Frequency | ControlFreq | Emits the Frequency property value as ControlFreq each frame. |
Granular Voice (GranularVoice)
GranularVoice)Micro-grain playback with textural, evolving sound
let node = GranularVoice.add("my_granularvoice");
Granular synthesis voice that continuously fills a circular buffer from an internal oscillator or external audio, then spawns overlapping micro-grains from that buffer to produce textural, evolving timbres.
The buffer is always being written to. Grains spawn at a rate set by Density and read from a position relative to the write head (Position = 0 reads the oldest sample, 1 reads the newest). Each grain gets independent randomized pitch offset, stereo pan, and optional reverse playback. The output is the normalized sum of all active grains, divided by the number currently sounding.
Gate (from the Gate port or MIDI NoteOn) resets the write head and spawn accumulator but does not start or stop grain generation - grains run continuously regardless of gate state. MIDI also sets oscillator frequency and emits gate/velocity on the output ports.
When Source is set to Input, the Audio In signal is written into the buffer instead of the internal oscillator. The oscillator frequency is ignored in this mode, but Frequency still affects MIDI-driven pitch tracking on the output.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..5000 |
| Grain Size | grain_size() | Float | 30 | 1..100 |
| Density | density() | Float | 10 | 1..50 |
| Position | position() | Float | 0.5 | 0..1 |
| Position Scatter | position_scatter() | Float | 0.1 | 0..1 |
| Pitch Scatter | pitch_scatter() | Float | 0 | 0..100 |
| Pan Scatter | pan_scatter() | Float | 0 | 0..1 |
| Reverse Chance | reverse_chance() | Float | 0 | 0..1 |
| Source | source() | Enum | enum(0) | Sine, Saw, Noise, Input |
| Buffer Length | buffer_length() | Float | 100 | 50..2000 |
| Grain Envelope | envelope() | Curve | [curve] |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note input. NoteOn sets oscillator frequency and resets the write head; NoteOff clears the triggered flag. Gate and velocity events are forwarded to the output ports. | |
| Gate | Gate | Rising edge resets the write head and spawn accumulator. Does not start or stop grain generation. Falling edge clears the triggered flag so the next rising edge can re-trigger. | |
| Audio In | Audio | External audio fed into the grain buffer. Only used when Source is set to Input; otherwise this port is ignored. The stereo signal is mono-summed before writing to the buffer. | |
| Frequency Mod | ControlFreq, VOct | Frequency control via V/Oct or ControlFreq. Priority: if MIDI is active and V/Oct is connected, they combine additively. V/Oct alone offsets the property frequency exponentially. ControlFreq replaces the frequency directly. When disconnected, the property value is used. | |
| Position Mod | ControlNorm | Overrides the Position property when connected. Smoothed to avoid zipper noise. When disconnected, the property value is used. | |
| Density Mod | ControlNorm | Overrides the Density property when connected. CV 0.0 maps to 1 grain/s, 1.0 maps to 50 grains/s. Smoothed. When disconnected, the property value is used. | |
| Grain Size Mod | ControlNorm | Overrides the Grain Size property when connected. CV 0.0 maps to 1 ms, 1.0 maps to 100 ms. Smoothed. When disconnected, the property value is used. | |
| Pitch Scatter Mod | ControlNorm | Overrides the Pitch Scatter property when connected. CV 0.0 maps to 0 cents, 1.0 maps to 100 cents. Written directly (not smoothed). When disconnected, the property value is used. | |
| Pan Scatter Mod | ControlNorm | Overrides the Pan Scatter property when connected. Smoothed. When disconnected, the property value is used. | |
| Tuning | Data(Tuning) | Tuning configuration for MIDI pitch quantization. Overrides the default 12-TET divisions per octave and V/Oct offset. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Stereo mix of all active grains, normalized by dividing by the number of currently sounding grains. |
| Gate | Gate | Gate events forwarded from the MIDI input. Not emitted when triggered by the Gate input port alone. |
| Velocity | ControlNorm | MIDI velocity forwarded as normalized CV. Only emitted when MIDI input is connected. |
Harmonic Oscillator (HarmonicOscillator)
HarmonicOscillator)16-partial additive synthesis with spectral tilt and inharmonicity
let node = HarmonicOscillator.add("my_harmonicoscillator");
Additive oscillator with 16 independently controllable sine partials. Sculpt timbres by drawing the harmonic spectrum directly - set each partial's amplitude and frequency ratio via node commands.
Tilt applies a dB-per-octave brightness slope across all partials: positive boosts upper partials, negative attenuates them. Tilt CV maps 0.0-1.0 to the full -1.0 to +1.0 range and overrides the property. Stretch shifts partial ratios toward inharmonic spacing using the formula ratio * (1 + stretch * (ratio - 1) * 0.01), so higher partials are displaced more. The output is normalized by the count of active (non-zero amplitude) partials.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..5000 |
| Tilt | tilt() | Float | 0 | -1..1 |
| Stretch | stretch() | Float | 0 | 0..1 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets fundamental pitch, resets all 16 phases on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets all 16 partial phases. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Tilt Mod | ControlNorm | Overrides the Tilt property. 0.0 maps to -1.0 (dark), 1.0 maps to +1.0 (bright). | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Sum of all active partials, normalized by the count of non-zero-amplitude partials. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Karplus-Strong (KarplusStrong)
KarplusStrong)Physical modeling of plucked strings using delay line synthesis
let node = KarplusStrong.add("my_karplusstrong");
Plucked-string physical model using delay line synthesis. A noise burst or impulse excites a tuned delay line that feeds back through a lowpass filter, producing realistic guitar, harp, and bell-like tones.
Trigger via Gate input or MIDI. MIDI NoteOn plucks at the given pitch and passes gate/velocity to the output ports. When both MIDI and V/Oct are connected, MIDI sets the base pitch and V/Oct adds an offset. V/Oct alone multiplies the Frequency property. ControlFreq directly sets frequency in Hz. Pitch is locked at trigger time in the delay line DSP.
Damping CV maps 0.0-1.0 to the 0.9-0.9999 feedback range. When disconnected, the Damping property is smoothed. Brightness CV is additive with the property value and clamped to 0.0-1.0. When disconnected, it is also smoothed.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..5000 |
| Damping | damping() | Float | 0.995 | 0.9..0.9999 |
| Brightness | brightness() | Float | 0.5 | 0..1 |
| Pick Position | pick_position() | Float | 0.5 | 0..1 |
| Excitation | excitation_type() | Enum | enum(0) | Noise, Impulse, FilteredNoise |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note input. NoteOn plucks the string at the given pitch and velocity. Gate and velocity are forwarded to the output ports. When both MIDI and V/Oct are connected, MIDI sets the base pitch and V/Oct adds an offset. | |
| Trigger | Gate | Rising edge excites the delay line (plucks the string). Uses the current frequency from property or CV inputs. Falling edge re-arms the trigger. | |
| Frequency Mod | ControlFreq, VOct | Pitch control. Accepts V/Oct (multiplied with base frequency or added to MIDI pitch) or ControlFreq (direct Hz). Priority: MIDI+V/Oct combined > V/Oct alone > ControlFreq > property. | |
| Damping Mod | ControlNorm | CV override for damping. Maps 0.0-1.0 to the 0.9-0.9999 feedback range. Overrides the smoothed property value when connected. | |
| Brightness Mod | ControlNorm | CV modulation added to the Brightness property value, clamped to 0.0-1.0. Overrides the smoothed property when connected. | |
| Tuning | Data(Tuning) | Tuning configuration data. Overrides the MIDI driver's divisions-per-octave and V/Oct offset for microtonal tuning. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Synthesized plucked-string audio (mono duplicated to stereo). |
| Gate | Gate | Gate events forwarded from MIDI input. Only emits events when MIDI In is connected. |
| Velocity | ControlNorm | MIDI velocity as normalized CV. Only updates when a MIDI NoteOn is received. |
Modal Body (ModalBody)
ModalBody)Struck body physical model - bells, marimbas, glass, metal, wood
let node = ModalBody.add("my_modalbody");
Physical model of struck objects using a bank of damped resonators. Simulates bells, marimbas, glass, metal plates, and wood blocks.
Trigger via Gate input or MIDI. MIDI NoteOn strikes the body at the given pitch and passes gate/velocity to the output ports. When both MIDI and V/Oct are connected, MIDI sets the base pitch and V/Oct adds an offset. V/Oct alone multiplies the Frequency property. ControlFreq directly sets frequency in Hz.
Material, Brightness, Decay, and Inharmonicity are sampled at trigger time to configure the resonator bank. Brightness and Decay CV inputs are evaluated at trigger time; changing them mid-ring does not alter the decay envelope.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..5000 |
| Material | material() | Enum | enum(0) | 0..4; Bell, Marimba, Glass, Metal, Wood |
| Brightness | brightness() | Float | 0.8 | 0..1 |
| Decay | decay() | Float | 0.5 | 0..1 |
| Inharmonicity | inharmonicity() | Float | 0 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note input. NoteOn strikes the body at the given pitch and velocity. Gate and velocity are forwarded to the output ports. When both MIDI and V/Oct are connected, MIDI sets the base pitch and V/Oct adds an offset. | |
| Gate | Gate | Rising edge triggers a strike using the current frequency, material, brightness, decay, and inharmonicity. Falling edge re-arms the trigger. | |
| Frequency Mod | ControlFreq, VOct | Pitch control. Accepts V/Oct (multiplied with base frequency or added to MIDI pitch) or ControlFreq (direct Hz). Priority: MIDI+V/Oct combined > V/Oct alone > ControlFreq > property. | |
| Brightness Mod | ControlNorm | CV modulation added to the Brightness property value, clamped to 0.0-1.0. Evaluated at trigger time. | |
| Decay Mod | ControlNorm | CV override for decay time. Replaces the Decay property value when connected. Evaluated at trigger time. | |
| Tuning | Data(Tuning) | Tuning configuration data. Overrides the MIDI driver's divisions-per-octave and V/Oct offset for microtonal tuning. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Synthesized struck-body audio (mono duplicated to stereo). |
| Gate | Gate | Gate events forwarded from MIDI input. Only emits events when MIDI In is connected. |
| Velocity | ControlNorm | MIDI velocity as normalized CV. Only updates when a MIDI NoteOn is received. |
Noise Filter Bank (NoiseFilterBank)
NoiseFilterBank)Noise through parallel bandpass filters with per-band envelopes for percussion synthesis
let node = NoiseFilterBank.add("my_noisefilterbank");
Noise source routed through four parallel bandpass filters, each with its own gain and exponential decay envelope. Designed for metallic percussion: hihats, cymbals, snare noise layers.
On a gate rising edge, all four band envelopes reset to zero and filter state is cleared. Each band runs an independent exponential decay (exp(-5t)) whose duration comes from the preset's per-band decay time multiplied by the Decay Scale property. A band is considered finished when its envelope time exceeds 3.0 (approximately -65 dB). When all four bands have finished, the voice deactivates and zeros the remaining output buffer for efficiency.
The Tone property shifts all four band center frequencies together as a power-of-two multiplier (2^tone octaves). Brightness scales the filter Q values (0.5 + brightness * 2.0), so higher brightness produces tighter, more resonant bands. The output is the sum of all four filtered bands, scaled by the Accent input.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Preset | preset() | Enum | enum(0) | Hihat, Snare, Cymbal, Custom |
| Noise Type | noise_type() | Enum | enum(0) | White, Pink, Brown |
| Tone | tone() | Float | 0 | -1..1 |
| Decay | decay_scale() | Float | 1 | 0.1..4 |
| Brightness | brightness() | Float | 0.5 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate In | Gate | required; Rising edge resets all four band envelopes and clears filter state for a clean attack. Only rising edges trigger; falling edges are ignored. | |
| Decay CV | ControlNorm | Replaces the Decay Scale property when connected. CV 0.0 maps to 0.1x, 1.0 maps to 4.0x. Sampled once per buffer. When disconnected, the property value is used. | |
| Brightness CV | ControlNorm | Replaces the Brightness property when connected. Sampled once per buffer. When disconnected, the property value is used. | |
| Accent | ControlNorm | Scales the output amplitude of all bands. Typically driven by a sequencer velocity output. Defaults to 1.0 when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Sum of all four filtered noise bands, each weighted by its preset gain and envelope, then scaled by Accent. |
Noise Generator (Noise)
Noise)White, pink, and brown noise generator
let node = Noise.add("my_noise");
Continuous noise source with selectable color. White has equal energy at all frequencies; pink has equal energy per octave (1/f via Voss-McCartney); brown rolls off at 1/f-squared (leaky integrator of white noise).
No MIDI or pitch input - this is a free-running generator. Amplitude CV overrides the property entirely (sets the smoother target to the CV value, ignoring the knob). When disconnected, the smoothed property value is used.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Noise Type | noise_type() | Enum | enum(0) | White, Pink, Brown |
| Amplitude | amplitude() | Float | 1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Amplitude Mod | ControlNorm | Overrides the Amplitude property entirely - the CV value replaces the knob setting via the smoother. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Continuous noise audio, amplitude-scaled. |
Organ (Organ)
Organ)Hammond-style drawbar organ with 9 harmonics and key click
let node = Organ.add("my_organ");
Hammond-style tonewheel organ with nine drawbar harmonics and optional key click. Each drawbar controls the level of a sine partial at a specific footage ratio (16' through 1').
Gate and MIDI note-on both reset the internal oscillator phases and trigger the key click transient (edge-triggered). Key Click CV adds to the property value, clamped 0.0-1.0; it bypasses the smoother when connected. Drawbar levels are integers 0-8 mapping to the traditional Hammond registration scale.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..5000 |
| 16' Sub | db_sub() | Int | 8 | 0..8 |
| 8' Fund | db_fund() | Int | 8 | 0..8 |
| 5-1/3' | db2nd() | Int | 8 | 0..8 |
| 4' | db4th() | Int | 6 | 0..8 |
| 2-2/3' | db5_1_3() | Int | 0 | 0..8 |
| 2' | db8th() | Int | 4 | 0..8 |
| 1-3/5' | db10_2_3() | Int | 0 | 0..8 |
| 1-1/3' | db12th() | Int | 0 | 0..8 |
| 1' | db16th() | Int | 0 | 0..8 |
| Key Click | key_click() | Float | 0.3 | 0..1 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets pitch, resets phases and triggers key click on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets oscillator phases and fires the key click. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Key Click Mod | ControlNorm | Adds to the Key Click property value, clamped 0.0-1.0. Bypasses the smoother when connected. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Sum of all nine drawbar harmonics plus key click transient. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Oscillator (Oscillator)
Oscillator)Waveform generator with frequency modulation
let node = Oscillator.add("my_oscillator");
General-purpose waveform generator with sine, saw, square, triangle, and pulse shapes. Use as a sound source, LFO, or modulator.
Pitch priority: MIDI sets the base pitch and VOct offsets it; VOct alone scales the Frequency knob exponentially; ControlFreq overrides the knob with an absolute Hz value. Detune applies on top of all three as an exponential octave offset. Gate and MIDI note-on both reset phase on the rising edge (edge-triggered, not level-sensitive). Gate Out and Velocity Out only emit when MIDI is connected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..20000 |
| Waveform | waveform() | Enum | enum(0) | Sine, Square, Sawtooth, Triangle |
| Pulse Width | pulse_width() | Float | 0.5 | 0.01..0.99 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets base pitch, resets phase on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets phase for consistent attack. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| PW Mod | ControlNorm | Overrides the PulseWidth property via the smoother. Only audible with Pulse waveform. | |
| Detune | ControlBipolar | Bipolar pitch offset applied after frequency resolution. -1.0 to +1.0 maps exponentially to -1 to +1 octaves. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Generated waveform audio. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Phase Distortion (PhaseDistortion)
PhaseDistortion)CZ-style phase distortion oscillator - saw, square, resonant, pulse timbres
let node = PhaseDistortion.add("my_phasedistortion");
Casio CZ-style oscillator that warps a sine wave's phase to produce saw, square, resonant, and pulse timbres from a single core. Sweep the Depth knob from zero (pure sine) to full for dramatically changing harmonic content.
Depth CV adds to the property value and clamps to 0.0-1.0; when disconnected the smoothed property value is used. The Mode selector chooses the phase transfer function - each mode produces a different harmonic structure at the same depth setting.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..20000 |
| Mode | mode() | Enum | enum(0) | 0..3; Saw, Square, Resonant, Pulse |
| Depth | depth() | Float | 0.5 | 0..1 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets pitch, resets phase on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets phase for consistent attack. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Depth Mod | ControlNorm | Adds to the Depth property value, clamped 0.0-1.0. Bypasses the smoother when connected. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Phase-distorted audio. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Sample Player (SamplePlayer)
SamplePlayer)Gate-triggered sample playback with stereo output
let node = SamplePlayer.add("my_sampleplayer");
Gate-triggered sample playback from the project library. Supports one-shot and looping modes with adjustable start/end points and pitch control.
Samples are borrowed from the resource cache each process call, so hot-swapped samples are picked up automatically. Stereo samples produce true stereo output; mono samples are duplicated to both channels. Linear interpolation is used between sample frames.
The Sample data input overrides the Sample property when connected. The Pitch input controls playback speed as a multiplier (1.0 = normal, 2.0 = double speed/octave up); negative values play in reverse. Defaults to 1.0 when disconnected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | OneShot, Loop |
| Start | start() | Float | 0 | 0..1 |
| End | end() | Float | 1 | 0..1 |
| Sample | sample_key() | String |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate | Gate | required; Rising edge resets the playback position to Start and begins playing. If the sample is not yet cached, the trigger is silently ignored (no crash, no playback). | |
| Pitch | ControlBipolar | Playback speed multiplier added to the position each sample. 1.0 = normal, 2.0 = double speed / octave up, negative = reverse. Defaults to 1.0 when disconnected. | |
| Sample | Data(Sample) | Data input to select a sample by resource key. Overrides the Sample property when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | Stereo audio output. Mono samples are duplicated to both channels. Outputs silence when stopped or when the sample is not cached. |
| Playing | Gate | High while the sample is actively playing. Goes low on OneShot completion or when the sample is cleared. |
Sample Recorder (SampleRecorder)
SampleRecorder)Records audio input to the project sample library
let node = SampleRecorder.add("my_samplerecorder");
Records audio input into the project sample library. Gate-on starts recording, gate-off saves the result. Can be inserted inline without breaking the signal chain -- audio passes through unchanged to the Thru output.
Create mode adds a numbered sample each recording (e.g. "recording_001"). Overwrite mode replaces an existing sample by key. The Sample data input overrides both mode and target property: when connected, recordings always overwrite that key regardless of mode setting.
Audio is downmixed to mono internally before saving. Recording auto-stops when Max Length is reached.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | Create, Overwrite |
| Name | sample_name() | String | recording | |
| Max Length | max_length() | Float | 60 | 0.1..300 |
| Target Sample | target_key() | String |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to record. Downmixed to mono before saving. Passed through unchanged to the Thru output regardless of recording state. | |
| Gate | Gate | required; Rising edge starts recording (clears any previous buffer). Falling edge finalizes and saves the recording to the project library. | |
| Sample | Data(Sample) | Data input to select a target sample key. When connected, recordings always overwrite the specified key regardless of the Mode or Target Sample property settings. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Thru | Audio | Audio passthrough -- the input signal forwarded unchanged regardless of recording state. Outputs silence when Audio In is disconnected. |
Shepard Tone (ShepardTone)
ShepardTone)Continuously ascending/descending pitch illusion
let node = ShepardTone.add("my_shepardtone");
Creates the auditory illusion of a pitch that rises (or falls) forever. Multiple sine voices spaced one octave apart are faded with a Gaussian window centered on the mid-range, so entering and leaving voices are inaudible.
The sweep position advances continuously in octaves and wraps at 1.0 (one full octave cycle). Each voice's frequency is base_freq * 2^(voice_index + sweep_position). The Spread property controls the Gaussian window width in octaves - smaller values create a narrower audible band. Speed CV maps 0.0-1.0 linearly to 0.01-2.0 oct/s and overrides the property. Gate and MIDI note-on both reset sweep position and all phases.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | base_frequency() | Float | 100 | 20..500 |
| Speed | speed() | Float | 0.2 | 0.01..2 |
| Direction | direction() | Enum | enum(0) | Up, Down, Static |
| Spread | spread() | Float | 2 | 0.5..4 |
| Voices | voice_count() | Int | 6 | 4..8 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets base pitch, resets sweep position and phases on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets sweep position to zero and clears all phases. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the base frequency exponentially; ControlFreq overrides in Hz. | |
| Speed Mod | ControlNorm | Overrides the Speed property. 0.0 maps to 0.01 oct/s, 1.0 maps to 2.0 oct/s (linear). | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Summed and Gaussian-windowed sine voices, normalized by voice count. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Spectral Painter (SpectralPainter)
SpectralPainter)Image-based drawable spectrogram - outputs spectral data
let node = SpectralPainter.add("my_spectralpainter");
Drawable spectrogram that converts painted pixel brightness into spectral magnitude data. Paint directly on the canvas in the UI to sculpt frequency content over time.
Each image column represents one spectral frame. The playhead advances with the transport, stepping one column per rows-per-beat subdivision, so timing is tempo-synced. Pixel Y maps to frequency on a logarithmic scale (bottom = 20 Hz, top = Nyquist). Pixel brightness (max of RGB channels) maps through a -90 dB to 0 dB range into linear magnitude.
When the Position CV is connected, it overrides the transport-driven playhead - the CV value selects the column directly as a normalized position across the canvas width. Depth CV scales all output magnitudes (defaults to 1.0 when disconnected).
The Reference input accepts spectral data from another source and renders it as an onion-skin overlay on the canvas, useful for tracing existing sounds. The onion skin can be shifted, captured, and cleared via commands. The canvas pixel buffer is persisted in the project file as an RGBA blob.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Length | length_bars() | Int | 4 | 1..32 |
| Resolution | rows_per_beat() | Int | 16 | 4..64 |
| Loop | loop() | Bool | true |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Reference | Spectral | Spectral data rendered as an onion-skin overlay on the canvas for tracing. The reference's FFT size and sample rate are resampled to the painter's own bin grid, so any upstream FFT configuration works. Only captured while onion recording is active. | |
| Position | ControlNorm | Overrides the transport-driven playhead when connected. The CV value selects the column as a fraction of total canvas width (0.0 = first column, 1.0 = last column). Takes priority over transport position. | |
| Depth | ControlNorm | Scales all output spectral magnitudes. At 0.0 the output is silent regardless of canvas content; at 1.0 magnitudes pass through at full strength. Defaults to 1.0 when disconnected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral Out | Spectral | Spectral frames derived from the painted canvas. One frame is emitted each time the playhead advances to a new column. Magnitudes are zero-phase; connect to an ISTFT or spectral processor downstream for audio output. |
Speech Synth (SpeechSynth)
SpeechSynth)Text-to-speech formant synthesis with enhanced playback features
let node = SpeechSynth.add("my_speechsynth");
Formant speech synthesizer based on the SAM (Software Automatic Mouth) engine. Type text and trigger playback with a gate to produce per-sample speech audio.
Gate triggers playback from the beginning. The Loop property auto-retriggers on completion. Gate Out goes high on play start and low on finish; with Loop on, it emits off+on at each loop boundary.
CV modulation inputs (Speed/Pitch/Mouth/Throat Mod) compute effective values without mutating config properties -- property values restore automatically on disconnect. CV maps 0.0-1.0 across the full parameter range.
External oscillator inputs (F1/F2/F3 Osc) replace the built-in oscillator for that formant. SAM still controls amplitude, frequency tracking, and glottal timing.
Scrub overrides linear playback: CV directly sets frame position. An LFO on Scrub creates cyclic vowel scanning. Freeze holds the current frame while oscillator phases and glottal pulse continue running. Freeze Drift adds random formant frequency wobble while frozen, creating evolving pad textures.
V/Oct overrides glottal pitch while preserving formant shapes (vowel identity). Morph crossfades between Text A and Text B frame data (requires both text inputs connected).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Text | text() | String | hello world | |
| Speed | speed() | Int | 72 | 20..200 |
| Pitch | pitch() | Int | 64 | 0..255 |
| Mouth | mouth() | Int | 128 | 0..255 |
| Throat | throat() | Int | 128 | 0..255 |
| Loop | loop() | Bool | false | |
| Interpolate | interpolate() | Bool | true | |
| Reverse | reverse() | Bool | false | |
| Glottal | glottal_envelope() | Curve | [curve] | |
| Waveform | waveform() | Enum | enum(0) | Classic, Saw, Triangle, Square, Pulse |
| Noise | noise() | Float | 0 | 0..1 |
| Sub | sub() | Float | 0 | 0..1 |
| Voices | voices() | Int | 1 | 1..8 |
| Detune | detune() | Float | 0 | 0..1 |
| Spread | spread() | Float | 0 | 0..1 |
| Scatter | scatter() | Float | 0 | 0..1 |
| Freeze Drift | freeze_drift() | Float | 0 | 0..200 |
| Morph | morph() | Float | 0 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate | Gate | Rising edge starts speech playback from the beginning (or from the end when Reverse is on). Unison voices are staggered with phase offsets to avoid cancellation. | |
| Text A | Data(Speech) | Text data for phrase A. Overrides the Text property when connected; reverts to the property when disconnected. | |
| Text B | Data(Speech) | Text data for phrase B, used as the morph target. Required for the Morph control to have any effect. | |
| Speed Mod | ControlNorm | CV modulation for phoneme playback speed. Maps 0.0-1.0 to the 20-200 range. Computes an effective value without mutating the property; disconnecting restores the property. | |
| Pitch Mod | ControlNorm | CV modulation for voice fundamental pitch. Maps 0.0-1.0 to the 0-255 range. Disconnecting restores the property value. | |
| Mouth Mod | ControlNorm | CV modulation for mouth cavity size. Maps 0.0-1.0 to the 0-255 range. Disconnecting restores the property value. | |
| Throat Mod | ControlNorm | CV modulation for throat cavity size. Maps 0.0-1.0 to the 0-255 range. Disconnecting restores the property value. | |
| V/Oct | VOct | Overrides glottal pitch using V/Oct standard (0V = middle C, ~261.6 Hz). Formant shapes (vowel identity) are preserved while the fundamental pitch follows the CV. | |
| Formant Shift | ControlNorm | Shifts formant frequencies up/down independent of pitch. Maps 0.0-1.0 to -1.0..+1.0 shift range. At 0.5 (center), no shift. Defaults to no shift when disconnected. | |
| Rate | ControlNorm | Playback rate with exponential scaling: 0.0 = 0.25x, 0.5 = normal, 1.0 = 4x. Defaults to normal speed when disconnected. | |
| Scrub | ControlNorm | Overrides linear playback -- CV directly sets the frame position. An LFO on Scrub creates cyclic vowel scanning. Active even when the voice is not playing. | |
| Freeze | Gate | Gate high freezes playback at the current phoneme frame. Oscillator phases and glottal pulse continue running, so the frozen vowel sustains with tonal variation. Resets to normal playback when disconnected. | |
| Scatter Mod | ControlNorm | CV override for scatter amount. Overrides the Scatter property when connected. | |
| Morph Mod | ControlNorm | CV override for A/B text morph position (0.0 = A, 1.0 = B). Overrides the Morph property when connected. | |
| Drift Mod | ControlNorm | CV override for freeze drift rate. Maps 0.0-1.0 to 0-200 Hz. Overrides the Freeze Drift property when connected. | |
| Voices Mod | ControlNorm | CV override for unison voice count (0.0 = 1 voice, 1.0 = 8 voices). Overrides the Voices property when connected. | |
| F1 Osc | Audio | External oscillator replacing the first formant carrier. SAM still controls amplitude, frequency tracking, and glottal timing for this formant. | |
| F2 Osc | Audio | External oscillator replacing the second formant carrier. SAM still controls amplitude, frequency tracking, and glottal timing for this formant. | |
| F3 Osc | Audio | External oscillator replacing the third formant carrier. SAM still controls amplitude, frequency tracking, and glottal timing for this formant. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Synthesized speech audio. Stereo when unison voices are spread, mono-duplicated otherwise. |
| Gate Out | Gate | High while speech is actively playing. With Loop on, emits off+on at each loop boundary for downstream envelope retriggering. |
| F1 Freq | ControlNorm | First formant frequency as normalized CV (0.0-1.0 maps to 0-3000 Hz). Useful for driving external filters or visualizations. |
| F2 Freq | ControlNorm | Second formant frequency as normalized CV (0.0-1.0 maps to 0-3000 Hz). |
| F3 Freq | ControlNorm | Third formant frequency as normalized CV (0.0-1.0 maps to 0-3000 Hz). |
Supersaw (Supersaw)
Supersaw)Multiple detuned sawtooth oscillators for unison lead and pad sounds
let node = Supersaw.add("my_supersaw");
Stack of up to seven detuned sawtooth oscillators for massive unison sounds. The signature trance/EDM lead and pad tone.
Spread sets the total detuning width in cents across all voices; mix crossfades between the center voice alone (0.0) and the full chorus (1.0). When Spread CV is connected it adds cv * 100 cents to the property value; Mix CV adds directly. Both are clamped to their property range. Pitch priority follows the standard chain: MIDI+VOct combined, VOct alone, ControlFreq, then the Frequency knob.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..20000 |
| Voices | voice_count() | Int | 7 | 1..7 |
| Spread | spread() | Float | 25 | 0..100 |
| Mix | mix() | Float | 1 | 0..1 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets pitch, resets all voice phases on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets all voice phases for consistent attack. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Spread Mod | ControlNorm | Adds cv * 100 cents to the Spread property, clamped 0-100. Bypasses the smoother when connected. | |
| Mix Mod | ControlNorm | Adds directly to the Mix property, clamped 0.0-1.0. Bypasses the smoother when connected. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Summed and mixed supersaw audio. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Sync Oscillator (SyncOsc)
SyncOsc)Hard-sync oscillator - aggressive, harmonically rich lead sounds
let node = SyncOsc.add("my_syncosc");
Hard-sync oscillator with a master/slave pair. The slave resets its phase every time the master completes a cycle, producing aggressive, harmonically rich timbres. Sweep the ratio for the classic sync-lead sound.
Frequency sets the master pitch; the slave runs at frequency * ratio. Ratio CV adds cv * 15 to the property value, clamped 1.0-16.0, and bypasses the smoother when connected. Gate and MIDI note-on both reset master and slave phases together (edge-triggered).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..20000 |
| Ratio | ratio() | Float | 2 | 1..16 |
| Waveform | waveform() | Enum | enum(0) | 0..3; Saw, Square, Sine, Triangle |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets master pitch, resets both phases on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets both master and slave phases. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Ratio Mod | ControlNorm | Adds cv * 15 to the Ratio property, clamped 1.0-16.0. Bypasses the smoother when connected. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Slave oscillator audio, periodically reset by the master. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Wavetable Oscillator (Wavetable)
Wavetable)Morphable wavetable oscillator with multiple waveform frames
let node = Wavetable.add("my_wavetable");
Scans through a table of stored waveform frames, interpolating between them for smoothly evolving timbres. Sweep the Position knob to morph through the wavetable; connect an audio signal to FM Input for linear frequency modulation.
FM is linear: the FM Input audio value is multiplied by FM Amount (in Hz) and added directly to the current frequency each sample. Position CV adds to the property value and clamps to 0.0-1.0; when disconnected, the smoothed property value is used. Detune is a static cents offset converted via 2^(cents/1200) and applied as a final frequency multiplier.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 440 | 20..20000 |
| Wavetable | wavetable_type() | Enum | enum(0) | SawStack, PWM, Formant, Harmonic, Digital, Vocal |
| Position | position() | Float | 0.5 | 0..1 |
| Detune | detune() | Float | 0 | -100..100 |
| FM Amount | fm_amount() | Float | 0 | 0..1000 |
| Phase Reset | phase_reset() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | MIDI note data. Sets pitch, resets phase on note-on, and drives Gate/Velocity outputs. | |
| Gate | Gate | Rising edge resets phase for consistent attack. Shares trigger state with MIDI gate. | |
| Frequency Mod | ControlFreq, VOct | Pitch input. VOct offsets MIDI or the Frequency knob exponentially; ControlFreq overrides the knob in Hz. | |
| Position Mod | ControlNorm | Adds to the Position property value, clamped 0.0-1.0. Bypasses the smoother when connected. | |
| FM Input | Audio | Audio-rate modulator for linear FM. Downmixed to mono, scaled by FM Amount, and added to frequency in Hz. | |
| Tuning | Data(Tuning) | Tuning configuration - overrides local divisions/octave. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Interpolated wavetable audio. |
| Gate | Gate | Forwards gate events from MIDI input. Silent when MIDI is disconnected. |
| Velocity | ControlNorm | Last MIDI note velocity as 0.0-1.0. Only emits when MIDI is connected. |
Spectral
Phase Sculptor (PhaseScatter)
Phase Sculptor (PhaseScatter)
Per-bin phase manipulation - scatter, coherence, drift, freeze, transfer, smear
let node = PhaseScatter.add("my_phasescatter");
Per-bin phase manipulation with six modes for reshaping spectral phase relationships.
Scatter: replaces inst_freq with random offsets (regenerated on Gate rising edge). Destroys pitch, creates diffuse textures. Coherence: locks inst_freq to deterministic mathematical patterns (Zero, Linear, Alternating, Quadratic). Creates metallic, robotic, or glassy timbres. Drift: adds a slow random walk to inst_freq per bin, creating evolving detuned textures. Rate controls walk speed, Amount limits max offset. FreezeDrift: captures magnitudes on Gate rising edge (or Freeze toggle), then applies Drift to the frozen spectrum. Gate toggles freeze state. Transfer: crossfades inst_freq toward a Reference spectral input. Holds last received reference when disconnected. Smear: blurs inst_freq across neighboring bins using a triangular kernel of Width radius. Amount at 0 bypasses all modes (passthrough). The Amount Mod and Rate Mod CV inputs add to their respective knob values.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | Scatter, Coherence, Drift, FreezeDrift, Transfer, Smear |
| Amount | amount() | Float | 0 | 0..1 |
| Pattern | pattern() | Enum | enum(0) | Zero, Linear, Alternating, Quadratic |
| Rate | rate() | Float | 1 | 0.01..10 |
| Width | width() | Int | 4 | 1..32 |
| Freeze | freeze() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Primary spectral data to manipulate. Required - produces no output without a connection. | |
| Reference | Spectral | required; Phase source for Transfer mode. The last received frame's inst_freq is held when disconnected, so the transfer effect persists after unplugging. | |
| Gate | Gate | required; Rising edge behavior depends on mode: Scatter regenerates random offsets, FreezeDrift toggles magnitude freeze state. Ignored in other modes. | |
| Amount Mod | ControlNorm | required; CV added to the Amount knob. Combined total is clamped 0-1. | |
| Rate Mod | ControlNorm | required; CV scaled to a 10x range and added to the Rate knob. Only affects Drift and FreezeDrift modes. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Spectral data with manipulated instantaneous frequencies. Magnitudes are unmodified except in FreezeDrift mode where they are replaced by the frozen capture. |
Pitch Correct (SpectralPitchCorrect)
SpectralPitchCorrect)Auto-tune - pitch detection + scale snapping + spectral correction
let node = SpectralPitchCorrect.add("my_spectralpitchcorrect");
Automatic pitch correction (auto-tune). Detects the fundamental pitch via Harmonic Product Spectrum, snaps it to the nearest note in the configured scale, and shifts the spectrum to correct.
Pitch detection runs on the first channel of the last spectral frame. The detected MIDI note is smoothed (0.7 EMA) to filter frame-to-frame jitter, then snapped to the nearest in-scale note. The correction amount is the difference between the snapped target and the smoothed detection. Speed controls convergence: at 0 the correction applies instantly (hard-tune effect), at 1 it glides very slowly (transparent correction). Notes further from the nearest scale tone than Range semitones are left uncorrected. The spectral shift uses the same bin interpolation as Spectral Shift. Detected pitch is output as V/Oct for free pitch tracking. Correction amount is output as bipolar CV (clamped to one semitone).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Speed | speed() | Float | 0 | 0..1 |
| Key | key() | Enum | enum(0) | C, C#, D, D#, E, F, F#, G, G#, A, A#, B |
| Scale | scale() | Enum | enum(0) | Chromatic, Major, Minor, Pentatonic |
| Sensitivity | sensitivity() | Float | 0.1 | 0..1 |
| Range | range() | Float | 2 | 0.5..12 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to pitch-correct. Required - produces no output without a connection. Needs valid sample_rate and fft_size for pitch detection. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Pitch-corrected spectral data with shifted bins and scaled instantaneous frequencies. |
| Pitch | VOct | Detected fundamental pitch as V/Oct. Derived from the raw (unsmoothed) MIDI note: (note - 69) / 12. Useful for pitch tracking even if correction is disabled. |
| Correction | ControlBipolar | Smoothed correction amount as bipolar CV, clamped to the -1..+1 range (one semitone each direction). Useful for visualizing or modulating other parameters based on how far the pitch is being adjusted. |
Spectral Analyze (SpectralAnalyze)
SpectralAnalyze)Audio to spectral data (forward FFT with phase vocoder)
let node = SpectralAnalyze.add("my_spectralanalyze");
Converts stereo audio into spectral data using forward FFT with phase vocoder analysis. This is the entry point for the spectral processing chain.
Connect the output to any spectral effect, then into Spectral Synthesize to return to audio. FFT Size trades frequency resolution for latency: 512 = ~11 ms, 1024 = ~21 ms, 2048 = ~43 ms, 4096 = ~85 ms at 48 kHz. Higher overlap (4x vs 2x) produces smoother results at more CPU cost. Low Freq and High Freq set the analysis range and are baked into every downstream SpectralBuffer so nodes like Spectral Split and Spectral Rotate inherit them automatically. The analyzer rebuilds when FFT Size or Overlap change.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| FFT Size | fft_size_prop() | Enum | enum(2) | S512, S1024, S2048, S4096 |
| Overlap | overlap_prop() | Enum | enum(1) | 2x, 4x |
| Low Freq | low_freq() | Float | 20 | 20..500 |
| High Freq | high_freq() | Float | 20000 | 2000..22000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to analyze. Each channel is processed independently through the phase vocoder. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Spectral magnitude, phase, and instantaneous frequency data. Connect to any spectral effect or directly to Spectral Synthesize. |
Spectral Cross (SpectralCross)
SpectralCross)Morph or vocode between two spectral sources
let node = SpectralCross.add("my_spectralcross");
Morphs between two spectral sources. In Blend mode, crossfades magnitudes, phases, and frequencies for smooth timbral transitions. In Vocoder mode, crossfades magnitudes while locking phase and frequency to input B.
Requires both inputs for crossfading. If only one input is connected, that signal passes through unmodified. Both inputs must share the same bin count - a mismatch produces silence with a logged error. Mix at 0.0 gives pure A; at 1.0 gives pure B. The Mix Mod CV input adds to the Mix knob (clamped 0-1). Output Gain is applied to the crossfaded magnitudes. In Vocoder mode, A provides the spectral envelope (magnitude) and B provides the pitch structure (phase + inst_freq), enabling spectral vocoding at full FFT resolution without band quantization.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mix | mix() | Float | 0.5 | 0..1 |
| Mode | mode() | Enum | enum(0) | Blend, Vocoder |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral A | Spectral | required; First spectral source. Provides the spectral envelope (magnitudes) in Vocoder mode. Passes through if B is disconnected. | |
| Spectral B | Spectral | required; Second spectral source. Provides the pitch structure (phase + inst_freq) in Vocoder mode. Passes through if A is disconnected. Must match A's bin count. | |
| Mix Mod | ControlNorm | required; CV added to the Mix knob. Combined total is clamped 0-1. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Blended or vocoded spectral data. Uses A's metadata (FFT size, sample rate, frequency range) for the output buffer. |
Spectral Delay (SpectralDelay)
SpectralDelay)Per-bin frequency-dependent delay with feedback
let node = SpectralDelay.add("my_spectraldelay");
Delays individual frequency bins independently, creating "frequency waterfall" effects impossible with conventional delay.
Each bin has its own delay line sized from the hop rate. The Time property sets the base delay for all bins. Spread offsets delay time linearly across the spectrum: positive values delay low frequencies more (bin_frac < 0.5 gets added time), negative values delay high frequencies more. Feedback mixes the delayed magnitude back into the delay line tail, building resonant spectral echoes (capped at 0.95 to prevent runaway). Phases and instantaneous frequencies pass through undelayed. The Time Mod CV input scales 0-1 to 0-2 seconds and adds to the Time knob. Delay lines rebuild whenever the upstream bin count or channel count changes.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Time | time() | Float | 0.5 | 0.01..2 |
| Spread | spread() | Float | 0 | -1..1 |
| Feedback | feedback() | Float | 0 | 0..0.95 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to delay. Required - produces no output without a connection. | |
| Time Mod | ControlNorm | required; CV added to the Time knob. 0-1 maps to 0-2 seconds. Combined total is clamped to the valid delay range. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Spectral data with per-bin delayed magnitudes. Phases and instantaneous frequencies are undelayed. |
Spectral Denoise (SpectralDenoise)
SpectralDenoise)Noise profile capture + spectral subtraction
let node = SpectralDenoise.add("my_spectraldenoise");
Removes steady-state noise via spectral subtraction. Capture a noise profile during a noise-only passage, then the node continuously subtracts that profile from the signal.
Workflow: connect Signal, optionally connect Reference, trigger Learn Gate (or toggle Learn) during noise-only audio, then release to apply. The noise profile is accumulated using EMA smoothing from the Reference input - or from Signal if Reference is disconnected. Subtraction always applies to Signal. Each bin's magnitude is reduced by (noise_profile * Reduction) and clamped to Floor to prevent musical noise artifacts. Before a profile is captured, Signal passes through unchanged. Phases and instantaneous frequencies are unmodified.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Reduction | reduction() | Float | 1 | 0..2 |
| Smoothing | smoothing() | Float | 0.8 | 0..1 |
| Floor | floor() | Float | 0.001 | 0..0.1 |
| Learn | learn() | Bool | false | |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Signal | Spectral | required; Spectral signal to denoise. Subtraction is applied here. Required - produces no output without a connection. Passes through unchanged before a noise profile is learned. | |
| Reference | Spectral | required; Noise reference source for learning. When disconnected, learning captures from Signal instead. Connect a separate noise-only source here for cleaner profiles. | |
| Learn Gate | Gate | required; Rising edge starts learning, falling edge stops. Either the gate or the Learn toggle can activate learning independently. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Denoised spectral data with noise profile subtracted from magnitudes. Phases and instantaneous frequencies are unmodified. |
Spectral Freeze (SpectralFreeze)
SpectralFreeze)Gate captures the current spectrum and holds it indefinitely
let node = SpectralFreeze.add("my_spectralfreeze");
Captures the current spectrum and holds it indefinitely, producing a sustained drone from any input.
On the rising edge of the Freeze toggle or Gate input, magnitudes from the first available frame are captured and held. While frozen, these captured magnitudes replace the live signal's magnitudes every frame. Phases and instantaneous frequencies pass through unmodified, so the downstream synthesizer's phase accumulator keeps evolving - producing a naturally shifting texture rather than a static buzz. Output Gain is applied to magnitudes during freeze; when not frozen, signal passes through unchanged. The Gate input overrides the Freeze toggle: a rising edge activates freeze regardless of the toggle state.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Freeze | freeze() | Bool | false | |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to freeze. Passes through unmodified when not frozen. Required - produces no output without a connection. | |
| Gate | Gate | required; Rising edge captures the current spectrum and activates freeze. Overrides the Freeze toggle - freeze stays active as long as either the gate or toggle is high. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Frozen spectral data: captured magnitudes with live phases and instantaneous frequencies from the input signal. |
Spectral Granular (SpectralGranular)
SpectralGranular)Spectral-domain granular processor with frame buffer and pitch shifting
let node = SpectralGranular.add("my_spectralgranular");
Spectral-domain granular processor. Captures spectral frames into a circular buffer and replays them as overlapping grains with per-grain pitch shifting.
Sits in the standard Analyze -> Effect -> Synthesize chain. Grains are scheduled at the Density rate and last for Size spectral frames. Each grain reads from a jittered Position in the frame buffer with pitch shifting via bin interpolation (same technique as Spectral Shift). Multiple grains are mixed by summing magnitudes (divided by active grain count) with winner-take-all phase selection - the grain with the highest magnitude at each bin determines the output phase and instantaneous frequency. Grain envelopes use a raised cosine window controlled by Blend (0 = rectangular, 1 = maximum fade). The Freeze gate stops recording into the buffer, letting grains play from a fixed snapshot. When the buffer is empty, input passes through directly.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Buffer | buffer_frames() | Int | 128 | 32..512 |
| Density | density() | Float | 8 | 0.5..50 |
| Size | size() | Int | 16 | 4..64 |
| Position | position() | Float | 0 | 0..1 |
| Pitch | pitch() | Float | 0 | -24..24 |
| Jitter | jitter() | Float | 0.1 | 0..1 |
| Max Grains | max_grains() | Int | 8 | 1..32 |
| Blend | blend() | Float | 0.5 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Source spectral data, continuously recorded into the frame buffer. Required - passes through directly when the buffer is empty. | |
| Freeze | Gate | required; Rising edge freezes buffer recording; falling edge resumes. Grains continue playing from the frozen buffer contents. | |
| Position Mod | ControlNorm | required; CV added to the Position knob. Combined total is clamped 0-1. | |
| Density Mod | ControlNorm | required; CV scaled to the density range and added to the Density knob. Combined total is clamped to valid bounds. | |
| Size Mod | ControlNorm | required; CV scaled to a 60-frame range and added to the Size property. Combined total is clamped to valid bounds. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral Out | Spectral | Mixed grain output: summed magnitudes (normalized by active grain count) with winner-take-all phase selection. |
Spectral Join (SpectralJoin)
SpectralJoin)Recombines per-band spectral data from Spectral Split
let node = SpectralJoin.add("my_spectraljoin");
Recombines per-band spectral data into a single full spectrum. The dual of Spectral Split - connect processed band outputs here to merge them back into one spectral stream for Spectral Synthesize.
For each bin, the input with the highest magnitude wins (winner-take-all): phase and instantaneous frequency come from that same input. When bands from Spectral Split do not overlap, each bin comes from exactly one input with no ambiguity. The output uses metadata (FFT size, sample rate, frequency range) from the first connected input.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Bands | bands() | Int | 4 | 1..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Band | Spectral | unbounded; required; Per-band spectral data from Spectral Split or per-band effects. Only connected inputs contribute; disconnected ports are skipped. When no inputs are connected, no output is produced. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Merged full-spectrum spectral data ready for Spectral Synthesize or further spectral effects. |
Spectral Mask (SpectralMask)
SpectralMask)Drawable per-bin frequency filter with live spectrum overlay
let node = SpectralMask.add("my_spectralmask");
Drawable frequency filter that multiplies each bin's magnitude by a user-drawn curve. Sculpt the spectrum to boost, cut, or isolate any frequency region.
The mask curve is drawn on a logarithmic frequency axis (20 Hz - 20 kHz) in the Band Curve Editor. Each FFT bin is mapped to its LogFreqT position and the curve is evaluated there, so the resolution follows the FFT size. Values above 1.0 boost that frequency region; 0.0 silences it. The curve is recomputed whenever upstream FFT parameters change (bin count, sample rate, FFT size). A live spectrum overlay displays the first channel's magnitudes in real time. Output Gain is multiplied into each bin alongside the mask value.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mask | mask() | Curve | [curve] | |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to filter. Required - produces no output without a connection. Provides FFT parameters used to map curve positions to frequency bins. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Spectral data with per-bin magnitudes scaled by the mask curve and Output Gain. Phases and instantaneous frequencies are unmodified. |
Spectral Rotate (SpectralRotate)
SpectralRotate)Circular bin rotation on the spectral bus
let node = SpectralRotate.add("my_spectralrotate");
Circular rotation of FFT bins by band-aligned offsets. Creates inharmonic, metallic textures or repositions the spectrum before Spectral Split.
Uses the same log-spaced band mapping as Spectral Split (derived from the upstream SpectralBuffer's frequency range and FFT size), so rotating by N shifts by exactly N Split bands worth of bins. Rotation wraps circularly - bins shifted past Nyquist reappear at DC. Magnitudes are scaled by Output Gain during rotation. Phases and instantaneous frequencies move with their bins unmodified. The Rotate Mod input is bipolar, scaled to the band count, and added to the Rotate property. Zero rotation passes through without processing.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Bands | bands() | Int | 8 | 2..64 |
| Rotate | rotate() | Int | 0 | -64..64 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to rotate. Required - produces no output without a connection. | |
| Rotate Mod | ControlBipolar | required; Bipolar CV scaled to the band count and added to Rotate. -1 rotates by -N bands, +1 rotates by +N bands. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Spectral data with circularly rotated bins, magnitudes, phases, and instantaneous frequencies. |
Spectral Shift (SpectralShift)
SpectralShift)Pitch shift via spectral bin shifting (±24 semitones)
let node = SpectralShift.add("my_spectralshift");
Pitch-shifts the spectrum by moving FFT bins up or down without changing playback speed.
Computes a frequency ratio from the semitone shift (2^(st/12)), then for each output bin, reads from a fractional source bin using linear interpolation on magnitudes. Instantaneous frequencies are taken from the nearest source bin and scaled by the ratio. Bins that would read beyond the spectrum stay zeroed. When shift is near zero and gain is unity, the signal passes through without processing. The Shift Mod input adds a bipolar offset scaled to the full range.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Shift | shift() | Float | 0 | -24..24 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to pitch-shift. Required - produces no output without a connection. | |
| Shift Mod | ControlBipolar | required; Bipolar CV added to the Shift value. -1 maps to -24 st, +1 maps to +24 st. Combined total is clamped to the full range. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Pitch-shifted spectral data with interpolated magnitudes and scaled instantaneous frequencies. |
Spectral Smear (SpectralSmear)
SpectralSmear)Temporal magnitude smoothing for ambient textures
let node = SpectralSmear.add("my_spectralsmear");
Applies temporal smoothing to per-bin magnitudes, blurring the spectrum over time. Low values add subtle sustain; high values create evolving ambient washes.
Uses an exponential moving average per bin: each frame blends the stored average with the incoming magnitude using the Smear factor as the EMA coefficient. At maximum, approaches infinite hold similar to Spectral Freeze but with gradual accumulation rather than a hard capture. When Smear is near zero, averages are reset and signal passes through. Phases and instantaneous frequencies are unmodified. The Smear Mod CV input adds to the Smear knob value (clamped 0-1).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Smear | smear() | Float | 0 | 0..1 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Spectral data to smooth. Required - produces no output without a connection. | |
| Smear Mod | ControlNorm | required; CV added to the Smear knob value. Combined total is clamped 0-1. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Spectral | Spectral | Temporally smoothed spectral data with EMA-blended magnitudes and original phases. |
Spectral Split (SpectralSplit)
SpectralSplit)Partitions spectral data into N frequency bands
let node = SpectralSplit.add("my_spectralsplit");
Partitions spectral data into N logarithmically-spaced frequency bands. Patch any effect between Split and Join to make it spectral - delay becomes spectral delay, distortion becomes spectral distortion.
Each output carries a SpectralBuffer with only that band's bins populated (all others zeroed). Pure spectral routing - no FFT/IFFT happens here. The typical chain is Spectral Analyze -> Split -> per-band effects -> Join -> Spectral Synthesize. Band boundaries use log-frequency spacing derived from the upstream Analyze node's Low Freq, High Freq, and FFT Size. Inherent latency equals the upstream FFT size in samples (~21 ms at 1024/48 kHz).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Bands | bands() | Int | 8 | 2..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | required; Full-spectrum spectral data from Spectral Analyze or another spectral effect. Provides bin count, frequency range, and sample rate used to compute band boundaries. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Band | Spectral | unbounded; One output per band, each containing only the bins for that frequency region. Connect to per-band effects, then into Spectral Join to recombine. |
Spectral Synthesize (SpectralSynthesize)
SpectralSynthesize)Spectral data to audio (inverse FFT with phase vocoder)
let node = SpectralSynthesize.add("my_spectralsynthesize");
Converts spectral data back into stereo audio using inverse FFT with overlap-add reconstruction. This is the exit point of the spectral processing chain.
Accepts one or many spectral inputs and produces a matching number of stereo audio outputs. Each input/output pair runs its own synthesizer, but all share FFT plans for efficiency. Synthesizers are lazily created on first data - FFT parameters (size, overlap) come from the upstream SpectralBuffer. Set the Channels property to match the number of spectral inputs you intend to connect.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Channels | channels() | Int | 4 | 1..64 |
| Output Gain | output_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Spectral | Spectral | unbounded; required; Spectral data from Spectral Analyze, spectral effects, or Spectral Split bands. Each connected input creates a dedicated synthesizer. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | unbounded; Reconstructed stereo audio. Output index matches the corresponding spectral input index. |
Utility
Audio ➔ Control (AudioToControl)
Audio ➔ Control (AudioToControl)
Converts audio-rate signals to control-rate by sampling first sample of buffer
let node = AudioToControl.add("my_audiotocontrol");
Samples the first stereo frame of each audio buffer, downmixes to mono ((L+R)/2), and outputs the result as a control-rate value.
Only the first sample of each buffer is used -- subsequent samples are ignored. The output updates once per buffer at whatever the graph's block size is. Useful for driving parameters from audio-rate signals like envelopes or LFOs running through the audio bus.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to sample. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Control Out | ControlNorm | Mono-downmixed amplitude as a normalized control value. |
Burst Generator (BurstGenerator)
BurstGenerator)Single trigger → N evenly-spaced trigger pulses with acceleration
let node = BurstGenerator.add("my_burstgenerator");
Fires a rapid burst of gate pulses from a single trigger.
Each rising edge on Trigger starts a burst of Count pulses at the configured Rate. Pulses have a 50% duty cycle. Acceleration divides each successive interval: >1.0 speeds up (accelerando), <1.0 slows down (ritardando), 1.0 is even spacing. A retrigger during an active burst restarts from the beginning. The minimum interval is clamped at 5 ms to prevent degenerate pulse widths.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Count | count() | Int | 4 | 1..32 |
| Rate | rate() | Float | 10 | 1..100 |
| Accel | acceleration() | Float | 1 | 0.25..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Trigger | Gate | required; Rising edge starts a new burst. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | Burst pulse train output. |
Clipper (Clipper)
Clipper)Audio clipping and saturation with hard and soft modes
let node = Clipper.add("my_clipper");
Clips or saturates a stereo audio signal. Each stereo channel is processed independently per sample.
In Hard mode the signal hits a symmetric brick wall at +/- threshold. In Soft mode tanh saturation provides a warmer overdrive character that smoothly approaches the ceiling. The threshold is per-sample smoothed to prevent clicks when adjusted. Lower threshold values clip harder.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Threshold | threshold() | Float | 0.95 | 0.1..1 |
| Mode | mode() | Enum | enum(1) | Hard, Soft |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to clip. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Clipped stereo audio. |
Clock (Clock)
Clock)Generates precise timing gate signals
let node = Clock.add("my_clock");
Free-running clock source that emits gate pulses at a configurable frequency.
Each cycle begins with the gate going high for the pulse width duration, then low for the remainder of the period. The pulse width is capped at 90% of the period to guarantee a falling edge even at high frequencies. A Rate Mod CV replaces the Frequency property with an exponential mapping from 0--1 across 0.01--1000 Hz. A Reset rising edge zeros the phase for sync.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Frequency | frequency() | Float | 1 | 0.01..1000 |
| Pulse Width | pulse_width() | Float | 10 | 1..100 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Rate Mod | ControlNorm | Replaces the Frequency property with an exponential mapping: 0.0 = 0.01 Hz, 1.0 = 1000 Hz. | |
| Reset | Gate | Rising edge resets the clock phase to zero. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate Out | Gate | Clock pulse output. |
Clock Div/Mult (ClockDivider)
ClockDivider)Divide or multiply incoming clock triggers by an integer ratio
let node = ClockDivider.add("my_clockdivider");
Divides or multiplies an incoming clock by an integer ratio.
In Divide mode, a rising edge passes through every N-th input trigger; falling edges only pass when the counter is at zero. In Multiply mode, the node measures the period between incoming triggers and emits N evenly-spaced subdivisions within that period, each with a 50% duty cycle. The first incoming trigger establishes the period -- no subdivisions are emitted until the second trigger arrives. A Reset rising edge zeros the counter.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | Divide, Multiply |
| Ratio | ratio() | Int | 2 | 1..32 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Clock | Gate | required; Incoming clock trigger to divide or multiply. | |
| Reset | Gate | Rising edge resets the internal counter. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | Divided or multiplied clock output. |
Comparator (Comparator)
Comparator)Threshold comparison on continuous signals, outputs gate
let node = Comparator.add("my_comparator");
Compares a continuous control signal against a threshold and outputs a gate. Schmitt trigger hysteresis prevents chattering.
The hysteresis creates a dead band: for > and >= modes, the gate turns on when the input exceeds threshold + hysteresis/2 and stays on until it drops below threshold - hysteresis/2 (and vice versa for < and <=). A Threshold Mod CV replaces the property value when connected. The input defaults to 0.0 when disconnected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Compare | op() | Enum | enum(0) | >, <, >=, <= |
| Threshold | threshold() | Float | 0.5 | -1..1 |
| Hysteresis | hysteresis() | Float | 0.01 | 0..0.2 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | ControlBipolar | required; Signal to compare against the threshold. | |
| Threshold | ControlBipolar | Replaces the Threshold property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | High when the comparison is true, low otherwise. |
Constant (Constant)
Constant)Outputs a constant value as configuration data
let node = Constant.add("my_constant");
Outputs a fixed value as structured data on every buffer.
Set the type (Number, String, Bool, FilePath, or Resource) and the corresponding value, then wire the Data output to any node that expects configuration. Changing the type resets the value to a default for that type. The output is a boxed ConstantValue enum on a Data port.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Type | value_type() | Enum | enum(0) | Number, String, Bool, FilePath, Resource |
| Number | number_value() | Float | 0 | -1000000..1000000 |
| String | string_value() | String | ||
| Bool | bool_value() | Bool | false | |
| File Path | file_path_value() | String | ||
| Resource | resource_value() | String |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Data Out | Data(Unknown) | The constant value as structured data. |
Control Converter (ControlConverter)
ControlConverter)Converts between control signal types with proper range mapping
let node = ControlConverter.add("my_controlconverter");
Accepts any control-family signal and produces correctly converted outputs for every signal type simultaneously.
The node detects the source port type at process time and applies proper conversion math. Norm uses linear 0--1 mapping; Bipolar maps -1..1 to 0..1 linearly; ControlFreq uses log-scale normalization across 20--20000 Hz. Generic Control is treated as already 0--1 for Norm and clamped to -1..1 for Bipolar.
Audio output is dual-mono stereo from the normalized value. V/Oct is derived by converting through frequency first (relative to middle C at 261.63 Hz). Gate goes high when the normalized value reaches 0.5 and low when it drops below, with edge detection. The raw Control output passes through unchanged.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Control, ControlNorm, ControlBipolar, ControlFreq | required; Any control-family signal (Control, Norm, Bipolar, Freq). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Control | Control | Raw input value passed through with no conversion. |
| Norm | ControlNorm | Normalized 0.0--1.0 output. |
| Bipolar | ControlBipolar | Bipolar -1.0--1.0 output. |
| Freq | ControlFreq | Frequency in Hz. Norm/Bipolar sources are exponentially mapped across the audible range; ControlFreq is clamped; generic Control is treated as Hz and clamped. |
| Audio | Audio | Dual-mono stereo audio from the normalized value. |
| VOct | VOct | V/Oct pitch CV derived from frequency conversion. |
| Gate | Gate | Gate high when normalized value >= 0.5. |
Control Mapper (ControlMapper)
ControlMapper)Maps control signals from one range to another
let node = ControlMapper.add("my_controlmapper");
Linearly rescales a control signal from one range to another.
The input value is normalized against the Input Min/Max range, then mapped to the Output Min/Max range. If the input range has zero width the output defaults to Output Min. With Clamp enabled, the output is constrained to the output bounds. With Clamp off, values outside the input range extrapolate beyond the output range. Inverted ranges (min > max) are supported for reverse mapping.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Input Min | input_min() | Float | 0 | -1000..1000 |
| Input Max | input_max() | Float | 1 | -1000..1000 |
| Output Min | output_min() | Float | 0 | -10000..10000 |
| Output Max | output_max() | Float | 1 | -10000..10000 |
| Clamp | clamp() | Bool | true |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Control In | Control | required; Control signal to remap. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Control Out | Control | Rescaled control signal. |
Control Math (ControlMath)
ControlMath)Two-input control math: add, subtract, multiply, min, max, crossfade
let node = ControlMath.add("my_controlmath");
Two-input math on control-rate signals. Combine, compare, or crossfade a pair of normalized control values.
Disconnected inputs default to 0.0. Add and Subtract results are clamped to 0.0--1.0. Multiply, Min, and Max are unclamped. Crossfade uses the Mix property: 0.0 = all A, 1.0 = all B. Unlike SignalMathNode this operates at control rate (one value per buffer, no per-sample smoothing on Mix).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Operation | op() | Enum | enum(0) | Add, Subtract, Multiply, Min, Max, Crossfade |
| Mix | mix() | Float | 0.5 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| A | ControlNorm | First control operand (0.0--1.0). | |
| B | ControlNorm | Second control operand (0.0--1.0). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Out | ControlNorm | Result of the selected operation (0.0--1.0). |
Control Scale (ControlScale)
ControlScale)Scales normalized control (0-1) to frequency range in Hz
let node = ControlScale.add("my_controlscale");
Scales a normalized 0--1 control value to a frequency range in Hz using linear interpolation between Min Hz and Max Hz.
The input is clamped to 0--1 before scaling. Min Hz must be less than Max Hz. Useful for converting LFO or knob output into a frequency modulation signal for oscillators and filters.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Min Hz | min_hz() | Float | 20 | 20..20000 |
| Max Hz | max_hz() | Float | 20000 | 20..20000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Control In | ControlNorm | required; Normalized control input (0.0--1.0). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Frequency Out | ControlFreq | Scaled frequency in Hz. |
Control Slew (ControlSlew)
ControlSlew)Smooth control signal transitions with independent rise and fall rates
let node = ControlSlew.add("my_controlslew");
Rate-limiter for control signals with independent rise and fall times.
Smooths abrupt parameter changes into gradual transitions. The slew operates at control rate (once per buffer): the maximum change per buffer is buffer_duration / slew_time. Rise time governs upward movement, fall time governs downward. At 0 seconds the output tracks the input instantly. Output is clamped to 0--1. On the first buffer, the output jumps to the input value with no slew. Useful for lag processing, portamento on control values, or envelope-like shaping of knob movements.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Rise Time | rise_time() | Float | 0.1 | 0..10 |
| Fall Time | fall_time() | Float | 0.1 | 0..10 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Control In | ControlNorm | required; Control signal to smooth. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Control Out | ControlNorm | Smoothed control output (0.0--1.0). |
Crossfade (Crossfade)
Crossfade)Smooth blend between two audio inputs
let node = Crossfade.add("my_crossfade");
Smooth blend between two audio inputs with per-sample smoothing on the position parameter.
Linear mode: gain_A = 1-pos, gain_B = pos. Equal-power mode uses sin/cos curves for constant loudness across the crossfade range. A Position Mod CV replaces the property value when connected; when disconnected the smoother tracks the property. Disconnected audio inputs contribute silence, so a single connected input acts as a fade in/out. Both inputs disconnected reports Silent.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Position | position() | Float | 0 | 0..1 |
| Curve | curve() | Enum | enum(0) | Linear, EqualPower |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input A | Audio | Audio input A (heard at Position 0.0). | |
| Input B | Audio | Audio input B (heard at Position 1.0). | |
| Position | ControlNorm | CV control over crossfade position (0.0 = A, 1.0 = B). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | Blended audio output. |
Curve Function (CurveFunctionGenerator)
CurveFunctionGenerator)Drawable signal→signal transfer function for waveshaping and CV remapping
let node = CurveFunctionGenerator.add("my_curvefunctiongenerator");
Drawable signal-to-signal transfer function. Maps audio or control input through a user-drawn spline curve.
For audio, each sample is driven by InputGain, clamped to -1..1, normalized to 0..1 for the curve lookup, then denormalized back to -1..1 and scaled by OutputGain. The Mix knob blends the shaped (wet) signal with the original (dry) per-sample with smoothing.
For control, the input is evaluated through the curve once per buffer without smoothing. InputGain and OutputGain only affect the audio path. Both audio and control paths can operate simultaneously through the same curve. A Mix CV overrides the property value when connected.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Curve | curve_data() | Curve | [curve] | |
| Input Gain | input_gain() | Float | 1 | 0.1..4 |
| Output Gain | output_gain() | Float | 1 | 0.1..4 |
| Mix | mix() | Float | 1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Audio signal for per-sample waveshaping. | |
| Control | ControlNorm | required; Control signal for CV remapping (evaluated once per buffer). | |
| Mix CV | ControlNorm | required; Modulates dry/wet mix. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Shaped audio output. |
| Control | ControlNorm | Shaped control output. |
Debug (Debug)
Debug)Comprehensive debug node for testing all proc macro features
let node = Debug.add("my_debug");
Diagnostic probe for inspecting signals in the graph. Accepts any signal type on unbounded inputs.
In Passthrough mode, the first connected input is forwarded to the output unchanged; remaining inputs are ignored. In Monitor and Analyze modes, all inputs are consumed (force-pulled to keep upstream nodes processing) but the output is silence. When Enabled is on, the node periodically emits a debug_tick custom event and increments the read-only EmitCount property at the configured interval. The output port type locks to the first connected input type.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Inputs | inputs() | Int | 4 | 1..64 |
| Emit Interval | emit_interval() | Float | 1 | 0..10 |
| Emit Count | emit_count() | Int | 0 | 0..1000000 |
| Enabled | enabled() | Bool | true | 0..1 |
| Mode | mode() | Enum | enum(0) | Passthrough, Monitor, Analyze |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | gain() | unbounded; Any signal type -- connect as many sources as needed. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | First input forwarded (Passthrough mode) or silence (Monitor/Analyze). |
Discontinuity Detector (DiscontinuityDetector)
DiscontinuityDetector)Detects and logs sudden jumps in audio signal (clicks/pops)
let node = DiscontinuityDetector.add("my_discontinuitydetector");
Monitors an audio stream for sudden sample-to-sample jumps that indicate clicks, pops, or buffer-boundary artifacts.
Computes the absolute difference between consecutive samples on each stereo channel independently and reports when the larger of the two exceeds the threshold. When Log to Console is enabled, each detection emits a tracing warning with the sample offset and per-channel deltas. Pass Through controls whether audio reaches the output or is replaced with silence, allowing silent monitoring. Internal counters track total detections and maximum discontinuity magnitude.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Threshold | threshold() | Float | 0.1 | 0.001..1 |
| Log to Console | log_to_console() | Bool | true | |
| Pass Through | pass_through() | Bool | true |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Audio signal to monitor for discontinuities. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Pass-through audio (or silence if pass-through is off). |
Euclidean Rhythm (EuclideanRhythm)
EuclideanRhythm)Evenly distributed rhythmic patterns via the Bjorklund algorithm
let node = EuclideanRhythm.add("my_euclideanrhythm");
Distributes N hits as evenly as possible across M steps using the Bjorklund algorithm.
On each clock rising edge the pattern advances one step. If the current step is a hit and the gate is low, a rising edge is emitted. On a clock falling edge the gate always goes low. This means the output gate width matches the input clock pulse width. A Reset rising edge returns the step counter to zero. Rotation shifts the pattern start position. The pattern recomputes whenever Hits, Steps, or Rotation changes.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Hits | hits() | Int | 3 | 0..32 |
| Steps | steps() | Int | 8 | 1..32 |
| Rotation | rotation() | Int | 0 | 0..31 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Clock | Gate | required; Clock input -- each rising edge advances the pattern one step. | |
| Reset | Gate | Rising edge resets the step counter to zero. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | Gate high on hit steps, low on rests. |
Fade (Fade)
Fade)Gate-triggered audio fade in/out
let node = Fade.add("my_fade");
Smoothly fades audio in and out with independent fade times.
Two trigger modes: the Level input (ControlNorm) takes priority when connected -- values at or above the Gate Threshold trigger fade in, below triggers fade out. This lets Arranger track outputs wire directly without a converter. When Level is not connected, the Gate input drives the fade via edge detection.
The fade is linear per-sample: each sample advances the internal gain by 1/(fade_time * sample_rate). When fully faded out with the gate low, the node reports Silent for optimization.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Fade In | fade_in() | Float | 100 | 0.5..5000 |
| Fade Out | fade_out() | Float | 100 | 0.5..5000 |
| Gate Threshold | gate_threshold() | Float | 0.5 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Audio signal to fade. | |
| Gate | Gate | Gate input - rising edge triggers fade in, falling edge triggers fade out. Ignored when the Level input is connected. | |
| Level | ControlNorm | ControlNorm level input. When connected, overrides the Gate input. Values at or above the Gate Threshold trigger fade in; below triggers fade out. Wire Arranger track outputs here directly. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Faded audio output. |
Flip-Flop (FlipFlop)
FlipFlop)Toggles between two audio inputs on gate trigger with smooth crossfading
let node = FlipFlop.add("my_flipflop");
Routes one of two audio inputs to the output, toggling on each gate trigger with smooth crossfading.
Gate priority: Set forces A (active_b = false), Reset forces B (active_b = true), Toggle flips the state. All three are evaluated per buffer in that order. A Crossfade CV overrides the toggle state entirely when connected: 0.0 = A, 1.0 = B, with intermediate values blending. The State gate output tracks the toggle state (high = B active). Disconnected audio inputs contribute silence.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Crossfade | crossfade_time() | Float | 5 | 0..1000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| A | Audio | First audio source (selected when toggle is off). | |
| B | Audio | Second audio source (selected when toggle is on). | |
| Toggle | Gate | Rising edge flips between A and B. | |
| Set | Gate | Rising edge forces output to A (clears active_b). | |
| Reset | Gate | Rising edge forces output to B (sets active_b). | |
| Crossfade | ControlNorm | CV mix override (0.0 = A, 1.0 = B). Overrides toggle state. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Crossfaded audio from A and B. |
| State | Gate | Gate high when B is active, low when A is active. |
Gain (Gain)
Gain)Amplify or attenuate audio signal
let node = Gain.add("my_gain");
Simple volume control for a stereo audio signal with per-sample smoothing to avoid clicks.
The Gain Mod CV input, when connected, replaces the property value: ControlNorm 0.0--1.0 maps to gain 0.0--2.0. When disconnected the smoother tracks the property value. Both paths go through the same smoother so transitions are always clean.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Gain | amount() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to amplify or attenuate. | |
| Gain Mod | ControlNorm | Replaces the property value. 0.0 = silence, 0.5 = unity, 1.0 = 2x. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Gained stereo audio. |
Gate Converter (GateConverter)
GateConverter)Converts gate events to Audio or Control signals based on output connection.
let node = GateConverter.add("my_gateconverter");
Converts gate events into Audio, Control, or Gate output depending on what the output port is connected to.
The conversion target is set automatically when the output is wired. Audio mode produces stereo dual-mono 0.0/1.0 steps with instant transitions on gate edges. Control mode applies one-pole attack/release smoothing from 0.0 to 1.0 -- the envelope advances by one sample per buffer, so attack/release times interact with buffer size at very short settings. Gate mode passes events through unchanged. When disconnected, the output reverts to accepting any of the three types.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Attack | attack() | Float | 5 | 0.1..1000 |
| Release | release() | Float | 100 | 1..5000 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate In | Gate | required; Gate events to convert. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio, Control, Gate | Converted signal (Audio, Control, or Gate based on connection). |
Gate Curve (GateCurve)
GateCurve)Transport-synced gate pattern from a drawn curve
let node = GateCurve.add("my_gatecurve");
Transport-synced gate pattern from a drawn curve. Outputs gate edges at threshold crossings and the raw curve value as a control signal.
The curve is evaluated once per buffer via advance_time against the transport beat position, looping over the configured bar length. The Gate output goes high when the curve value is >= Threshold. The default curve is flat at 1.0 (constant gate high). No inputs; position comes entirely from the transport.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Curve | curve_data() | Curve | [curve] | |
| Length | length_bars() | Int | 4 | 1..64 |
| Loop | loop() | Bool | true | |
| Threshold | threshold() | Float | 0.5 | 0..1 |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | Edge-detected gate high when the curve value is >= Threshold. |
| Level | ControlNorm | Raw curve value clamped to 0.0-1.0, independent of the Threshold setting. |
Gate Logic (GateLogic)
GateLogic)Boolean logic on two gate inputs (AND, OR, XOR, NOT)
let node = GateLogic.add("my_gatelogic");
Boolean logic on two gate inputs. All five outputs (AND, OR, XOR, NOT A, NOT B) are computed simultaneously every buffer.
Unconnected inputs default to low (false). Gate edges are emitted only on state transitions, so outputs stay silent when the logic result does not change. The node tracks the last event on each input -- if multiple events arrive in one buffer, only the final state matters for the logic evaluation.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| A | Gate | First gate operand. | |
| B | Gate | Second gate operand. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| AND | Gate | High only when both A and B are high. |
| OR | Gate | High when either A or B is high. |
| XOR | Gate | High when exactly one of A or B is high. |
| NOT A | Gate | Inverted A -- high when A is low. |
| NOT B | Gate | Inverted B -- high when B is low. |
Gate Router (ProbabilisticGateRouter)
ProbabilisticGateRouter)Routes each gate to one of N outputs by probability, round-robin, or random
let node = ProbabilisticGateRouter.add("my_probabilisticgaterouter");
Routes each incoming gate to exactly one of N outputs based on probability weights, round-robin order, or uniform random selection.
Each gate-on selects a target output; the corresponding gate-off is always sent to the same output. In Weighted mode, per-output Weight properties control relative probability; a CV input shifts probability toward the last output. In RoundRobin mode, outputs are cycled in order regardless of weights. In Random mode, each output has equal probability regardless of weights.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Outputs | outputs() | Int | 2 | 2..16 |
| Mode | mode() | Enum | enum(0) | Weighted, RoundRobin, Random |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Clock | Gate | required; Incoming gate events to distribute. | |
| CV | ControlNorm | required; Modulates weight distribution in Weighted mode. At 1.0, all probability shifts to the last output. Ignored in other modes. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | unbounded |
Grain Scheduler (GrainScheduler)
GrainScheduler)Stochastic MIDI grain triggers for modular granular synthesis
let node = GrainScheduler.add("my_grainscheduler");
Stochastic MIDI grain trigger generator. Emits overlapping note-on / note-off pairs at a configurable density.
When MIDI In is disconnected, note numbers cycle round-robin through base_note..base_note+Voices-1, mapping to Voice Mixer slots. When MIDI In is connected, held notes form a note pool and the scheduler cycles through those instead -- if the pool is empty no grains fire.
Clock input overrides density: one grain per rising edge. Density and Size Mod CVs are additive to the property values. Jitter randomizes spawn timing; Size Jitter randomizes grain duration; Velocity Jitter randomizes note velocity. In Chord mode all voice slots fire simultaneously per grain with optional stagger spread. If a new grain targets a note that is already active, the old grain is released first to prevent double note-ons.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Density | density() | Float | 10 | 0.5..100 |
| Size | size() | Float | 200 | 5..500 |
| Voices | voices() | Int | 8 | 1..64 |
| Base Note | base_note() | Int | 60 | 0..127 |
| Mode | mode() | Enum | enum(0) | Single, Chord |
| Stagger | stagger() | Float | 5 | 0..10 |
| Jitter | jitter() | Float | 0.1 | 0..1 |
| Size Jitter | size_jitter() | Float | 0.1 | 0..1 |
| Velocity | velocity() | Float | 1 | 0..1 |
| Vel Jitter | velocity_jitter() | Float | 0 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; Held notes define which notes the scheduler cycles through. When disconnected, notes are generated from BaseNote + Voices. | |
| Clock | Gate | required; External clock for synced grain density. One grain per rising edge. | |
| Density Mod | ControlNorm | required; Modulates grain spawn rate. | |
| Size Mod | ControlNorm | required; Modulates grain duration. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| MIDI Out | Midi | Grain triggers as MIDI note-on/note-off events. |
Logger (Logger)
Logger)Logs port data and passes it through unchanged. Useful for debugging.
let node = Logger.add("my_logger");
Logs signal information to the console and passes data through unchanged. Accepts any port type.
Insert inline to inspect audio levels, control values, MIDI events, gate counts, V/Oct ranges, or data payloads without altering the signal chain. The output port type locks to match the connected input. Audio logs show stereo min/max range; MIDI logs show individual events; empty MIDI buffers are skipped. The node label (if set) prefixes every log line.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Log Level | log_level() | Enum | enum(2) | Trace, Debug, Info, Warn, Error |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | required; Any signal type to log and forward. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | Input data passed through unchanged. |
Micro Tuning (MicroTuning)
MicroTuning)Microtonal tuning - divisions/octave and V/Oct offset with CV modulation
let node = MicroTuning.add("my_microtuning");
Microtonal tuning configuration source. Outputs a TuningConfig on a Data port for downstream pitch calculations.
CV inputs are additive: the Divisions CV value is added to the property value, and the Offset CV value is added to the property value, both clamped to their respective ranges after summing. Standard 12-TET is the default (12 divisions, 0 offset).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Divisions/Oct | divisions_per_octave() | Float | 12 | 1..128 |
| V/Oct Offset | v_oct_offset() | Float | 0 | -10..10 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Divisions CV | ControlFreq | Modulates divisions/octave (added to property value). | |
| Offset CV | ControlBipolar | Modulates V/Oct offset (added to property value). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Tuning | Data(Tuning) | TuningConfig for downstream pitch calculations. |
Mid/Side (MidSide)
MidSide)Stereo ↔ mid/side conversion for independent channel processing
let node = MidSide.add("my_midside");
Converts between stereo L/R and mid/side representation.
Encode: M = (L+R)/2, S = (L-R)/2. The left channel of the output carries mid, the right carries side. Decode: L = M+S, R = M-S. The left channel of the input is treated as mid, right as side. Use an Encode→processing→Decode chain for mastering-style EQ, compression, or stereo widening on the mid and side independently.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | Encode, Decode |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Stereo audio: L/R for encoding, M/S for decoding. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Converted stereo audio: M/S from encoding, L/R from decoding. |
Mixer (Mixer)
Mixer)Sums multiple audio inputs with per-input gain and pan control
let node = Mixer.add("my_mixer");
Sums multiple stereo audio inputs into a single output with per-input gain and constant-power pan controls, plus a master gain stage.
Each input is gained and then panned using a constant-power (sin/cos) law before summing. Pan at -1.0 routes all energy to the left; at 1.0 to the right; at 0.0 the input is centered with equal power in both channels. The per-input gain multiplies the signal before the pan stage, so gain and pan interact: a gained signal is panned, not the other way around. Master gain is applied after all inputs are summed. All gain and pan values are per-sample smoothed to prevent clicks.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Inputs | inputs() | Int | 4 | 1..64 |
| Master Gain | master_gain() | Float | 1 | 0..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio | gain(), pan() | unbounded; Audio source to mix. Per-input gain and pan are available. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | Summed and gained stereo mix. |
Multiplexer (Multiplexer)
Multiplexer)Selects from multiple audio inputs with smooth crossfading
let node = Multiplexer.add("my_multiplexer");
Selects one of N audio inputs and routes it to a single output with smooth crossfading between adjacent channels.
Selection by CV (0.0 = first input, 1.0 = last) or gate stepping. The Select CV overrides the step index when connected. Step gate advances by one on each rising edge; with Wrap enabled, stepping past the last input returns to the first, otherwise it clamps. Crossfade time controls how quickly the smoothed position tracks the target -- at 0 ms transitions are instant. Only connected inputs participate; disconnected slots contribute silence.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Inputs | inputs() | Int | 4 | 1..64 |
| Crossfade | crossfade_time() | Float | 5 | 0..1000 |
| Selection | selection() | Int | 0 | 0..63 |
| Wrap | wrap() | Bool | true |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Select | ControlNorm | Continuous position selector. Fractional values crossfade between adjacent inputs. Overrides the step index when connected. | |
| Step | Gate | Rising edge advances to the next input. | |
| Input | Audio | unbounded; Audio source to select from. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Selected and crossfaded audio output. |
Null Sink (NullSink)
NullSink)Discards all inputs and outputs silence
let node = NullSink.add("my_nullsink");
Pulls and discards all connected inputs, then outputs silence.
Forces upstream nodes to process in a pull-based graph without routing their output anywhere audible. Accepts any signal type on unbounded inputs. The audio output is always silent zeros. Useful for side-chain triggers, analysis nodes, or any upstream processing chain that needs to run but not be heard.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Inputs | inputs() | Int | 4 | 1..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | unbounded; Any signal type -- pulled and discarded each buffer. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Silent stereo audio (all zeros). |
Offset (Offset)
Offset)Adds DC bias to audio signal
let node = Offset.add("my_offset");
Adds a constant DC offset to both channels of a stereo audio signal, with per-sample smoothing to prevent clicks.
The same offset value is added to both left and right channels. Useful for shifting bipolar signals to unipolar, biasing control signals routed through the audio bus, or centering waveforms.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Offset | amount() | Float | 0 | -2..2 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to offset. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Offset stereo audio. |
Passthrough (Passthrough)
Passthrough)Passes data through unchanged. Accepts any port type.
let node = Passthrough.add("my_passthrough");
Copies input data to the output unchanged. Accepts any signal type.
When connected, the port type locks to the first connection's type. If the input is disconnected and the output is audio, the output fills with silence. Useful for testing, as a routing placeholder, or as a type-locking bridge between polymorphic ports.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | Any signal type to pass through. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio, Control, ControlNorm, ControlBipolar, ControlFreq, Midi, Gate, VOct, Data(Unknown), Image | Unmodified copy of the input signal. |
Probability Gate (ProbabilityGate)
ProbabilityGate)Randomly passes or blocks gate events with configurable probability
let node = ProbabilityGate.add("my_probabilitygate");
Randomly passes or blocks incoming gate events based on a probability percentage.
Each gate-on event is evaluated independently: at 100% everything passes, at 0% nothing does. Gate-off events pass through only if their corresponding gate-on was allowed, so downstream nodes always see matched on/off pairs.
When PatternLength is >0, random decisions are cached per step position within the pattern cycle. The same step always gets the same pass/block result until the pattern length changes, giving repeatable variation. A Probability Mod CV input overrides the property (0.0--1.0 maps to 0--100%).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Probability | probability() | Float | 100 | 0..100 |
| Pattern Length | pattern_length() | Int | 0 | 0..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate In | Gate | required; Incoming gate events to filter. | |
| Probability Mod | ControlNorm | CV override for probability (0.0--1.0 maps to 0--100%). Replaces the property value when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate Out | Gate | Filtered gate events that passed the probability check. |
Quantizer (Quantizer)
Quantizer)Quantizes CV to musical scales with optional slew limiting
let node = Quantizer.add("my_quantizer");
Snaps V/Oct pitch CV to the nearest note in a musical scale.
Choose a scale type and root note, and the quantizer constrains incoming pitch to valid scale degrees. When the optional Trigger input is connected, quantization only updates on rising edges -- between triggers the output holds the last quantized value. Without Trigger, every sample is quantized continuously.
The Slew property adds portamento between quantized notes. The Gate output fires a rising edge each time the quantized pitch changes to a different note.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Scale | scale() | Enum | enum(1) | Chromatic, Major, Minor, HarmonicMinor, MelodicMinor, Dorian, Phrygian, Lydian, Mixolydian, Aeolian, Locrian, MajorPentatonic, MinorPentatonic, Blues, WholeTone, Diminished |
| Root Note | root_note() | Enum | enum(0) | C, C#, D, D#, E, F, F#, G, G#, A, A#, B |
| Range | range_octaves() | Float | 3 | 1..5 |
| Slew | slew_ms() | Float | 0 | 0..100 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| CV In | VOct | required; V/Oct pitch CV to quantize. | |
| Trigger | Gate | Optional trigger -- when connected, quantization only updates on rising edges. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| CV Out | VOct | Quantized V/Oct pitch CV snapped to the selected scale. |
| Gate Out | Gate | Trigger pulse on each pitch change. |
Rectifier (Rectifier)
Rectifier)Half-wave or full-wave rectification for audio and control signals
let node = Rectifier.add("my_rectifier");
Rectifies audio or control signals. Both paths operate independently and simultaneously when both inputs are connected.
Full-wave mode applies abs() to each sample, folding the negative half upward and doubling the perceived frequency (octave-up effect on audio). Half-wave mode applies max(0, x), clipping negative values to zero and adding odd harmonics. The control output type is ControlNorm (>= 0) regardless of mode, effectively converting bipolar to unipolar.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | Full, Half |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | Stereo audio to rectify. | |
| Control | ControlBipolar | Bipolar control signal to rectify. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio | Audio | Rectified stereo audio. |
| Control | ControlNorm | Rectified control value (always >= 0). |
Sample & Hold (SampleHold)
SampleHold)Gate-triggered sample and hold with glide
let node = SampleHold.add("my_samplehold");
Captures the input signal on each gate rising edge and holds that value until the next trigger.
The input audio is mono-downmixed at the sample where the rising edge occurs. The held value is linearly mapped through the OutputMin/OutputMax range (value * (max - min) + min). When glide is non-zero, transitions between held values use an ease-out curve over the glide duration. Gate state is tracked per sample for sample-accurate triggering.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Glide Time | glide_time() | Float | 0 | 0..1000 |
| Output Min | output_min() | Float | 0 | -1..1 |
| Output Max | output_max() | Float | 1 | -1..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Signal | Audio | required; Audio to sample from. Mono-downmixed at the exact sample where the gate rising edge occurs. | |
| Gate In | Gate | required; Each rising edge captures a new value from Signal. Gate state is tracked per sample for sample-accurate triggering. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | Held value as mono-to-stereo audio, mapped through the output range, with glide applied if non-zero. |
Signal Follower (SignalFollower)
SignalFollower)Envelope follower with gate output - trigger events from audio amplitude
let node = SignalFollower.add("my_signalfollower");
Tracks the amplitude envelope of an audio signal and emits gate events when the level crosses a threshold.
The envelope follower uses asymmetric one-pole smoothing: attack coefficient tracks rising amplitude, release coefficient tracks falling amplitude. The mono-downmixed absolute value is fed into the follower per sample. A Schmitt trigger with configurable hysteresis converts the envelope to a gate: the gate turns on when the envelope exceeds threshold + hysteresis/2, and off when it drops below threshold - hysteresis/2. Gate edges are emitted at sample-accurate offsets. The Envelope output provides the smoothed amplitude as a normalized 0--1 control value.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Threshold | threshold() | Float | 0.1 | 0.001..1 |
| Hysteresis | hysteresis() | Float | 0.05 | 0..0.3 |
| Attack | attack() | Float | 1 | 0.1..50 |
| Release | release() | Float | 50 | 1..500 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio | Audio | required; Audio signal to track. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate | Gate | High when envelope exceeds threshold, low when it drops below. |
| Envelope | ControlNorm | Smoothed amplitude envelope as normalized CV (0.0--1.0). |
Signal Math (SignalMath)
SignalMath)Two-input audio math: add, subtract, multiply (ring mod), min, max, crossfade
let node = SignalMath.add("my_signalmath");
Two-input math on stereo audio signals. Add layers together, subtract for sidechain ducking, multiply for ring modulation, or crossfade between A and B with a mix knob.
Disconnected inputs contribute silence (zero). In Crossfade mode, 0.0 = all A, 1.0 = all B, with per-sample smoothing on the mix. A Mix Mod CV overrides the property value and is fed into the smoother. The smoother advances even when not in Crossfade mode to stay in sync, so switching modes mid-stream is seamless.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Operation | op() | Enum | enum(0) | Add, Subtract, Multiply, Min, Max, Crossfade |
| Mix | mix() | Float | 0.5 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Input A | Audio | required; First audio operand. | |
| Input B | Audio | Second audio operand. Defaults to silence when disconnected. | |
| Mix Mod | ControlNorm | CV modulation of the crossfade mix (0.0--1.0). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Result of the selected math operation. |
Splitter (Splitter)
Splitter)Duplicates audio signal to multiple outputs with per-output gain control
let node = Splitter.add("my_splitter");
Duplicates a single stereo audio input to multiple outputs with independent per-output gain control.
At unity gain with a settled smoother, outputs share the input buffer via Arc clone (zero-copy). When gain differs from unity or the smoother is still transitioning, a per-sample copy with gain is performed. Use for parallel processing chains, dry/wet mixing, or multi-destination routing.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Outputs | outputs() | Int | 4 | 1..64 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio signal to duplicate. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Output | Audio | unbounded; Copy of the input audio, with per-output gain. |
Stereo Panner (StereoPanner)
StereoPanner)Pan and mix up to two audio inputs with independent stereo placement
let node = StereoPanner.add("my_stereopanner");
Pans and mixes up to two stereo audio inputs with independent pan and width controls per channel into a single stereo output.
Pan uses constant-power (sin/cos) placement. Width crossfades between full stereo (1.0) and collapsed mono (0.0) by blending opposite channels. Both channels are summed into the output, so with two sources you get a stereo mix. CV modulation on all four parameters overrides the property values when connected. All parameters are per-sample smoothed. Unconnected inputs contribute nothing, but their smoothers still advance to stay in sync.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Pan A | pan_a() | Float | 0 | -1..1 |
| Width A | width_a() | Float | 1 | 0..1 |
| Pan B | pan_b() | Float | 0 | -1..1 |
| Width B | width_b() | Float | 1 | 0..1 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio A | Audio | First stereo audio source. | |
| Pan A Mod | ControlBipolar | CV modulation for Pan A (-1.0--1.0). | |
| Width A Mod | ControlNorm | CV modulation for Width A (0.0--1.0). | |
| Audio B | Audio | Second stereo audio source. | |
| Pan B Mod | ControlBipolar | CV modulation for Pan B (-1.0--1.0). | |
| Width B Mod | ControlNorm | CV modulation for Width B (0.0--1.0). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Combined stereo mix of A and B with panning applied. |
Stereo ➔ Mono (StereoToMono)
StereoToMono)Downmix stereo audio to mono (L+R average, output as dual-mono stereo)
let node = StereoToMono.add("my_stereotomono");
Downmixes stereo audio to mono by averaging L and R. The output is still a stereo frame (both channels identical) since the audio bus is always stereo. Useful before processing that expects a centered mono signal.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Stereo audio to downmix. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Dual-mono stereo audio (L = R = average). |
Timed Gate (TimedGate)
TimedGate)Fixed-duration gate with transport or wall-clock trigger
let node = TimedGate.add("my_timedgate");
Fixed-duration gate generator with two independent timing modes.
In WallClock mode, a rising edge on Gate In starts the cycle: optional delay, then the gate opens for the configured duration. A retrigger during the active phase restarts the cycle. The Reset input forces the gate closed and returns to idle.
In Transport mode, the gate auto-fires when the transport reaches the Offset bar. Stopping the transport closes the gate and re-arms the trigger; starting again re-evaluates the offset. Duration, delay, and gap are specified in bars and scale with tempo.
Both modes support repeat (Off, Infinite, or a fixed Count) with an optional gap between cycles. Three outputs: the gate itself, an inverted copy, and a one-sample end-trigger pulse when the active phase closes.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Mode | mode() | Enum | enum(0) | WallClock, Transport |
| Offset | offset() | Int | 0 | 0..9999 |
| Bars | bars() | Int | 1 | 1..9999 |
| Delay | delay_bars() | Int | 0 | 0..9999 |
| Gap | gap_bars() | Int | 0 | 0..9999 |
| Duration | milliseconds() | Int | 500 | 1..65535 |
| Delay | delay_ms() | Int | 0 | 0..65535 |
| Gap | gap_ms() | Int | 0 | 0..65535 |
| Repeat | repeat() | Enum | enum(0) | Off, Infinite, Count |
| Count | repeat_count() | Int | 1 | 1..9999 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Gate In | Gate | required; Trigger source (WallClock mode only). Rising edge starts or retriggers the cycle. Ignored in Transport mode. | |
| Reset | Gate | required; Forces the gate closed and resets all state to idle (WallClock mode only). Ignored in Transport mode. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Gate Out | Gate | Fixed-duration gate signal. |
| Inverse | Gate | Inverted gate -- HIGH when Gate Out is LOW. |
| End Trigger | Gate | One-sample pulse when the active phase ends. Fires before the gap or next cycle begins -- useful for chaining. |
Timing (Timing)
Timing)Timing source - BPM, time signature, and key signature
let node = Timing.add("my_timing");
Outputs structured timing metadata (BPM, time signature, key signature) as a TimingData value on a Data port.
When the Tempo input is connected, its value replaces the BPM property (clamped 1--999). Time signature and key signature always come from the properties. The Beat Unit property is an encoded power-of-two exponent (0=1, 1=2, 2=4, 3=8, 4=16, 5=32).
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| BPM | bpm() | Float | 120 | 1..999 |
| Beats/Bar | time_sig_num() | Int | 4 | 1..32 |
| Beat Unit | time_sig_denom() | Int | 2 | 0..5 |
| Key (sharps/flats) | key_sharps() | Int | 0 | -7..7 |
| Minor | key_minor() | Bool | false |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Tempo | Control | Replaces the BPM property with the signal value (clamped 1--999). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Timing | Data(Timing) | Structured timing metadata. |
Transport (Transport)
Transport)Outputs transport state: playing gate, beat position, and tempo
let node = Transport.add("my_transport");
Exposes the global transport state as patchable signals and optionally accepts MIDI input for external transport control.
Three outputs: Playing (gate high while transport plays, with edge events on transitions), Beat (unbounded Control with the current beat position), and BPM (unbounded Control with the current tempo). The node always processes even when no outputs are connected, to ensure MIDI transport commands are captured.
When a MIDI input is connected, system real-time Start/Continue messages trigger Play, Stop messages trigger Stop, and configurable CC numbers map to play, stop, and master volume. CC values >0 trigger the action; volume maps 0--127 to 0.0--1.0.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Play CC | play_c_c() | Int | 115 | -1..127 |
| Stop CC | stop_c_c() | Int | 116 | -1..127 |
| Volume CC | volume_c_c() | Int | 79 | -1..127 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| MIDI In | Midi | required; MIDI input for external transport control. System RT messages and configured CCs are translated to transport commands. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Playing | Gate | Gate high while transport is playing, low when paused. |
| Beat | Control | Current beat position as an unbounded control value. |
| BPM | Control | Current tempo in beats per minute as an unbounded control value. |
Turing Machine (TuringMachine)
TuringMachine)Shift register sequencer with mutation probability for evolving patterns
let node = TuringMachine.add("my_turingmachine");
Shift register sequencer that generates looping CV and gate patterns which evolve over time via a mutation probability knob.
On each clock rising edge the register shifts right by one bit. The LSB wraps to the MSB position, with a configurable chance of being flipped. At 0% mutation the sequence repeats perfectly; at 100% the output is fully random. The CV output is the full register value normalized to 0.0--1.0. The Gate output reflects the MSB. The Pulse output fires a one-sample trigger on every clock step regardless of register content. The register initializes with random bits on the first clock edge.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Length | length() | Int | 8 | 2..32 |
| Mutation | mutation() | Float | 0 | 0..100 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Clock | Gate | required; Clock input - each rising edge advances the register. | |
| Mutation CV | ControlNorm | required; CV control over mutation probability (0.0--1.0 maps to 0--100%). Overrides the Mutation property when connected. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| CV | ControlNorm | Full register value normalized to 0.0--1.0. Updates once per clock step and holds between clocks. |
| Gate | Gate | High when MSB of register is 1. |
| Pulse | Gate | Trigger pulse on every clock step. |
VCA (VCA)
VCA)Voltage controlled amplifier for audio-rate modulation
let node = VCA.add("my_vca");
Voltage-controlled amplifier -- multiplies two stereo audio signals sample-by-sample, channel-by-channel.
Both inputs are Audio type (not Control), enabling true stereo audio-rate modulation. Wire an envelope, LFO, or any audio source into the Control input for amplitude shaping, tremolo, or ring modulation. Both inputs are required -- the node produces nothing if either is disconnected.
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| Audio In | Audio | required; Audio signal to modulate. | |
| Control | Audio | required; Audio-rate modulator (envelope, LFO, or other audio). Multiplied channel-by-channel with Audio In. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Audio Out | Audio | Audio multiplied by the control signal. |
VOct Converter (VoctConverter)
VoctConverter)Converts VOct pitch CV to multiple output types simultaneously
let node = VoctConverter.add("my_voctconverter");
Converts V/Oct pitch CV to multiple output formats simultaneously.
Freq output: reference_freq * 2^voct. Audio output: dual-mono stereo from the V/Oct buffer values (not frequency). Control output: raw V/Oct value. VOct output: pass-through of the input buffer. MIDI output: converts the first sample's V/Oct to a note number (voct * 12 + reference_note, rounded) and emits note-on / note-off events driven by the Gate input. Without a Gate input, no MIDI events are generated. Note changes without a gate re-trigger do not emit new note-on events.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Reference Note | reference_note() | Int | 60 | 0..127 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| VOct In | VOct | required; V/Oct pitch CV to convert. | |
| Gate In | Gate | Drives MIDI note-on/note-off generation. Without this input connected, the MIDI output stays empty. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| Freq | ControlFreq | Pitch as frequency in Hz. |
| Audio | Audio | Dual-mono stereo audio from V/Oct values. |
| Control | Control | Raw V/Oct value as a control signal. |
| MIDI | Midi | MIDI note events generated from V/Oct + gate. |
| VOct | VOct | V/Oct pass-through. |
VOct Scale/Offset (VoctScaleOffset)
VoctScaleOffset)Scale and offset VOct pitch CV. Useful for creating intervals, harmonies, and creative pitch effects.
let node = VoctScaleOffset.add("my_voctscaleoffset");
Multiplies and offsets V/Oct pitch CV per sample: output = input * Scale + Offset.
Negative Scale inverts pitch intervals. Scale of 2.0 doubles intervals (octave becomes two octaves). Offset shifts the entire pitch up or down in V/Oct units (1.0 = one octave). Useful for creating harmonies, inversions, and creative pitch effects.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Scale | scale() | Float | 1 | -4..4 |
| Offset | offset() | Float | 0 | -4..4 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| VOct In | VOct | required; V/Oct pitch CV to transform. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| VOct Out | VOct | Scaled and offset V/Oct pitch CV. |
VOct Slew (VoctSlew)
VoctSlew)Smooth VOct pitch transitions with configurable slew rate. Creates portamento/glide effects between notes.
let node = VoctSlew.add("my_voctslew");
Rate-limits V/Oct pitch transitions for portamento and glide effects. Operates at audio rate (per sample).
Rise and Fall times override SlewTime when set above zero. If both are zero, SlewTime is used for both directions. A Slew Mod CV (0.0--1.0) scales the effective slew time: at 0.0 transitions are instant, at 1.0 the full time is used. The maximum change per sample is 1/(slew_time * sample_rate) V/Oct. On the first sample the output jumps to the input with no slew.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Slew Time | slew_time() | Float | 0.1 | 0..10 |
| Rise Time | rise_time() | Float | 0 | 0..10 |
| Fall Time | fall_time() | Float | 0 | 0..10 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| VOct In | VOct | required; V/Oct pitch CV to smooth. | |
| Slew Mod | ControlNorm | CV scaling of slew time (0.0 = instant, 1.0 = full slew time). |
Outputs:
| Port | Type | Notes |
|---|---|---|
| VOct Out | VOct | Slew-limited V/Oct output. |
VOct Transpose (VoctTranspose)
VoctTranspose)Transpose VOct pitch CV by semitones, octaves, and cents.
let node = VoctTranspose.add("my_vocttranspose");
Shifts V/Oct pitch CV by semitones, octaves, and cents.
All three offsets are summed into a single V/Oct shift: semitones/12 + octaves + cents/1200. The combined offset is added to every sample in the input buffer. Use for fixed transpositions, octave layering, or fine-tuning.
Properties:
| Property | Method | Type | Default | Range |
|---|---|---|---|---|
| Semitones | semitones() | Int | 0 | -48..48 |
| Octaves | octaves() | Int | 0 | -4..4 |
| Cents | cents() | Int | 0 | -100..100 |
Inputs:
| Port | Type | Properties | Notes |
|---|---|---|---|
| VOct In | VOct | required; V/Oct pitch CV to transpose. |
Outputs:
| Port | Type | Notes |
|---|---|---|
| VOct Out | VOct | Transposed V/Oct pitch CV. |
Idempotency
Scripts are designed to be safely rerunnable. Running the same script twice produces the same graph, not duplicates.
| Operation | Behavior on rerun |
|---|---|
Type.add("label") | Returns existing node with that label and type |
AudioSink.first() | Always returns the singleton sink |
out.port().connect(in.port()) | No-op if connection already exists |
node.frequency(440.0) | Naturally idempotent |
node.add_track("name") | Returns existing track if name matches |
Hot Reload
The script editor (Cmd+E) supports Cmd+R to hot-reload: clear the graph and re-execute from scratch. With idempotent operations, scripts can also be rerun without clearing.
Tips
Inspecting State
osc.show(); // print node details
osc.input("Audio In").show(); // print port details
track.show(); // print track details
region.show(); // print region details
note.show(); // print note details
list_nodes(); // list all nodes in the graph
list_types(); // list all available node types
help(); // list all functions
help("sequencer"); // filter help by keyword
Connecting Nodes
Inspecting State
osc.show(); // print node details
osc.input("Audio In").show(); // print port details
track.show(); // print track details
region.show(); // print region details
note.show(); // print note details
list_nodes(); // list all nodes in the graph
list_types(); // list all available node types
help(); // list all functions
help("sequencer"); // filter help by keyword
Connecting Nodes
Use typed port handles with .port() for connecting:
let osc = Oscillator.add("osc");
let mix = Mixer.add("mix");
let sink = AudioSink.first();
osc.output("Audio Out").port().connect(mix.input("Input 0").port());
mix.output("Output").port().connect(sink.input("Audio In").port());
Port Properties
Some ports have per-connection properties with typed methods:
let mix = Mixer.add("mix");
mix.input("Input 0").gain(0.5); // typed method
mix.input("Input 0").pan(0.3); // typed method
Sequencer Programming
let edit = SequencerEditor.add("seq_edit");
let seq = Sequencer.add("seq");
edit.output("Sequencer Out").port().connect(seq.input("Config In").port());
edit.bpm(120.0);
edit.bars(4);
edit.timesig(4, 4);
edit.set_loop(true);
let mel = edit.add_track("Melody");
mel.add_notes([
["C4", 0.0, 1.0, 100],
["E4", 1.0, 1.0, 90],
["G4", 2.0, 1.0, 85],
]);
Arranger Programming
let edit = ArrangerEditor.add("arr");
let arr = Arranger.add("arr_play");
edit.output("Arrangement").port().connect(arr.input("Config In").port());
edit.bpm(140.0);
edit.bars(32);
let verse = edit.add_track("Verse");
let r = verse.add_region(0.0, 16.0);
r.set_label("Intro");
r.set_level(0.8);
Envelopes
let synth = PolysynthV2.add("synth");
synth.shape(adsr(0.01, 0.1, 0.7, 0.2)); // attack, decay, sustain, release
synth.shape(one_shot(0.005, 0.3)); // attack, decay (no sustain)
synth.shape(decay_env(0.1)); // instant peak, decay
synth.shape(gated_hold(0.01, 0.5)); // attack to peak, hold until release
Curves
let edit = SequencerEditor.add("seq_edit");
let seq = Sequencer.add("seq");
edit.output("Sequencer Out").port().connect(seq.input("Config In").port());
edit.bpm(120.0);
edit.bars(4);
edit.timesig(4, 4);
edit.set_loop(true);
let mel = edit.add_track("Melody");
mel.add_notes([
["C4", 0.0, 1.0, 100],
["E4", 1.0, 1.0, 90],
["G4", 2.0, 1.0, 85],
]);
Arranger Programming
let edit = ArrangerEditor.add("arr");
let arr = Arranger.add("arr_play");
edit.output("Arrangement").port().connect(arr.input("Config In").port());
edit.bpm(140.0);
edit.bars(32);
let verse = edit.add_track("Verse");
let r = verse.add_region(0.0, 16.0);
r.set_label("Intro");
r.set_level(0.8);
Envelopes
let synth = PolysynthV2.add("synth");
synth.shape(adsr(0.01, 0.1, 0.7, 0.2)); // attack, decay, sustain, release
synth.shape(one_shot(0.005, 0.3)); // attack, decay (no sustain)
synth.shape(decay_env(0.1)); // instant peak, decay
synth.shape(gated_hold(0.01, 0.5)); // attack to peak, hold until release
Curves
let synth = PolysynthV2.add("synth");
synth.shape(adsr(0.01, 0.1, 0.7, 0.2)); // attack, decay, sustain, release
synth.shape(one_shot(0.005, 0.3)); // attack, decay (no sustain)
synth.shape(decay_env(0.1)); // instant peak, decay
synth.shape(gated_hold(0.01, 0.5)); // attack to peak, hold until release
Curves
Build automation curves for AutomationCurve, CurveFunctionGenerator,
and other curve-property nodes:
// Factory functions
let c = linear_curve(4.0); // 0→1 over 4 seconds
let c = ease_in_out_curve(8.0); // smooth S-curve over 8 seconds
// Build from scratch
let c = curve();
c.add_point(0.0, 0.0); // time, value (zero tangents)
c.add_smooth_point(2.0, 0.8, 0.0, 1.5); // time, value, in_tangent, out_tangent
c.add_point(4.0, 1.0);
c.bipolar(); // set range to -1..1
// Apply to a node
let auto = AutomationCurve.add("sweep");
set(auto, "CurveData", c);
// Query
print(c.eval(1.0)); // evaluate at time
print(c.duration()); // total duration
print(c.len()); // keyframe count
Timing
sleep(1000); // pause for 1 second
breakpoint(); // pause until resumed (script editor only)
Batch Notes
sleep(1000); // pause for 1 second
breakpoint(); // pause until resumed (script editor only)
Batch Notes
Individual add_note() calls cost one round-trip each. For many notes,
use add_notes() which sends the entire batch in one call:
track.add_notes([
["C4", 0.0, 1.0, 100], // [pitch, beat, duration, velocity]
["E4", 1.0, 0.5, 90],
["G4", 1.5, 0.5, 85],
]);
View Control
autolayout(true); // auto-arrange nodes on topology changes
zoom_to_fit(); // fit all nodes in view
toggle("3d"); // toggle 3D mode
toggle("activity"); // toggle activity monitor
Graph Management
graph_clear(); // remove all nodes except AudioSink
graph_new("name"); // create a new graph (requires open project)
undo(); // undo last operation
redo(); // redo
autolayout(true); // auto-arrange nodes on topology changes
zoom_to_fit(); // fit all nodes in view
toggle("3d"); // toggle 3D mode
toggle("activity"); // toggle activity monitor
Graph Management
graph_clear(); // remove all nodes except AudioSink
graph_new("name"); // create a new graph (requires open project)
undo(); // undo last operation
redo(); // redo
Generated by dump-scripting-docs. Regenerate with:
cargo run -p editor-logic --bin dump-scripting-docs