Skip to content

Modules

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

Import a module with use. The imported module is bound to a namespace variable.

use "../lib_example/lib" as lib;
// Access top-level exports
PRINT lib.default_bpm; // 120
// Access sub-module functions
PRINT lib.patterns.kick(); // [C1 C1 C1 C1]
PRINT lib.patterns.arp(); // [C4 E4 G4 C5]

If no as alias is given, the alias is derived automatically:

  • For regular files, the alias is the file stem (filename without .non): use "./patterns" binds to patterns.
  • For lib.non files, the alias is the parent directory name: use "./drums/lib" binds to drums.

The .non extension is appended automatically when missing, so use "./patterns" and use "./patterns.non" are equivalent.

use "./scales"; // binds to `scales`
use "./drums/lib"; // binds to `drums`
use "./utils" as helpers; // explicit alias overrides derivation

Any .non file loaded via use becomes a module. Its public definitions form the module’s exports:

  • let bindings (including computed expressions)
  • fn definitions
  • class definitions
  • Nested use imports (re-exported as named fields)
theory.non
use "./scales"; // re-exported as theory.scales
let middle_c = C4; // exported
let default_vel = 100; // exported
let doubled = 21 * 2; // exported (computed at import time)
fn octave_up(note) { // exported
return note + 12;
}
class Interval { // exported
let semitones;
fn new(s) {
this.semitones = s;
}
}

Mark variables, functions, and classes as private to prevent them from being exported. Private members are only accessible within the module that defines them.

drums.non
private let BASE_VEL = 0.8; // internal only
private fn humanize(pattern) { // internal only
return pattern * vel(BASE_VEL);
}
private class InternalState { // internal only
let value;
}
let default_bpm = 120; // public
/// Four-on-the-floor kick pattern.
fn kick() { // public
return humanize([C1 C1 C1 C1]);
}

Attempting to access a private member from outside the module produces an error:

Cannot access private 'BASE_VEL' in module 'drums'

The show() function displays private items in a separate section, so you can inspect a module’s full contents without accessing them. See Inspecting modules in the REPL.

Resonon resolves use paths in the following order:

  1. Relative paths — paths starting with ./ or ../, resolved from the current file
  2. Absolute paths — paths starting with /
  3. Library search paths — checked in order from the configured lib_paths

At each step, Resonon first tries the path as a directory (looking for <dir>/lib.non), then as a file (appending .non if needed).

use "./patterns"; // 1. relative
use "/opt/resonon/utils"; // 2. absolute
use "my_library/lib"; // 3. library search paths

Library search paths are configured in ~/.config/resonon/config.toml:

lib_paths = [
"~/resonon-libs",
"/shared/team-libs",
]

When lib_paths is not set (or empty), two defaults are used:

  1. ./lib — a lib/ directory relative to the working directory
  2. ~/.config/resonon/lib — the user-level library directory

Tilde (~) is expanded to your home directory in all paths.

See also: Configuration reference and Package management guide.

A library is a directory with a lib.non entry point. Importing the directory loads lib.non automatically.

drums/
lib.non ← entry point
patterns.non ← sub-module
fills.non ← sub-module
lib.non
use "./patterns";
use "./fills";
let default_vel = 100;
// Consumer code
use "drums";
drums.patterns.kick();
drums.fills.intro();
PRINT drums.default_vel; // 100

A use statement inside a module re-exports the imported module as a named field. This lets you build multi-level namespaces.

theory/lib.non
use "./scales";
use "./chords";
main.non
use "theory";
let mode = theory.scales.dorian(C4);
let triad = theory.chords.major(C4);

Each nested module is a separate .non file that follows the same export rules — let bindings, fn definitions, class definitions, and its own nested use imports are all available through dot access.

Resonon ships with a standard library compiled into the binary. It is split into a prelude (auto-loaded, always available) and modules (imported on demand with use).

The prelude is loaded automatically — no import needed. Everything available in a fresh REPL comes from the prelude.

AreaExports
CoreArray, String, Dict, Iterator, clock, reset, type, show, range, map, filter, fold
PatternsPattern, Sequence, Stack, Alternating, euclid, viz
MIDIMidiTrack, midi_connect, midi_ports, midi_routing, midi_clock_*
TheoryScale, Key, scale, chord, interval
AudioAudioTrack, Master, Event, setbpm, render, audio_devices, routing, project_*
Cycle callbackson_cycle, off_cycle, prime_cycles

Modules are imported with use "std/...". Use destructuring to import specific names:

