Package Management
A resonon package is a directory — usually a git repository — that bundles reusable
code (a src/lib.non file) and/or sample kits (a kits/ directory). There’s no registry
and no publish step: packages live on GitHub and install with a gh:user/repo shorthand
that maps straight to a clone URL.
This guide covers scaffolding projects, the resonon.toml manifest, installing and
publishing packages, and the kit.toml sample-kit format. For the runtime CLI (running
files, the REPL, the server), see the CLI Reference.
Scaffolding a Project
Section titled “Scaffolding a Project”resonon init <name> scaffolds a project in the current directory; resonon new <name>
creates a new directory first. Both accept flags that choose the project type:
| Flag | Project type |
|---|---|
| (none) | Plain project with src/main.non. |
--lib | Library with src/lib.non (importable code). |
--kit | Sample-kit project with a kits/ directory. |
--native | Native Rust extension (implies --lib). |
Flags combine:
resonon new my-pkg --lib --kit # library plus sample kitsresonon new my-ext --lib --native # library backed by a native extensionEvery scaffold writes a resonon.toml manifest, a README.md, an MIT LICENSE, a
.gitignore, and initializes a git repository (unless you’re already inside one). A plain
project looks like this:
my-project/ resonon.toml src/ main.non # entry point — run with `resonon run` README.md LICENSE .gitignoreresonon run executes src/main.non from the project root. The generated .gitignore excludes
dependencies/, which resonon install restores from the committed resonon.lock, and renders/,
which is output rather than source.
Project Setup
Section titled “Project Setup”The scaffolded src/main.non sets project metadata with builtins that double as your
project’s configuration:
project_title("My Project");project_artist("Your Name");project_bpm(120);| Builtin | Description |
|---|---|
project_title(string) | Set the project title. |
project_artist(string) | Set the artist name. |
project_bpm(bpm, beats_per_cycle?) | Set the project tempo in BPM. beats_per_cycle defaults to 4. |
project_bpm() establishes the tempo at startup. To change tempo live during a session,
use setbpm(bpm, beats_per_cycle?) instead — it takes effect immediately and is ignored
while resonon is following an external MIDI clock. See Clock
Sync.
The Manifest (resonon.toml)
Section titled “The Manifest (resonon.toml)”resonon.toml declares package metadata and dependencies. It’s created automatically by
resonon init / resonon new:
[package]name = "my-project"version = "0.1.0"authors = ["Your Name <email>"]resonon = "0.10.0"
[dependencies]drums = { source = "gh:resonon/drum-kits", version = "v1.0" }synths = { source = "gh:user/synths" }[package]
Section titled “[package]”| Field | Required | Description |
|---|---|---|
name | Yes | Package name. |
version | No | Package version. Informational. |
authors | No | List of authors. Informational. |
resonon | No | resonon version the project targets. |
resonon is compared against your installed version on every run, at
major.minor precision. A project targeting a newer resonon refuses to run —
its source may use syntax your version does not have. A project targeting an
older one runs with a warning. Patch releases are interchangeable.
[dependencies]
Section titled “[dependencies]”Each entry maps a package name to its source. The key is the local package name (the directory it installs into).
| Field | Required | Description |
|---|---|---|
source | Yes | Package source (gh:user/repo). |
version | No | Git tag to pin to (e.g. "v1.0"). |
[local]
Section titled “[local]”What the project draws on that has no upstream to fetch it from — the mirror image of
[dependencies], and the reason nothing in it ever takes a source.
| Field | Required | Description |
|---|---|---|
kits | No | Kits that came from outside the project and cannot be fetched. |
resonon collect writes these entries for you. See Sharing a
Project.
[native]
Section titled “[native]”Present only for packages with a native Rust extension. See Native Extensions.
| Field | Required | Description |
|---|---|---|
crate | Yes | Path to the native Rust crate (relative to the project root). |
Keys that aren’t manifest keys
Section titled “Keys that aren’t manifest keys”A key resonon doesn’t read is named, not obeyed and not fatal:
warning: /my-project/resonon.toml has a key that is not a manifest key: `[package] versoin` — nothing reads this key. Did you mean `version`?Everything else in the file still applies, and the piece still plays. A typo under
[local] must not take your project name and your kit pool down with it mid-set — so it is
reported, once, and the rest of the manifest is read as usual. Open resonon.toml in VSCode
and the key is underlined where it is written, with a Rename to version quick fix.
A file that cannot be parsed at all is a different matter: there is no way to tell what it
says, so every command that reads it stops and names the line. resonon install checks this
before it fetches anything, so a manifest it could not record the install in leaves nothing
behind on disk.
Installing Packages
Section titled “Installing Packages”The pkg subcommands have top-level aliases for the common cases:
| Full command | Alias |
|---|---|
resonon pkg install <source> | resonon install <source> |
resonon pkg update [name] | resonon update [name] |
| — | resonon install (no args — install all from resonon.toml) |
| — | resonon remove <name> |
Install from GitHub with the gh:user/repo shorthand, optionally pinning a tag with @:
resonon install gh:resonon/drum-kitsresonon install gh:resonon/drum-kits@v1.0Where packages install: in the project. Packages go to dependencies/ relative to the
project root and are recorded in resonon.toml and resonon.lock, which is what makes the
project reproducible somewhere else. There is no machine-wide package directory, and
resonon install outside a project is an error rather than a silent install into one — start a
project first with resonon new <name> or resonon init.
After install, resonon reports the package type and any kits:
Installed 'drum-kits' [kits] to dependencies/drum-kits Kits: 808 — Sampler(Kit("drum-kits/808")) vinyl — Sampler(Kit("drum-kits/vinyl"))Run resonon install with no arguments to install every dependency listed in
resonon.toml.
Dependencies are only half of what a freshly cloned project needs. If its resonon.toml
pins a resonon version you do not have, resonon install says so when it finishes and
names the command that fixes it:
This project targets Resonon 0.12.0, and 0.11.0 is running.Switch with: resonon version switch 0.12.0 --installIt reports rather than acts — switching would change what every other shell resolves, and
that is not something installing one project’s dependencies should do. See
resonon version.
Reproducible installs
Section titled “Reproducible installs”resonon install installs at the lock. For a dependency with no version pin,
it checks out the commit recorded in resonon.lock rather than whatever the default
branch currently points at — so everyone on the project, and every CI run, gets the
same code. If a dependency has drifted away from the lock, resonon install restores
it, and says which commit it put it back to:
Restored 'drum-kits' [kits] to dependencies/drum-kits, at locked commit 4f3c1a9e02b7 Kits: 808 — Sampler(Kit("drum-kits/808"))A version in resonon.toml still wins, since pinning a tag is the more explicit
instruction.
resonon update is the command that moves dependencies forward and rewrites the lock.
Commit resonon.lock: it is project source, and is what makes installs reproducible across
machines. Scaffolded projects track it by default and ignore the restored dependencies/
directory.
Listing & Inspecting
Section titled “Listing & Inspecting”resonon pkg list shows installed packages and their types:
Installed packages (dependencies/):
drum-kits [kits] kit: 808 — Sampler(Kit("drum-kits/808")) kit: vinyl — Sampler(Kit("drum-kits/vinyl")) my-lib [code] studio-tools [code+kits] kit: piano — Sampler(Kit("studio-tools/piano"))| Type | Meaning |
|---|---|
[code] | Has src/lib.non (importable code). |
[kits] | Has a kits/ directory. |
[code+kits] | Has both. |
resonon pkg inspect <name> prints a package’s documentation, code exports with
signatures, and kit details:
Package: my-lib [code+kits]
A collection of utility functions and drum kits.
Exports:
fn scaled(value, factor) let default_bpm = <number>
Kits:
lo-fi — oneshot mode, 12 samples Usage: Sampler(Kit("my-lib/lo-fi"))Updating & Removing
Section titled “Updating & Removing”resonon update [name] re-clones dependencies at their pinned tag (or the latest commit
when no version is set), rebuilds any native extensions, and regenerates resonon.lock:
resonon update # update all dependenciesresonon update drum-kits # update oneresonon remove <name> drops a dependency from resonon.toml and
resonon.lock and deletes its installed directory:
resonon remove drum-kitsCreating a Package
Section titled “Creating a Package”A package needs at least one of: a src/lib.non (code) or a kits/ directory (samples).
Both can coexist in one repository.
Code Packages
Section titled “Code Packages”Write src/lib.non with the functions and values you want to share. All top-level
fn, let, and class definitions are exported by default; mark items private to
keep them internal. /// doc comments appear in resonon pkg inspect, and the first
doc-comment block becomes the package description.
/// my-lib — utility helpers for generative rhythms./// The first doc-comment block becomes the package description.
private let lib_name = "my-lib";
/// Scale a value and round to the nearest integer.fn scaled(value, factor) { round(value * factor)}
/// Default tempo for performance templates.let default_bpm = 120;
/// Print a greeting. Internal — not exported.private fn greet() { print(f"Hello from {lib_name}!");}Kit Packages
Section titled “Kit Packages”Organize WAV files under kits/<kit-name>/. A kit is one subdirectory of kits/:
my-drums/ kits/ 808/ bd.wav sd.wav hh.wavIf you name files after GM drum abbreviations (bd, sd, hh, …),
Resonon auto-detects notes and you can skip kit.toml entirely. For explicit control, add
a kit.toml — and then the samples move into a samples/ subdirectory:
my-drums/ kits/ 808/ kit.toml samples/ bd.wav sd.wav hh.wavSee Kit Configuration.
Using Installed Packages
Section titled “Using Installed Packages”Import a package’s code with use "<name>" — the bare package name resolves to its
src/lib.non. The alias is the package name with hyphens turned into underscores,
since it has to be a valid identifier:
use "studio-tools";
let pattern = studio_tools.swing(42, 0.6);Load its kits with Sampler(Kit("<name>/<kit>")):
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");drums.instrument(Sampler(Kit("studio-tools/lo-fi")));drums << [bd sd bd sd];PLAY;The bare kit name works too — Kit("lo-fi") — since a package’s kits are searched wherever the
package itself is. Qualify when two packages ship a kit of the same name, or when you want the
source to say which package a sound comes from.
See Modules for the full import system and Samplers for sampler details.
Kit Configuration (kit.toml)
Section titled “Kit Configuration (kit.toml)”A kit.toml controls how a kit’s samples are mapped and played. Every section is optional
— with no kit.toml, resonon auto-detects WAV files in the directory.
Every path a kit.toml names must be relative, must live under samples/, and must not
climb out of the kit with ... A kit directory is a unit: that rule is what lets it be
copied into a project — by hand or with resonon collect
— and still play. A kit that pointed at /Users/you/samples/kick.wav would work on your
machine and silently fall apart on anyone else’s. Loading such a kit is an error that names
every offending path.
[kit] — metadata
Section titled “[kit] — metadata”| Field | Type | Default | Description |
|---|---|---|---|
name | String | Directory name | Display name for the kit. |
author | String | — | Kit author. |
mode | String | "oneshot" | Playback mode: "oneshot" or "melodic". |
default_sample | String | Sole sample | Initial sample for SamplerMelodic; required when ambiguous. |
Oneshot plays each sample at a fixed pitch — standard for drums. Melodic repitches
samples relative to each sample’s root note — use it for tonal instruments.
[defaults] — bulk note mappings
Section titled “[defaults] — bulk note mappings”Map sample names to notes in one place instead of repeating note in every entry. Values
are note names ("C2", "F#3") or MIDI numbers (36, 42):
[defaults]bd = "C2"sd = "D2"hh = "F#2"[samples] — per-sample settings
Section titled “[samples] — per-sample settings”Each key defines a sample:
| Field | Type | Default | Description |
|---|---|---|---|
files | Array of strings | — | WAV paths under samples/ in the kit directory. Multiple files enable round-robin. |
note | String or number | Auto-assigned | MIDI note that triggers this sample ("C2" or 36). |
root | String or number | — | Root pitch for melodic mode ("C4" or 60). |
choke_group | Number | — | Samples sharing a group cut each other off. |
velocity | Number | 1.0 | Default velocity (0.0–1.0). |
loop_start | Number | — | Loop start, normalized (0.0–1.0). |
loop_end | Number | — | Loop end, normalized (0.0–1.0). |
A drum kit with a choke group (the open and closed hats cut each other):
[kit]name = "Lo-Fi Drums"
[samples]bd = { files = ["samples/kick.wav"] }sd = { files = ["samples/snare.wav"], note = "D2" }hh = { files = ["samples/hihat_closed.wav"], note = "F#2", choke_group = 1 }hh_open = { files = ["samples/hihat_open.wav"], note = "A#2", choke_group = 1 }Multiple files per sample give automatic round-robin variation:
[samples]sd = { files = ["samples/snare_1.wav", "samples/snare_2.wav", "samples/snare_3.wav"], note = "D2" }A melodic kit repitches relative to each root; use loop points for sustaining sounds:
[kit]name = "Tape Piano"mode = "melodic"default_sample = "piano"
[samples]piano = { files = ["samples/piano_c4.wav"], root = "C4" }pad = { files = ["samples/pad_c3.wav"], root = "C3", loop_start = 0.1, loop_end = 0.95 }SamplerMelodic(Kit("Tape Piano")) loads piano immediately. A melodic kit
with exactly one sample may omit default_sample; a kit with several samples
must declare it. Each selectable melodic sample names exactly one file—multiple
files are round-robin material for the drum sampler. The selected file and its
root must be valid when the device is constructed, so broken presets fail with
an error instead of producing a silent instrument.
Note Resolution
Section titled “Note Resolution”When a sample has no explicit note, resonon resolves it in order:
- Explicit
notein the[samples]entry. - A
[defaults]mapping. - The GM drum mapping below (by sample name).
- Sequential assignment from C2 (MIDI 36).
| Name | Aliases | Note |
|---|---|---|
bd | kick, bass | C2 (36) |
sd | snare | D2 (38) |
rs | rim, rimshot | C#2 (37) |
hh | hihat, hat, ch | F#2 (42) |
hh_open | hihat_open, oh | A#2 (46) |
cp | clap | D#2 (39) |
lt | tom, tom_low | A2 (45) |
mt | tom_mid | B2 (47) |
ht | tom_high, tom_hi | D3 (50) |
crash | cy | C#3 (49) |
ride | — | D#3 (51) |
cb | cowbell | G#3 (56) |
ma | maracas | A#4 (70) |
cl | clave | D#5 (75) |
lc | conga_low | E4 (64) |
mc | conga_mid | D#4 (63) |
hc | conga_high | D4 (62) |
With no kit.toml, each .wav becomes a sample named after its filename, notes come from
the table above (or sequentially from C2), and the mode defaults to oneshot — so a minimal
kit is just a folder of WAV files.
Native Extensions
Section titled “Native Extensions”A --native project adds a Rust crate under native/, a built-dylib directory lib/,
and a [native] section in resonon.toml. Build it with:
resonon build # build for the current platformresonon build --dist # platform-named output for binary distributionWriting native extensions — the resonon-ext crate, the #[ext_fn] macro, and calling
into Rust from .non — is covered in Going Native.
Publishing
Section titled “Publishing”Packages publish by pushing to GitHub — gh:user/repo maps directly to
https://github.com/user/repo.git. There’s no registry or publish command:
cd my-packagegit initgit add .git commit -m "Initial commit"gh repo create my-package --public --source=. --pushOthers then install it:
resonon install gh:yourname/my-packageVersioning. Tag releases with git tags. Consumers pin a tag in resonon.toml:
[dependencies]my-package = { source = "gh:yourname/my-package", version = "v1.0" }…or at install time with resonon install gh:yourname/my-package@v1.0. Without a pin,
install clones the latest commit on the default branch, and resonon.lock records the
exact commit so the install is reproducible.
Troubleshooting
Section titled “Troubleshooting”“Module ‘x’ not found. Searched: …” — The message lists every directory that was looked
in, tagged with why each one was searched. Check the package is installed (resonon pkg list)
and that you’re using the repository name, not the [kit].name from kit.toml. The bare name
(use "drum-kits") resolves to the package’s src/lib.non, and binds drum_kits — a hyphen
cannot be part of a name.
“Name collision: ‘x’ is both …” — A package bound a name something else in your file already had. A warning, not an error; see Troubleshooting.
“no lib.non or kits/ found” after install — The repository doesn’t follow the package
layout. Ensure code is at src/lib.non or kit WAVs are under kits/<kit-name>/.
Samples not loading — Confirm the kit directory holds .wav files (not .mp3/.flac)
and that the path has both parts: Sampler(Kit("package-name/kit-name")).
See Also
Section titled “See Also”- CLI Reference — the runtime CLI (
run, REPL, server). - Configuration — where each kind of setting lives, and the
~/.resononlayout. - Modules — the
useimport system. - Samplers — loading and playing sampler kits.
- Going Native — writing native Rust extensions.