Configuration
Where a setting lives follows from what kind of fact it is, and there are only three kinds:
| What it is | Where it goes | Travels with the project? |
|---|---|---|
| A fact about this machine — which audio interface, which MIDI ports, where your kits and plugins are | ~/.resonon/preferences.toml | No — and it must not |
| A fact about this piece — which packages it draws on, which kits came from outside it | resonon.toml in the project | Yes |
| An operational knob — CI, headless rendering, benchmarks | A CLI flag, an environment variable, or a VSCode setting | n/a |
That is the split every DAW makes between its Preferences and its project folder. Live has one Preferences dialog; so does resonon, and it is a file.
The rule underneath it: a setting anything outside the running session needs to know is data.
The editor, resonon collect, resonon kits list and the plugin scan all need to know where your
content is, and none of them runs your code. A folder named by a function call only exists while
something is running that call, so everything else was guessing.
Preferences
Section titled “Preferences”~/.resonon/preferences.tomlRead before any of your code runs, so the device comes up right the first time rather than coming up wrong and being reopened.
[audio]output = "Focusrite Scarlett 2i2"input = "Focusrite Scarlett 2i2"sample_rate = 48000buffer_size = 256recording_offset_ms = 2.5
[[midi.outputs]]alias = "daw"port = "IAC Driver Resonon"
[[midi.outputs]]alias = "synth"port = "Moog Sub 37"optional = true
[[midi.inputs]]alias = "kb"port = "Arturia KeyStep"
[folders]kits = ["/Volumes/Audio/Samples/kits"]plugins = ["/Volumes/Audio/Plugins"]
[display]viz_resolution = 48Your piece then holds channel numbers and kit names only, so it travels:
project_title("my_song");project_bpm(120);audio_output_channels(0, 1);
let vocals = AudioTrack("vocals");vocals.input(0);Omit the file entirely and resonon uses the system default devices at their own sample rate and block size — a script that never mentions audio still makes sound and still records.
Setting it up
Section titled “Setting it up”resonon setupThe Preferences dialog, in the terminal. It asks about every key below in turn — the interface, the
rate and block size, the ports, the folders — and offers what this machine actually has, so an
interface is chosen from a list rather than typed from memory. Nothing is written until the last
page, which names every line that would change; esc at any point writes nothing at all.
Pressing ⏎ through a rig that is already set up changes nothing: every page opens on the answer
the file already gives. It is the command to reach for after moving to a new interface, not only on
a new machine.
Typing a path and browsing to one are the same act, so both doors lead to the same file. Editing it
by hand is not second best — a file setup writes comes back out of setup with your comments, your
spacing and your key order intact, and anything in it setup does not ask about is left exactly where
it is. resonon new offers to run it on a machine that has no preferences file yet, and asks only
where there is a terminal to answer in: on a pipe, in CI or from an editor task it prints a tip and
carries on.
In VSCode, RESONON: Open Preferences opens this same machine-level file. The sidebar action creates an empty file on first use but never fills in or rewrites preferences on your behalf.
Without a terminal, resonon setup says so and names the written way to do each of the same things.
A key nobody can honour costs only itself
Section titled “A key nobody can honour costs only itself”A rate this interface cannot run, a block size out of range, a folder that does not exist: each is named, dropped, and the rest of the file is applied. A rig with none of it right still comes up and runs.
Warning: /Users/you/.resonon/preferences.toml asks for audio this machine cannot give: `[audio] sample_rate` 192000 — the device does not run at that rate; it does 44100, 48000, 96000Everything else in the file is applied.A device you named is a device you get, or none
Section titled “A device you named is a device you get, or none”The one key that is never traded for something else is the hardware. Name an interface that is not connected and resonon waits for it: nothing plays, the message says which device it is waiting for, and no other device is opened in its place.
Warning: /Users/you/.resonon/preferences.toml asks for audio this machine cannot give: `[audio] output` "Scarlett 2i2" — is not connected, so it is what this session is waiting for rather than something else to open in its place. Connect it and press play; this machine has: MacBook Pro SpeakersEverything else in the file is applied.Plug it in and press play: PLAY is what opens the device, so there is nothing to restart and nothing to edit. The same holds for an interface unplugged mid-session — playback stops, resonon says so, and play picks it up again when it is back.
This is the one place resonon refuses to be helpful on your behalf. Silently moving a session onto
the laptop speakers is how a monitored microphone ends up in a feedback loop, and a rig you pinned to
its converters is a rig that should stay there. [audio] input follows the same rule in its own
half: a microphone that is not connected leaves capture closed rather than opening the built-in one,
and playback is untouched.
Preferences are never rewritten for you. An interface you unplug keeps its name in the file, so plugging it back in is all it takes.
[audio]
Section titled “[audio]”| Key | What it is |
|---|---|
output | Output device, by name. Exact match first, then substring — "Scarlett" finds "Focusrite Scarlett 2i2". Not connected means nothing plays until it is; never a substitute device. |
input | Input device, matched the same way. Opened at startup, not on the first .input(…). Not connected means capture stays closed; playback is unaffected. |
sample_rate | Hz. A rate the device cannot run is named and dropped; the block size beside it still applies. |
buffer_size | Frames per block. The latency/stability trade, and the one worth tuning by ear. |
recording_offset_ms | Signed correction, in milliseconds, on top of the round trip a recorded take is already compensated by automatically. Usually absent; ±500 ms at most. |
The block size resonon reports is the one the device actually settled on. A backend is free to ignore a fixed size — WASAPI in shared mode always does, CoreAudio may clamp — and a rig that believes it is running at 128 frames while the device hands it 512 is a rig whose owner is debugging the wrong thing. If the two differ, resonon says so.
audio_sample_rate() and audio_buffer_size() read what is actually running. audio_output_devices()
reports sample_rates and buffer_frames per device, so you can see the options without guessing.
sample_rate is the whole session’s, not the output’s alone. Left unset, resonon picks a rate the
devices you named for input and output can both run at — which is how a pair like AirPods, whose
microphone only does 24 kHz, works without being configured. Set it and your rate stands: an input
that cannot join is refused rather than allowed to move the session.
[[midi.outputs]] and [[midi.inputs]]
Section titled “[[midi.outputs]] and [[midi.inputs]]”An array of tables rather than an alias = "port" map, for two reasons: the order is meaningful,
and optional is a fact about one entry rather than about the table.
optional = true is for gear that is not always plugged in. An optional port that is absent keeps
its alias, so a track pointed at it stays valid and its events are counted and reported against it
by name. A required port that is absent is reported and no alias is created, so a track pointed
at it says so where you can act on it.
Run resonon --list-ports for a paste-ready entry per device attached right now, or
resonon setup to pick them off a list and name each one as you go. A port declared here and not
plugged in stays on setup’s list and stays selected: unplugging a synth is not a decision to stop
using it.
Under resonon --no-midi every entry becomes a reservation: nothing is opened, nothing is sent, and
a file evaluates exactly as it would with the hardware attached.
[folders]
Section titled “[folders]”[folders]kits = ["~/Music/kits", "/Volumes/Audio/Samples/kits"]plugins = ["~/Plugins"]Or without opening the file — one folder at a time:
resonon config kits add "~/Music/kits"resonon config kits listresonon config plugins add "/Volumes/Audio/Plugins"resonon setup has a page for each list, where the same folders are added and removed against a
view of where each one currently points.
The list is the truth, so taking an entry out takes it out of the search — something a list that only ever grew could never say.
Only ~ is expanded here. An entry naming an environment variable, or a relative one, is
reported and left out of the search list rather than expanded. A stored path read by the CLI, by the
language server and by a CI shell would name a different folder in each, which is the whole problem
preferences exist to fix. $NAME still works in export() and plugin_rescan(), which are
gestures inside one running process.
| You write | You get |
|---|---|
~, ~/… | Your home directory. Only at the start of a path; ~user is not expanded. |
| anything else | Refused, naming why. |
[display]
Section titled “[display]”| Key | What it is |
|---|---|
viz_resolution | How wide viz() draws one cycle, in characters. 8–64; the default is 24. |
Pick a multiple of the subdivision you are looking at — 24 shows sixteenths in 4/4, 30 shows
triplets — or viz() will say so rather than draw a pattern that lies about where the events are.
Applying a change without restarting
Section titled “Applying a change without restarting”Editing the file changes nothing on its own. Nothing watches it, deliberately: a stray editor save should not be able to reopen your audio device under a take.
reload_preferences();Reads the file, compares it against what is applied, and applies the difference. Only differences, so calling it on an unchanged file costs nothing and makes no gap in the sound. Live’s Preferences dialog applies on click; this is the same act for a file that changed underneath you.
- A file that will not parse changes nothing and says why. The whole file is read before anything is touched, so it cannot half-apply.
- Past that, a key nobody can honour still costs only itself. A missing MIDI port does not cost you your buffer size.
- Every change it makes is named, one line each, so you can see what the call did rather than infer it from the sound.
This covers every setting in the file, the folder lists included. A kit folder added with
resonon config kits add in another terminal is in the file at once, and in force in a session that
is already playing when you call this — the same as the audio device. A search list that moved
because a file was saved could move mid-take, which is exactly what nobody wants.
- While a take is recording, the whole
[audio]section is left alone and said so. The offset trims the head of a take, and the rate would stitch two devices into one.
What belongs in the piece instead
Section titled “What belongs in the piece instead”Device names are a fact about your machine: "Focusrite Scarlett 2i2" is true of one rig and
meaningless on another. Channel numbers are a fact about your piece: drums.input(2, 3) means “the
third and fourth inputs”, and that survives moving to a different rig. So channel routing stays in
the music:
| Setting | Call |
|---|---|
| Master output channels | audio_output_channels(0, 1) |
| Per-track output channels | audio_output_channels(drums, 2, 3) |
| Per-track input channels | drums.input(2, 3) |
What is a call, not a setting
Section titled “What is a call, not a setting”Three things, and they are the three things Live keeps as buttons rather than as fields:
| Call | What it does |
|---|---|
plugin_scan() | Rescan the plugin folders now. |
plugin_rescan(path) | Re-open one bundle, whatever the database says. |
reload_preferences() | Put the file in force. |
resonon setup is a gesture too, and the same one Live’s Preferences dialog is: you open it, you
change something, you close it. It writes the file; reload_preferences() is what puts a change in
force in a session that is already playing.
They are actions — things you do at a moment — rather than settings, which are things that are true until you change them. That is the whole test.
Search paths add, they never replace
Section titled “Search paths add, they never replace”A folder you name is searched in addition to the built-in ones. Naming a kit folder of your own never takes the standard ones away, so adding one cannot make the bundled kits disappear.
Directories are searched in this order, and the first match wins:
| Order | Where | Modules | Kits |
|---|---|---|---|
| 1 | The project | ./lib, ./dependencies | ./kits, then ./lib/*/kits and ./dependencies/*/kits |
| 2 | This machine | — | every [folders] kits entry, in file order |
| 3 | The resonon home | — | the kits shipped with this version, then ~/.resonon/kits |
The project comes first so that a project defines itself: a folder your rig happens to have cannot change what somebody else’s project resolves, and the same source sounds the same on every machine. Your own folders still shadow the installed ones, which is where shadowing is useful.
This is also why a folder list is safely data. It is additive, and ranked below the project’s own content, so it cannot change how a portable piece sounds — only whether a piece that was already reaching outside itself finds what it was reaching for.
There is no machine-wide module path, and no setting that adds one. A module is reachable
because this project’s resonon.toml declares the package it lives in and resonon install put it
in dependencies/, or because a use names it by a relative path — never because of anything on
this machine. Modules says why the
machine-wide directory went away.
A package keeps its kits inside itself, under <package>/kits, so a package’s kits are searched
wherever the package itself is searched — the */kits entries above are the same directories the
Modules column lists, one level down. Sampler(Kit("808")) therefore finds a declared package’s kit
just as Sampler(Kit("drum-kits/808")) does. The qualified spelling stays the one to reach for when
two packages ship a kit of the same name.
Sample files are not searched for at all. Sample("kick.wav") is a path, not a name: it
resolves relative to the .non file that names it — the way use and load_file do — and an
absolute path is taken as given. There is no list, so a sample either sits where your piece says it
sits or it does not exist, and no folder anyone adds can change which file a piece plays. A kit is
the other kind of thing: Kit("808") is a name, and a name is what gets looked up in the list
above. To reach one hit out of a kit, go through the kit rather than through a path that happens to
land inside one:
Sampler(Kit("CR-78")).get("bd")"bd" is the name the kit gives that sound, not the name of a file inside it — which is the point:
the kit decides what its sounds are called, so a piece written against it keeps working when the
kit’s files are renamed or replaced.
A kit found through a [folders] kits entry is still outside the project, and still says so: naming
the folder makes the kit load, it does not make the piece portable. resonon collect is what brings
one in. See Sharing a project.
To see the resolved list — every directory, in order, with where it came from and whether it exists:
resonon config pathsIt covers modules and native extensions, sampler kits, Scala tuning files, CLAP and VST3 plugin
folders, the standard library, and the plugin and cache directories. The editor resolves modules
through the same list, so a use that works when you run a file also hovers, jumps and completes.
Plugins
Section titled “Plugins”Resonon looks in the standard plugin folders for your OS, in CLAP_PATH and VST3_PATH — the
variables the CLAP and VST3 specifications define, which every host reads — and in every
[folders] plugins entry. Both formats look in all of them.
Resonon sweeps those folders on a background thread at startup, opening only bundles that are new or
changed, so installing a plugin does not mean remembering to rescan. Because the folders are read
before the sweep dispatches, a folder of your own is included in it — which is the concrete reason
they had to be data. To rescan by hand: plugin_scan(), RESONON: Rescan Plugins, or
resonon plugin scan.
There is no format preference. With both a CLAP and a VST3 build of the same plugin installed, say which one you mean where you use it:
Instrument("Diva"); // CLAP if there is one, VST3 otherwiseInstrument("Diva", plugin_id); // exactly this build, recorded in the pieceWhich binary loads changes the sound, so it belongs in the piece rather than in a machine-wide setting that would make the same code play differently in two places. With only one format installed there is nothing to say.
Operational knobs
Section titled “Operational knobs”Some things are not settings about your music at all. They exist for continuous integration, headless rendering and benchmarking, they are resolved once at startup before any audio engine exists, and so they are flags and environment variables rather than data:
| Knob | Where | Accepts |
|---|---|---|
| Startup plugin scan | VSCode resonon.plugins.autoScan, --plugin-auto-scan=<bool>, or RESONON_PLUGIN_AUTO_SCAN | on/off |
| Multicore rendering | VSCode resonon.audio.multicore, --multicore, or RESONON_PARALLEL | on/off |
| External-plugin automation stride | VSCode resonon.audio.pluginModStride, --plugin-mod-stride, or RESONON_PLUGIN_MOD_STRIDE | a whole number, 1 or more |
| Isolated plugin scanning | RESONON_SUBPROCESS_SCAN | on/off (default on) |
| Worker count | RESONON_WORKERS | a whole number, 1 or more |
| Worker spin budget | RESONON_SPIN_BUDGET | a whole number, 0 or more |
| Log level | RESONON_LOG | off, error, warn, info, debug, trace |
| Server password | --password, or RESONON_SERVER_PASSWORD | any text |
| The resonon home | RESONON_HOME | an absolute path to a directory, or to nothing yet |
| A stdlib of your own | RESONON_STDLIB_PATH | an absolute path to a directory that is there |
All of them speak one dialect:
-
First match wins: the environment variable, then the flag, then the default.
-
On/off is
1,true,yesoronagainst0,false,nooroff, in any case. -
Unset and empty mean the same thing — not given.
RESONON_WORKERS=falls through to the default, so a script can clear a knob without branching around the assignment. -
A value resonon cannot read stops it starting, naming the variable and what it takes:
Error: RESONON_PARALLEL=ture is not a boolean.Accepted: 1, true, yes, on / 0, false, no, off (case-insensitive).There is no fallback here that would not be a lie: a benchmark that quietly ran serial because of a typo is worse than one that refused to run at all. It is the same answer the flag spelling already gave —
--multicore=turehas always been a usage error — and the environment spelling is not the more forgiving of the two.
Turning the startup scan off makes scanning strictly manual — nothing opens a plugin folder unless you ask it to.
A location is ignored rather than fatal
Section titled “A location is ignored rather than fatal”The last two rows are the exception to that last rule, because they name a location rather than a value. A location has a safe default to fall back to, and the very command you would run to find out what went wrong prints it — so an unusable one is ignored, the default stands, and the startup says so, rather than leaving you unable to start at all over a stale line in a shell profile. An env typo must not be able to cost you a set.
Warning: RESONON_STDLIB_PATH is not applied.`lib/std` is not an absolute path, so it would name a different directory in every directoryresonon is started in.The standard library that ships with this build is in use.Both must be absolute, and that is the load-bearing half: everything below the home hangs off it
and the whole standard library hangs off the other, so a relative value would be a different library
for every directory you happened to start in — the terminal in ~/songs, the editor’s language
server at your workspace root, a render launched from the Finder.
Past that they differ, and the difference is between a folder resonon makes and one it only reads — the same split Live draws between its Library and a Place:
| Nothing there yet | Something else there | |
|---|---|---|
RESONON_HOME | Applied. It is a root resonon owns; resonon install creates versions/ under it, so naming one that does not exist yet is how a second rig is set up. | Ignored. A file where the home should be is a place nothing can ever be kept: ~/.resonon stands instead. |
RESONON_STDLIB_PATH | Ignored. It is content resonon only reads, and a directory that is not there is not a standard library. | Ignored. Nor is a file, or a directory this user cannot read. The ordinary search order below it stands. |
Both are settled once, when the process starts, and the answer is kept for as long as it runs — so the home cannot move under a running session because something appeared or went away on disk. It is the same trade Live makes: it works out where its Library is at launch, gives you the default when it cannot use the one it was told about, and does not run a set on a Library it already knows it cannot write to.
Either way, resonon config paths prints the directory that was actually taken — and tags the
standard library line with RESONON_STDLIB_PATH only when the variable was applied, so it cannot
claim an override that was refused.
Directory Layout
Section titled “Directory Layout”Everything resonon stores per-user lives under ~/.resonon. Set RESONON_HOME to relocate the
whole directory — one home per rig, if you keep more than one.
The value must be an absolute path, under the location rule
above: everything below hangs off it, so a relative one
would be a different library for every directory you happened to start in. A value that is not one
is ignored, ~/.resonon stands, and the startup says so rather than leaving you to work out why
your kits went missing.
The directory itself need not exist yet — resonon makes it, which is how you set up a second rig. But a path with something else already at it is refused the same way a relative one is, because nothing can be kept under a file:
Warning: RESONON_HOME is not applied.`/etc/hosts` is a file, not a directory./Users/you/.resonon is in use.Where the home is is worked out once, when the process starts, and does not change while it runs.
| Path | Description |
|---|---|
~/.resonon/preferences.toml | This machine’s preferences. |
~/.resonon/versions/ | Installed resonon versions. |
~/.resonon/versions/current | The active version. |
~/.resonon/versions/<v>/kits/ | The kits that ship with a version. |
~/.resonon/kits/ | Your own sampler kit library. |
~/.resonon/plugins/ | Embedded CLAP plugins cache. |
~/.resonon/cache/clap-cache.json | CLAP plugin database. |
~/.resonon/cache/vst3-cache.json | VST3 plugin database. |
The plugin database carries a format version. One written by a different resonon version is discarded and rebuilt, so the first sweep after upgrading opens every bundle again — slower once, then back to normal. Nothing to do about it.
See Also
Section titled “See Also”- CLI Reference — flags, subcommands, and environment variables
- Modules — how a
useresolves - Plugins — CLAP/VST3 plugin usage
- Recording & Input — live audio input configuration
- Sharing a project — making a project travel
- Troubleshooting — common issues and fixes