Skip to content

Package Management

This content is for v0.7. Switch to the latest version for up-to-date documentation.

Resonon packages bundle reusable code and sample kits that can be installed from GitHub repositories.

Install a package from GitHub using the gh:user/repo shorthand:

Terminal window
resonon pkg install gh:user/repo

Pin to a specific version tag with @:

Terminal window
resonon install gh:user/repo@v1.0

Inside a project (where resonon.toml exists), packages install to dependencies/ relative to the project root. Outside a project, they install to ~/.config/resonon/lib/. After installation, Resonon reports what the package contains — installed kits and code exports.

Terminal window
resonon pkg install gh:resonon/drum-kits
# Installing gh:resonon/drum-kits ...
# Installed 'drum-kits' [kits] to ~/.config/resonon/lib/drum-kits
# Kits:
# 808 — Sampler(Kit("drum-kits/808"))
# vinyl — Sampler(Kit("drum-kits/vinyl"))

To reinstall a package, use resonon update:

Terminal window
resonon update drum-kits

Update all dependencies to their latest versions:

Terminal window
resonon pkg update
# or
resonon update

Update a specific package:

Terminal window
resonon pkg update drums
# or
resonon update drums

Updates re-clone each package at its pinned version tag (or latest commit if no version is set), rebuild native extensions if present, and regenerate resonon.lock.

View all installed packages and their types:

Terminal window
resonon pkg list

Example output:

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"))

Each package shows its type and available kits:

TypeMeaning
[code]Has lib.non (importable code)
[kits]Has kits/ directory (sampler kits)
[code+kits]Has both code and kits

Kit entries also show the access path, e.g. Sampler(Kit("packagename/kitname")).

Get detailed information about an installed package:

Terminal window
resonon pkg inspect name

This shows:

  • Package metadata and documentation (from lib.non doc comments)
  • Code exports with function signatures
  • Available kits with sample counts and playback mode
Package: my-lib [code+kits]
A collection of utility functions and drum kits.
Exports:
fn swing_pattern(seed, density)
let default_bpm = <number>
Kits:
lo-fi — oneshot mode, 12 samples
Usage: Sampler(Kit("my-lib/lo-fi"))

Create a new project with resonon init (in current directory) or resonon new (creates a new directory):

Terminal window
# Initialize in current directory
resonon init my-project
# Create a new directory
resonon new my-project

Both commands accept flags to create different project types:

FlagDescription
(none)Plain project with main.non
--libLibrary project with lib.non
--kitKit project with kits/ directory
--nativeNative extension project with Rust crate

Flags can be combined:

Terminal window
resonon new my-ext --lib --native # Library with native extension
resonon new my-pkg --lib --kit # Library with sample kits

Each project gets a resonon.toml manifest, README.md, LICENSE (MIT), .gitignore, and a git repository.

A Resonon package is a directory (typically a git repo) that contains a lib.non file, a kits/ directory, or both. There’s no build step or manifest file — just write code or add samples.

Create a code package by writing a lib.non file at the root of your project:

Terminal window
mkdir my-utils
cd my-utils

Write lib.non with your exported functions and values:

/// Utility functions for generative rhythms.
/// Provides euclidean and probabilistic pattern helpers.
/// Generate a swing pattern from a seed value.
/// Returns a pattern with alternating long/short durations.
fn swing(seed, amount) {
let base = euclid(seed, 16);
base.swing(amount)
}
/// Generate a fill that ramps in density.
fn ramp_fill(steps, density) {
let result = #[];
for i in range(steps) {
if rand(i) < density * (i / steps) { result.push(bd); } else { result.push(~); }
}
return Pattern(result);
}
/// Default BPM for live performance templates.
let default_bpm = 120;
/// Internal helper — not exported.
private fn quantize_to_grid(value, grid) {
round(value * grid) / grid
}

Key points:

  • All top-level fn, let, and class definitions are exported by default — consumers can access them via packagename.name
  • Mark items private to keep them internal: private fn helper() { ... }, private let internal_val = 42;, or private class Internal { ... }
  • Add /// doc comments above functions and values — they appear in resonon pkg inspect output
  • The first doc comment block (before any code) becomes the package description

Create a kit package by organizing WAV files under a kits/ directory:

Terminal window
mkdir -p my-drums/kits/808
# Copy your WAV files into the kit directory
cp kick.wav snare.wav hihat.wav my-drums/kits/808/

Optionally add a kit.toml for explicit configuration (see Kit Configuration Reference below):

[kit]
name = "808 Classics"
author = "Your Name"
[samples]
bd = { files = ["kick.wav"], note = "C2" }
sd = { files = ["snare.wav"], note = "D2" }
hh = { files = ["hihat.wav"], note = "F#2", choke_group = 1 }

A package can provide both code and kits — just include both lib.non and kits/ in the same repo:

studio-tools/
lib.non # Helper functions
kits/
lo-fi/
kit.toml
kick_tape.wav
snare_vinyl.wav
piano/
kit.toml
piano_c4.wav

Users can then import code and load kits from a single package:

