Skip to content

Configuration

Where a setting lives follows from what kind of fact it is, and there are only three kinds:

What it isWhere it goesTravels with the project?
A fact about this machine — which audio interface, which MIDI ports, where your kits and plugins are~/.resonon/preferences.tomlNo — and it must not
A fact about this piece — which packages it draws on, which kits came from outside itresonon.toml in the projectYes
An operational knob — CI, headless rendering, benchmarksA CLI flag, an environment variable, or a VSCode settingn/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.

~/.resonon/preferences.toml

Read 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 = 48000
buffer_size = 256
recording_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 = 48

Your 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.

Terminal window
resonon setup

The 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 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, 96000
Everything 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 Speakers
Everything 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.

KeyWhat it is
outputOutput 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.
inputInput device, matched the same way. Opened at startup, not on the first .input(…). Not connected means capture stays closed; playback is unaffected.
sample_rateHz. A rate the device cannot run is named and dropped; the block size beside it still applies.
buffer_sizeFrames per block. The latency/stability trade, and the one worth tuning by ear.
recording_offset_msSigned 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.

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]
kits = ["~/Music/kits", "/Volumes/Audio/Samples/kits"]
plugins = ["~/Plugins"]

Or without opening the file — one folder at a time:

Terminal window
resonon config kits add "~/Music/kits"
resonon config kits list
resonon 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 writeYou get
~, ~/…Your home directory. Only at the start of a path; ~user is not expanded.
anything elseRefused, naming why.
KeyWhat it is
viz_resolutionHow 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.

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.

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:

SettingCall
Master output channelsaudio_output_channels(0, 1)
Per-track output channelsaudio_output_channels(drums, 2, 3)
Per-track input channelsdrums.input(2, 3)

Three things, and they are the three things Live keeps as buttons rather than as fields:

CallWhat 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.

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:

OrderWhereModulesKits
1The project./lib, ./dependencies./kits, then ./lib/*/kits and ./dependencies/*/kits
2This machine—every [folders] kits entry, in file order
3The 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:

Terminal window
resonon config paths

It 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.

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 otherwise
Instrument("Diva", plugin_id); // exactly this build, recorded in the piece

Which 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.

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:

KnobWhereAccepts
Startup plugin scanVSCode resonon.plugins.autoScan, --plugin-auto-scan=<bool>, or RESONON_PLUGIN_AUTO_SCANon/off
Multicore renderingVSCode resonon.audio.multicore, --multicore, or RESONON_PARALLELon/off
External-plugin automation strideVSCode resonon.audio.pluginModStride, --plugin-mod-stride, or RESONON_PLUGIN_MOD_STRIDEa whole number, 1 or more
Isolated plugin scanningRESONON_SUBPROCESS_SCANon/off (default on)
Worker countRESONON_WORKERSa whole number, 1 or more
Worker spin budgetRESONON_SPIN_BUDGETa whole number, 0 or more
Log levelRESONON_LOGoff, error, warn, info, debug, trace
Server password--password, or RESONON_SERVER_PASSWORDany text
The resonon homeRESONON_HOMEan absolute path to a directory, or to nothing yet
A stdlib of your ownRESONON_STDLIB_PATHan 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, yes or on against 0, false, no or off, 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=ture has 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.

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 directory
resonon 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 yetSomething else there
RESONON_HOMEApplied. 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_PATHIgnored. 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.

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.

PathDescription
~/.resonon/preferences.tomlThis machine’s preferences.
~/.resonon/versions/Installed resonon versions.
~/.resonon/versions/currentThe 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.jsonCLAP plugin database.
~/.resonon/cache/vst3-cache.jsonVST3 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.