use "std/effects" { Delay, Lowpass, Reverb };
use "std/instruments" { Sampler, Kit, SamplerMelodic };
use "std/signals" { Sine, Saw, automation };
use "std/random" { rand, choose, choose_weighted };
use "std/generative" { stream, markov };
ModuleExports
std/effectsDelay, Lowpass, Highpass, Bandpass, Notch, Allpass, Peak, Reverb, PlateReverb, Overdrive, Distortion, Effect (CLAP), Vst3Effect
std/instrumentsSample, Kit, Sampler, SamplerMelodic, ClapInstrument, Vst3Instrument, Instrument, plugin_scan, plugin_list
std/signalsSignal, Sine, Saw, Tri, Square, Rand, Perlin (+ bipolar variants Sine2, Saw2, Tri2, Square2), hz, signal, automation, Cc, Cc_learn
std/randomrand, choose, choose_weighted, rand_true, choose_true
std/generativestream, markov
use "std/effects" { Delay, Lowpass };
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
drums << [bd sd bd sd];
drums.load_effect(Delay(0.25, 0.5));
drums.load_effect(Lowpass(800));
PLAY;

Use show() to inspect a module’s contents.

use "../lib_example/lib" as lib;
show(lib);

Output:

┌────────────────────────────────────────┐
Module: lib (from /path/to/lib.non)
Library entry point
Exports:
patterns: Module
default_bpm: Number
fn rest(beats) - Generate rests for a number of beats
Private:
INTERNAL_VEL: Number
└────────────────────────────────────────┘

The output shows:

  • Header — module name and file path
  • Module doc — first line of the module-level /// comment (if present)
  • Exports — functions (with signatures and doc summaries), variables (with types), and nested modules
  • Private — private items with their types, visible for inspection but not accessible in code

Use /// comments to document modules and their exports. These are preserved at parse time and displayed by show().

Place /// comments at the top of the file, before any use, let, or fn statements:

/// Drum pattern library.
/// Provides kick, snare, and hi-hat patterns.
use "./fills";
let default_vel = 100;

Place /// comments directly above a fn or let binding:

/// Base velocity for all drum hits.
let default_vel = 100;
/// Four-on-the-floor kick pattern.
fn kick() {
return [C1 C1 C1 C1];
}
  • A /// comment attaches to the next fn or let statement.
  • A blank line between a /// block and the next statement breaks the attachment — the orphaned comment becomes the module doc (if no module doc exists yet) or is discarded.
  • Doc comments above use statements are not attached to the import; they become the module doc if eligible.

If two modules import each other, Resonon detects the cycle and returns an empty module (no exports) for the already-loaded file rather than looping forever.

a.non
use "./b"; // loads b, which tries to load a again
PRINT b.value; // b has exports
// b.non
use "./a"; // a is already being loaded → empty module
PRINT a.value; // ERROR: a has no exports here
  • Module files: snake_case.non (e.g., drum_patterns.non)
  • Library entry points: always lib.non
  • Imports: use descriptive aliases (use "drums/lib" as drums;)

A self-contained drum library with private helpers and documented exports.

drums/lib.non
/// Acoustic drum kit patterns.
/// Velocity-humanized with configurable intensity.
use "./fills";
private let BASE_VEL = 0.75;
/// Applies velocity humanization to a pattern.
private fn humanize(pat) {
return pat * vel(BASE_VEL + rand(0.1));
}
/// Standard four-on-the-floor kick.
fn kick() {
return humanize([C1 C1 C1 C1]);
}
/// Offbeat hi-hat pattern.
fn hihat() {
return humanize([_ F#1 _ F#1]);
}
/// Backbeat snare on 2 and 4.
fn snare() {
return humanize([_ D1 _ D1]);
}
drums/fills.non
/// Drum fill for transitions.
fn crash_fill() {
return [C1 D1 F#1 C#2];
}

Consumer code:

use "drums";
Track("Drums", 10)
>> drums.kick()
>> drums.snare()
>> drums.hihat();

A project with shared utilities and multiple performance files.

my_project/
lib/
shared/
lib.non ← shared scales and utilities
scales.non
songs/
verse.non
chorus.non
main.non ← entry point
lib/shared/lib.non
/// Shared music theory utilities.
use "./scales";
let root = C4;
let bpm = 128;
lib/shared/scales.non
/// Minor pentatonic from a given root.
fn minor_penta(root) {
return [root, root+3, root+5, root+7, root+10];
}
main.non
use "./lib/shared" as shared;
set_bpm(shared.bpm);
let notes = shared.scales.minor_penta(shared.root);
PRINT notes; // [C4 Eb4 F4 G4 Bb4]