use "std/instruments" { Sampler, Kit };
use "studio-tools";
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("studio-tools/lo-fi")));
// Use exported helper functions
let pattern = studio-tools.swing(42, 0.6);
drums << pattern;
PLAY;

Walkthrough: Creating a Package from Scratch

Section titled “Walkthrough: Creating a Package from Scratch”

Here’s a complete example creating a drum kit package:

Terminal window
# 1. Create the directory structure
mkdir -p beat-box/kits/vinyl
cd beat-box
# 2. Add your samples
cp ~/samples/kick_dusty.wav kits/vinyl/
cp ~/samples/snare_warm.wav kits/vinyl/
cp ~/samples/hihat_crispy.wav kits/vinyl/
# 3. Write a kit.toml for explicit note mappings
cat > kits/vinyl/kit.toml << 'EOF'
[kit]
name = "Vinyl Drums"
author = "Your Name"
[samples]
bd = { files = ["kick_dusty.wav"] }
sd = { files = ["snare_warm.wav"] }
hh = { files = ["hihat_crispy.wav"], choke_group = 1 }
EOF
# 4. Test locally — Resonon searches the current directory too
resonon -e 'use "std/instruments" { Sampler, Kit }; let d = AudioTrack("d"); d.load_instrument(Sampler(Kit("beat-box/vinyl"))); d << [bd sd bd sd]; PLAY;'

The kit.toml file controls how samples are mapped and played. All sections are optional — Resonon auto-detects WAV files if no kit.toml is present.

FieldTypeDefaultDescription
nameStringDirectory nameDisplay name for the kit
authorStringKit author
modeString"oneshot"Playback mode: "oneshot" or "melodic"
[kit]
name = "Lo-Fi Drums"
author = "Producer Name"
mode = "oneshot"

Oneshot mode plays each sample at a fixed pitch — standard for drum kits. Melodic mode repitches samples based on the incoming MIDI note relative to the root note — use this for tonal instruments like piano or synth samples.

Map sample names to MIDI notes in bulk. Useful when you have many samples and want consistent mappings without repeating note in every [samples] entry:

[defaults]
bd = "C2"
sd = "D2"
hh = "F#2"
hh_open = "A#2"
cp = "D#2"

Values can be note names ("C2", "F#3") or MIDI numbers (36, 42).

Each key in the [samples] table defines a sample with its settings:

FieldTypeDefaultDescription
filesArray of stringsWAV file paths (relative to kit directory). Multiple files enable round-robin playback
noteString or numberAuto-assignedMIDI note that triggers this sample ("C2" or 36)
rootString or numberRoot pitch for melodic mode ("C4" or 60)
choke_groupNumberChoke group (samples in the same group cut each other off)
velocityNumber1.0Default velocity (0.01.0)
loop_startNumberLoop start point (0.01.0, normalized position)
loop_endNumberLoop end point (0.01.0, normalized position)
[samples]
bd = { files = ["kick.wav"] }
sd = { files = ["snare.wav"], note = "D2" }
hh = { files = ["hihat_closed.wav"], note = "F#2", choke_group = 1 }
hh_open = { files = ["hihat_open.wav"], note = "A#2", choke_group = 1 }
cp = { files = ["clap.wav"], note = "D#2" }

Provide multiple files per sample for automatic round-robin variation:

[samples]
sd = { files = ["snare_1.wav", "snare_2.wav", "snare_3.wav"], note = "D2" }
hh = { files = ["hat_soft.wav", "hat_med.wav", "hat_hard.wav"], note = "F#2" }

Each trigger cycles through the files in order, adding natural variation.

For tonal instruments, set mode = "melodic" and specify a root note for each sample:

[kit]
name = "Tape Piano"
mode = "melodic"
[samples]
piano = { files = ["piano_c4.wav"], root = "C4" }
pad = { files = ["pad_c3.wav"], root = "C3", loop_start = 0.1, loop_end = 0.95 }

The root tells Resonon the original pitch of the recording. Incoming notes are repitched relative to this root — so a piano_c4.wav sample with root = "C4" plays at original pitch when triggered by C4, and shifts up/down for other notes. Use loop_start and loop_end for sustaining sounds like pads and strings.

When a sample doesn’t have an explicit note, Resonon resolves it in this order:

  1. Explicit note in the sample’s [samples] entry
  2. Default mapping from the [defaults] section
  3. GM drum mapping based on the sample name (see table below)
  4. Sequential assignment starting from C2 (MIDI 36)

Common GM drum mappings recognized by name:

Sample NameAliasesMIDI Note
bdkick, bassC2 (36)
sdsnareD2 (38)
rsrim, rimshotC#2 (37)
hhhihat, hat, chF#2 (42)
hh_openhihat_open, ohA#2 (46)
cpclapD#2 (39)
lttom, tom_lowA2 (45)
mttom_midB2 (47)
httom_high, tom_hiD3 (50)
crashcyC#3 (49)
rideD#3 (51)
clclaveD#5 (75)
cbcowbellG#3 (56)
mamaracasA#4 (70)
lcconga_lowE4 (64)
mcconga_midD#4 (63)
hcconga_highD4 (62)

