Skip to content

The Evaluation Model

Most languages run a file top to bottom and exit. Resonon doesn’t work that way. You keep a session alive and grow it — evaluating a line here, a block there, each fragment landing in a runtime that remembers everything you’ve run so far. If you’ve used a DAW, the session is your project: you don’t rebuild it each time you touch a fader, you adjust the thing that’s already playing. This page is the mental model for how code becomes a living session.

Here’s a complete starting point. Write it into a .non file, select it, and execute it — we’ll trace what the runtime keeps afterward.

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

Once that runs, the drums track exists in the session. It isn’t torn down when the evaluation finishes — it stays loaded, stays playing, and stays bound to the name drums. So the next thing you execute can simply talk to it:

drums << [bd _ sd _, hh hh hh hh];

You didn’t redeclare the track or reload the sampler. You selected one line, ran it, and the pattern swapped underneath the still-running session. That persistence — the runtime holding your variables, tracks, instruments, and effects between evaluations — is the whole foundation of live coding in Resonon.

The primary way to drive a session is the select-and-execute workflow in Visual Studio Code: write Resonon in a .non file, select (highlight) the lines you want, and press Cmd+Enter. Place your cursor on a single line with no selection and the same key runs that whole line.

The first execution starts a Resonon runtime with an active server, then sends your code to it. Every later execution reuses that same runtime — which is exactly why the session accumulates. You aren’t restarting anything; you’re appending to a program that’s already running.

The REPL follows the identical model: each line you type is evaluated against one persistent runtime, so a variable defined on one line is available on the next. Whether you drive it from VSCode or the terminal, “running code” means evaluating a fragment against the live session.

Because the session is long-lived, it matters how a name gets its value.

let tempo = 120; // define: introduce a new binding
tempo = 96; // reassign: update the existing binding

let x = ... introduces (or redefines) a binding; a bare x = ... updates a name that already exists. The distinction has teeth when the value is a track: redefining a track variable with let orphans the old track — Resonon cleans it up and detaches it from the audio graph — and gives you a fresh one. Reassigning a plain value just swaps the number. In day-to-day live coding you mostly re-send patterns and tweak parameters on tracks that already exist, rather than rebuilding them.

Playback is controlled by four transport statements, each with a VSCode keybinding:

ActionCodeKeybinding
PlayPLAY;F5
PausePAUSE;F5 (toggle)
StopSTOP;Shift+F5
ResetRESET;Ctrl+Shift+F5
PLAY;
PAUSE;
STOP;
RESET;

PLAY starts (or resumes) playback; PAUSE halts it where it stands and toggles back on with PLAY. STOP halts playback, returns to the start, and mutes the output — but the session itself is untouched: your tracks, instruments, and variables are all still there, ready to play again.

RESET is the heavy one. It clears the entire session — every variable, track, instrument, and effect — and tears down the scheduler, leaving you a blank runtime. It’s the live-coding equivalent of closing the project and opening an empty one: the right move when a session has drifted somewhere you can’t easily edit your way out of.

You now have the core loop: a persistent runtime, fragments evaluated into it, and transport plus reset to control and clear it. The chapters build on this.