If a kit directory contains WAV files but no kit.toml, Resonon auto-detects:

  • Each .wav file becomes a sample named after the filename (without extension)
  • Notes are assigned via the GM drum mapping table above, or sequentially from C2 for unrecognized names
  • Mode defaults to "oneshot"

This means a minimal kit is just a folder of WAV files:

kits/
quick-kit/
bd.wav
sd.wav
hh.wav

No kit.toml needed — Sampler(Kit("my-package/quick-kit")) works immediately.

Resonon packages are published by pushing to GitHub. There’s no registry or publish command — gh:user/repo maps directly to https://github.com/user/repo.git.

  1. Initialize a git repo in your package directory:

    Terminal window
    cd my-package
    git init
    git add .
    git commit -m "Initial commit"
  2. Create a GitHub repository and push:

    Terminal window
    gh repo create my-package --public --source=. --push

    Or add a remote manually:

    Terminal window
    git remote add origin git@github.com:yourname/my-package.git
    git push -u origin main
  3. Others install it with:

    Terminal window
    resonon pkg install gh:yourname/my-package

Use clear, descriptive names that indicate the package contents:

  • drum-kits — a collection of drum sample kits
  • resonon-utils — general utility functions
  • lofi-textures — lo-fi sample kits
  • generative-tools — generative/algorithmic composition helpers

A well-structured package repo typically contains:

my-package/
lib.non # Code entry point
kits/ # Sample kits
kit-name/
kit.toml
*.wav
README.md # Usage instructions
LICENSE
.gitignore

Add a .gitignore to keep your repo clean:

# DAW project files
*.als
*.flp
*.logic
*.ptx
# OS files
.DS_Store
Thumbs.db
# Editor files
*.swp
*~

Use git tags to version your package. Consumers can pin to a specific tag in resonon.toml:

[dependencies]
my-package = { source = "gh:yourname/my-package", version = "v1.0" }

Or pin at install time:

Terminal window
resonon install gh:yourname/my-package@v1.0

Without a version pin, resonon install clones the latest commit on the default branch. The resonon.lock file records the exact commit installed for each dependency. When you push updates, consumers run resonon update to get the new version.

Packages follow a simple directory convention:

my-package/
resonon.toml # Project manifest (optional)
resonon.lock # Dependency lock file (auto-generated)
lib.non # Code entry point (optional)
kits/ # Sample kits directory (optional)
kit-name/
kit.toml # Kit configuration (optional)
kick.wav
snare.wav
...
native/ # Rust crate for native extensions (optional)
Cargo.toml
src/lib.rs
lib/ # Built native dylibs (optional)

A package must have at least one of:

  • lib.non at the package root — makes it a code package. All top-level fn, let, and class definitions are exported unless marked private.
  • kits/ directory with one or more subdirectories containing .wav files — makes it a kit package. Each subdirectory is a separate kit.

Both can coexist in a single package ([code+kits]).

Inside a project, installed packages live in dependencies/. Outside a project, they install to ~/.config/resonon/lib/. Resonon searches both locations (and the current working directory) when resolving use imports and Sampler() paths.

Projects that track dependencies use a resonon.toml manifest file. It’s created automatically by resonon init or resonon new.

[package]
name = "my-project"
version = "0.1.0"
authors = ["Your Name <email>"]
resonon = "0.3.0"
[dependencies]
drums = { source = "gh:resonon/drum-kits", version = "v1.0" }
synths = { source = "gh:user/synths" }
[native]
crate = "native"
FieldRequiredDescription
nameYesPackage name
versionNoPackage version
authorsNoList of authors
resononNoMinimum Resonon version

Each dependency is a key-value pair where the key is the package name and the value specifies:

FieldRequiredDescription
sourceYesPackage source (gh:user/repo)
versionNoGit tag to pin to (e.g. "v1.0")

Only present for packages with native Rust extensions. See Native Extensions.

FieldRequiredDescription
crateYesPath to the native Rust crate (relative to project root)

Import code from a package with use:

use "packagename";
// Access exported functions and values
let result = packagename.my_function(args);

Load kits from a package with Sampler:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("packagename/kitname")));
drums << [bd sd bd sd];
PLAY;

“Package not found” when using use — Check that the package is installed (resonon pkg list) and that you’re using the correct name. The package name is the repository name, not the [kit].name from kit.toml.

“No lib.non or kits/ found” after install — The repository exists but doesn’t follow the package structure convention. Ensure lib.non is at the repo root or kit WAV files are inside kits/{kit-name}/.

Samples not loading — Verify the kit directory contains .wav files (not .mp3, .flac, etc.). Check the path format: Sampler(Kit("package-name/kit-name")) — both parts are required.

Reinstalling a package — Use resonon update <name> to pull the latest version. If that doesn’t resolve the issue, remove and reinstall manually:

Terminal window
rm -rf dependencies/package-name # or ~/.config/resonon/lib/package-name
resonon install gh:user/package-name