Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Conchordal is a research-composer scripting surface. The central idea is not note scheduling. The central idea is shaping a perceptual consonance field, then letting populations of voices move, survive, and reorganize inside it.

A scenario script defines Materials (voice templates), places them into the field as Participants, and shapes the terrain they live on. Harmony emerges from psychoacoustics (roughness and harmonicity); rhythm emerges from coupled oscillators on a shared meter the population itself drives. The script is a director, not a sequencer.

Conchordal v0.4.0 is aimed at research composers who want to work with these concepts directly. It is not trying to hide the model behind common music-production vocabulary.

How this book is organized

  • Tutorial gets a first sound out and your editor wired up: Quick Start, Editor Setup.
  • Concepts explains the three pillars of the model: the Consonance Field, Rhythm, and Routing and the Listener Twin.
  • Reference is the complete, generated API Reference — it is produced from the engine’s registered scripting surface, so it cannot drift — and the Curated Samples listening path. The reference is split into three tiers: the Core API (enough for every curated sample), Mechanism Tuning, and Research Controls. Start with Core and ignore the rest until a piece demands it.

Every rhai code block in this book is executed against the real script engine by the test suite, so the examples are guaranteed to run.

Quick Start

Run a scenario script with the real-time instrument (release mode is recommended for real-time DSP):

cargo run --release -- samples/01_a_single_voice.rhai

Minimal Sound

place(sine().amp(0.08).sustain(), at(440.0));
wait(2.0);

place(material, placement) stages a Participant at the current script time. It is committed by wait(seconds) or flush(), then can be patched while it is alive.

Basic Objects

  • Materials are voice templates made with sine(), harmonic(), modal(), saw(), square(), and noise().
  • Variants clone a material with variant(material).
  • Placements decide where participants enter: field targets consonance(), dissonance(), edge(), gap() (cloud by default, .peak() for the extremum), plus random(), at(), and line().
  • Participants are the handles returned by place(). Before the next wait() or flush(), participant builder methods still shape the initial spawn; after that, patchable methods update running voices.
  • Sections scope participants and release them automatically.
let voice = harmonic()
    .amp(0.08)
    .sustain()
    .brightness(0.35);

section("plain entry", || {
    place(voice, line(220.0, 440.0).count(3));
    wait(4.0);
});

Placing Into the Field

consonance(root_hz).peak() places voices at high Consonance Field positions around a root. The field is shaped by what the system perceives — an anchor changes where the peaks are.

let anchor = harmonic()
    .brain("drone")
    .amp(0.06)
    .sustain()
    .anchor();

let voice = harmonic()
    .amp(0.04)
    .sustain();

section("field placement", || {
    place(anchor, at(110.0));
    wait(1.0);

    place(voice, consonance(110.0).peak().range(1.0, 4.0).count(6).spacing(0.9));
    wait(6.0);
});

How the field works — and how voices move, survive, and respawn inside it — is the subject of The Consonance Field.

Live Patching

Some participant methods patch running voices; others only shape the draft and must be set before the group is committed. The API Reference tags every method as live-patchable or draft-only.

let g = place(
    harmonic().amp(0.04).sustain(),
    consonance(220.0).peak().count(3)
);
wait(2.0);     // commit: the group is now live

g.amp(0.02);   // live patch on running voices
g.glide(0.8);
wait(3.0);
release(g);

A Complete Miniature

seed(7);

let anchor = harmonic()
    .brain("drone")
    .amp(0.05)
    .sustain();

let colony = harmonic()
    .amp(0.035)
    .sustain()
    .seek_consonance()
    .glide(0.4)
    .avoid_neighbors(0.6);

section("emergence", || {
    place(anchor, at(110.0));
    wait(2.0);

    place(colony, consonance(90.0, 900.0).count(8).spacing(0.8));
    wait(8.0);
});

Where to go next

  • Editor Setup — completion, hover docs, and diagnostics for the whole scripting surface.
  • The Consonance Field — field, density, movement, viability, respawn.
  • Rhythm — the coupling continuum and the director’s rhythmic terrain.
  • Curated Samples — the guided listening path.

Editor Setup (LSP)

Conchordal ships a Rhai LSP definition file describing the entire scripting surface, including hover documentation for every function. Hooking your editor up to it gives you completion, hover, go-to-def, and inline diagnostics for every conchordal function — place, harmonic, .brain(), .send(habitat_bus), and so on.

The two files that drive this are committed at the repo root:

  • Rhai.toml — workspace config picked up by rhai-lsp
  • rhai-defs/conchordal.d.rhai — auto-generated type/fn declarations with doc comments

Install the rhai-lsp server once (it is not on crates.io; install directly from the git repo):

cargo install --git https://github.com/rhaiscript/lsp rhai-cli

This builds a binary named rhai with the lsp subcommand.

Then wire your editor:

VS Code

The official Rhai extension currently provides syntax highlighting only. It does not launch rhai-lsp. For LSP features in VS Code, use an LSP client/extension that can launch this command from the conchordal workspace:

rhai lsp stdio --config Rhai.toml

Neovim (nvim-lspconfig)

require("lspconfig").rhai.setup({
  cmd = { "rhai", "lsp", "stdio" },
  filetypes = { "rhai" },
  root_dir = require("lspconfig.util").root_pattern("Rhai.toml", ".git"),
})

Helix

In ~/.config/helix/languages.toml:

[[language]]
name = "rhai"
scope = "source.rhai"
file-types = ["rhai"]
language-servers = ["rhai-lsp"]

[language-server.rhai-lsp]
command = "rhai"
args = ["lsp", "stdio"]

Emacs (eglot)

(add-to-list 'eglot-server-programs
             '(rhai-mode . ("rhai" "lsp" "stdio")))
(add-hook 'rhai-mode-hook #'eglot-ensure)

Regenerating the definition file

The definition file and the API Reference are both generated from the engine’s register_fn surface joined with the documentation registry (src/scripting/docs.rs). If you pull a new conchordal version and miss diagnostics, regenerate both:

cargo run --bin gen_rhai_defs

CI tests fail whenever the committed artifacts are stale, so in a clean checkout they are always in sync with the engine.

The Consonance Field

Conchordal’s perception core (the Landscape) listens to the habitat bus, transforms it into log-frequency space, and computes two potentials: roughness (sensory dissonance from amplitude fluctuations within critical bands) and harmonicity (periodicity and template matching). Their combination is the Consonance Field: an evaluation terrain over frequency that placement, movement, prediction, and survival all read from.

Because the field is computed from what the system actually hears, every voice deforms the terrain for every other voice. That feedback loop — not a chord chart — is where harmony comes from.

Placing into the field: consonance, dissonance, edge, gap

A field-relative placement names a target — a region of the field — and is realized either as a cloud (the default) or as a deterministic extremum with .peak(). The targets are:

  • consonance — high-consonance positions (harmonic centers, fusion).
  • dissonance — low-consonance positions (tension, clusters, color).
  • edge — the consonance/dissonance boundary (the metastable middle).
  • gap — empty registers (fill the room, avoid masking).

consonance(root) takes a harmonic window around a root (multiples via range()); every target also takes an absolute (min_hz, max_hz) range.

let anchor = harmonic()
    .brain("drone")
    .amp(0.06)
    .sustain()
    .anchor();

let voice = harmonic()
    .amp(0.04)
    .sustain();

section("field placement", || {
    place(anchor, at(110.0));
    wait(1.0);

    place(voice, consonance(110.0).peak().range(1.0, 4.0).count(6).spacing(0.9));
    wait(6.0);
});

A target with no modifier is a density cloud: not “random but harmonic” but a normalized distribution derived from the consonance model, well-defined inside the range. Placement is independent of behavior — enter at dissonance and anchor() for a held cluster, or enter at dissonance and seek_consonance() for a resolution gesture.

let cloud = harmonic().amp(0.035).sustain();

place(cloud, consonance(90.0, 1200.0).count(10).spacing(0.8));
wait(8.0);

Placement tension: tension(τ)

consonance aims at the strongest peak. tension(τ) aims a step below it: τ ∈ [0, 1] is the tension degree, where 0 keeps the resolved peak and larger values target progressively weaker, metastable steps (target = L_max − τ·(L_max − L_min) over the range, in field score). It is the placement twin of movement’s search temperature — the dial for how resolved a spawn should sit — and it reads the field’s score directly, so the degree rides the terrain’s own scale rather than the count of peaks that happen to be sounding. With .peak() it snaps to the nearest step; as a cloud it concentrates the distribution around the target.

let tense = harmonic().amp(0.035).sustain();

// A metastable step below the strongest peak — placed, not resolved.
place(tense, consonance(110.0, 1200.0).peak().tension(0.4).count(6).spacing(0.8));
wait(6.0);

The field-agnostic placements are random(min_hz, max_hz) (log-uniform) and the geometric at(hz) and line(start_hz, end_hz).

Consonance Movement: seek_consonance

Use seek_consonance() when voices should actively seek better field positions. It sets free hill-climb movement with glide defaults. Use glide(tau_sec) when the musical thought is “same movement idea, slower or faster pitch motion”.

let mover = harmonic()
    .amp(0.045)
    .sustain()
    .seek_consonance()
    .glide(0.35)
    .avoid_neighbors(0.6)
    .global_peaks(8, 70.0)
    .ratio_candidates(5);

place(mover, consonance(80.0, 900.0).count(8));
wait(12.0);

avoid_neighbors(strength) adds crowding repulsion so movers spread out instead of collapsing onto the same peak.

The opposite of movement is anchor(): an anchored voice holds its pitch and only deforms the terrain for others. Voices placed with at() or given freq() are anchored implicitly; use anchor() to freeze strategy-placed voices at their settled position.

How movement lands is resolved from phonation: sustained movers glide, re-attacking movers (pulse(), metric(), entrained(), flow()) snap to their new pitch at each onset. Override with pitch_apply_mode() when a script needs the other behavior. Mechanism-level controls (pitch_core() and the hill-climb / peak-sampler tuning in the API Reference) remain available for research scripts; prefer seek_consonance() and glide() in curated work.

Consonance Viability and Respawn

Viability makes field fit matter over time. consonance_viability(low, high) defines the consonance window, and viability_rate(rate) controls continuous recharge: a voice in a well-fitting place is sustained, a voice in a poor place starves.

By default viability uses environment-relative scoring: a voice is evaluated against the field with its own footprint approximately removed. Use viability_scope("total") only when the compositional question is explicitly total-field viability.

Respawn closes the loop into an ecology: when voices die, replacements appear according to a respawn policy. respawn_consonance() draws them from consonance-biased parental peaks; respawn_capacity(count) keeps the population bounded; respawn_settle(placement) decides where replacements settle.

let settle = consonance(70.0, 1100.0).spacing(0.8);

let ecology = harmonic()
    .amp(0.04)
    .repeat()
    .pulse(1.5)
    .cycles(3)
    .seek_consonance()
    .glide(0.45)
    .initial_energy(0.7)
    .energy_cap(1.0)
    .metabolism(0.09)
    .action_cost(0.012)
    .viability_rate(0.18)
    .consonance_viability(0.32, 0.82)
    .respawn_consonance()
    .respawn_capacity(14)
    .respawn_settle(settle);

place(ecology, consonance(70.0, 1100.0).count(14));
wait(30.0);

The full lifecycle surface (energy, metabolism, costs) and the respawn policies are documented in the API Reference.

Landscape-aware timbre

The field can shape timbre as well as pitch. The modal() body takes a mode pattern, and landscape-aware patterns sample the live field, so a bell’s partials can sit where the terrain already supports them.

let shimmer_modes = landscape_density_modes()
    .count(10)
    .range(1.0, 5.5)
    .gamma(1.6)
    .spacing(0.7);

let shimmer = modal()
    .amp(0.025)
    .sustain()
    .seek_consonance()
    .modes(shimmer_modes)
    .brightness(0.7);

place(shimmer, consonance(200.0, 1600.0).count(4));
wait(8.0);

Mode constructors include harmonic_modes(), odd_modes(), power_modes(beta), stiff_string_modes(stiffness), custom_modes([ratios]), modal_table(name), landscape_density_modes(), and landscape_peaks_modes() — see the API Reference.

Rhythm: One Coupling Continuum

Rhythm in conchordal is one coupling continuum on a shared emergent meter, not a set of independent clocks. The population drives a single production meter (a coupled-oscillator beat); each voice is a phase oscillator that entrains its onset phase to that emergent beat with a coupling strength. There is no externally imposed grid — coherence (or its absence) emerges from how tightly each voice locks to the meter the population itself drives.

The three rhythm “families” are just three regions of the continuum:

  • metric: high coupling — a deep attractor, reads as a stable pulse.
  • entrained: medium coupling — synchronization emerges over time, still drifts.
  • flow: near-zero coupling — a free renewal process, non-metric texture.

Rhythm is part of the same ecology as consonance, viability, movement, and respawn: timing can affect survival, and survival reorganizes timing.

Director-level terrain

The director shapes the rhythmic terrain, symmetric to the consonance-field operations. These are soft priors, never a schedule — emergence still does the work:

  • meter_stability(value) — attractor depth in [0,1]: how readily a pulse forms. It only deepens the basin for a real periodicity; it never fabricates a beat from non-metric input.
  • temporal_basin(min_hz, max_hz) — the tempo region the emergent beat gravitates toward (the time-axis analogue of consonance(min, max)). It shapes the terrain; it does not place a beat, and it never forces a measure.

Per-voice presets and modifiers

The Tier-1 presets take no rate argument: metric(), entrained(), and flow(). The tempo region is a property of the terrain, set once at the director level, not per voice. They work on both a Material and a draft Participant.

Per-voice modifiers refine where on the continuum a voice sits:

  • entrainment(strength) — coupling in [0,1], free (0) .. locked (1).
  • rhythm_role("beat"|"subdivision"|"accent"|"texture") — the voice’s metrical job. accent emits a stronger onset that drives the shared meter harder, so a recurring downbeat can seed an emergent measure.
  • microtiming(amount) — a signed beat-phase offset in [-0.5, 0.5]. 0.5 places a voice a half-beat off, reading as syncopation.
meter_stability(0.85);     // attractor depth: how readily a pulse forms
temporal_basin(1.8, 2.2);   // tempo region the emergent beat gravitates toward

let beat = harmonic()
    .metric()
    .rhythm_role("accent")  // a strong onset that drives the shared beat
    .cycles(2);

let entrained = harmonic()
    .entrained()
    .cycles(2);

let drift = harmonic()
    .flow()
    .cycles(1);

let offbeat = harmonic()
    .metric()
    .microtiming(0.5)       // a half-beat offset reads as syncopation
    .cycles(2);

place(beat, at(110.0));
place(entrained, consonance(110.0).peak().count(3));
place(drift, consonance(300.0, 1200.0).count(4));
place(offbeat, at(220.0));
wait(12.0);

Calls on the same axis are last-write-wins: the last timing mode and the last duration mode determine the final behavior. Modifiers are remembered and applied when their matching preset is selected, so entrainment(0.8).metric() and metric().entrainment(0.8) are equivalent. The same applies to duration_range(...).adaptive_duration() and the reverse order.

Explicit when/duration (Tier 2)

Below the presets sit explicit controls: once(), pulse(rate_hz), while_alive(), cycles(n), and adaptive_duration() (with duration_range, duration_curve, and shorten_on_drop for tuning).

Timing as survival (Tier 3)

Use rhythm_coupling_vitality(lambda_v, v_floor) and rhythm_reward(rho_t, "attack_phase_match") when timing should affect survival and reorganization, and rhythm_freq(freq_hz) to set the internal oscillator directly:

let pulse_voice = harmonic()
    .repeat()
    .pulse(2.0)
    .cycles(2)
    .rhythm_freq(2.0)
    .rhythm_coupling_vitality(0.8, 0.4)
    .rhythm_reward(0.4, "attack_phase_match");

place(pulse_voice, at(165.0));
wait(8.0);

Scaffolds (research controls)

The scaffold functions impose an external pulse for comparison assays:

set_scaffold_off();
set_scaffold_shared(2.0);
set_scaffold_scrambled(2.0, 17);

They are useful for demos and assays. They are not the rhythm-composition abstraction: composed rhythm should come from the continuum and the terrain.

Routing and the Listener Twin

Two buses

Each voice contributes to two independent mono buses:

  • presentation bus → cpal output / offline render / UI metering (the work as presented)
  • habitat bus → NSGT analysis → landscape (what the ALife ecology responds to)

By default both buses receive the voice. Use send() when a voice should feed only one side, or combine buses with |:

// Reference anchor: sensed by the ecology, absent from the presented sound.
let anchor = harmonic().brain("drone").send(habitat_bus);

// Presented decor that does not influence the ecology.
let decor = sine().send(presentation_bus);

// Explicitly both (the default).
let normal = harmonic().send(habitat_bus | presentation_bus);

place(anchor, at(110.0));
place(decor, at(880.0));
place(normal, consonance(110.0).peak().count(3));
wait(4.0);

Keeping the buses separate is a composition tool. A hidden anchor on send(habitat_bus) can shape how the population organizes without ever becoming audible, and presented decor on send(presentation_bus) can be heard without perturbing the ecology.

The Listener Twin

conchordal keeps a ListenerTwin: a listener-side model of the presented sound only. It never reads the habitat bus, so hidden scaffolds cannot create fake listener tension. It reports five state values:

  • stability_level: how stable / consonant the current audible sound is.
  • resolvability_level: whether a nearby audible state offers a plausible, more stable continuation.
  • tension_level: (1 - stability) * resolvability — unstable now, but with a reachable path toward improvement.
  • attention_level: presentation-derived onset / spectral-flux salience.
  • neural_rhythms: presentation-derived listener-side rhythm (delta/theta).

There is no scripting verb for the twin. It is observed, not commanded: when you run with reporting enabled it emits listener_state records, and the GUI shows the same state. Use it to check whether the tension you hear matches what the twin reports before coupling it back into generation.

DCC: coupling the twin back

That optional coupling is DCC, configured in conchordal.toml, not in script:

[dcc]
# Listener pressure is report/UI-only by default.
# coupling_strength = 0.0
# max_exploration_bonus = 0.10
  • coupling_strength (0.01.0, default 0.0): at 0.0 the twin is report/UI-only and generation is unchanged. Above 0.0 it applies tension_pressure = tension_level * resolvability_level * coupling_strength as a transient pitch-exploration bonus only. It never sets target pitches or changes rhythm synchronization.
  • max_exploration_bonus (default 0.10): ceiling on that transient bonus.

Raise coupling_strength gradually, and only after the reported listener_state looks musically legible.

API Reference

This page is generated from the engine’s registered scripting surface joined with the documentation registry (src/scripting/docs.rs). A CI test fails whenever this page drifts from the engine. Regenerate with:

cargo run --bin gen_rhai_defs

Builder methods return their receiver and are chainable. Integer and float literals are interchangeable wherever both overloads are registered; the generated LSP definitions (rhai-defs/conchordal.d.rhai) list the exact overloads.

The surface is split into three tiers. Core API is the curated composing surface — it is enough for every curated sample. Mechanism Tuning adjusts the mechanisms behind the core verbs when a piece needs a behavior the core surface does not express. Research Controls exist for studying the instrument, not composing with it.

Types

TypeDescription
MaterialVoice template built from a constructor such as sine() or harmonic(); all builder methods are chainable.
ParticipantHandle to a placed voice group returned by place(); draft until the next wait()/flush(), then live.
PlacementWhere voices enter the field: built by at(), consonance(), dissonance(), edge(), gap(), random(), or line().
ModePatternFrequency pattern for modal synthesis bodies; built by *_modes() constructors.
BusOne of the two mono output buses; combine with `
BusSetCombination of buses produced by `bus

Built-in Constants

ConstantTypeDescription
habitat_busBusAnalysis bus: NSGT -> landscape; what the ecology senses.
presentation_busBusPresentation bus: cpal output, recording, UI metering; what the listener hears.

Core API

The curated composing surface. These verbs are enough for every curated sample.

Materials

Materials are voice templates. A constructor returns a Material; builder methods refine it; place() turns it into living voices.

sine

sine() -> Material

Material with a pure sine body.

harmonic

harmonic() -> Material

Material with a harmonic-series body.

modal() -> Material

Material with a resonator-based modal body; shape it with modes().

saw

saw() -> Material

Harmonic-body preset with brightness 0.85.

square

square() -> Material

Harmonic-body preset with brightness 0.65.

noise

noise() -> Material

Harmonic-body preset with brightness 1.0 and motion 1.0.

variant

variant(material) -> Material

Clone a material so the copy can be modified independently.

Placement

Placements decide where voices enter the frequency field. Constructors return a Placement; modifiers refine it before it is passed to place().

at

at(freq_hz) -> Placement

Place at a fixed frequency in Hz.

consonance

consonance(root_hz) -> Placement
consonance(min_hz, max_hz) -> Placement

Target consonance maxima. Density cloud by default; .peak() for the extremum. consonance(root) takes a harmonic window around a root (default 1x-4x, set multiples with range()); consonance(min, max) takes an absolute range. Default spacing is 1.0 ERB.

dissonance

dissonance(min_hz, max_hz) -> Placement

Target consonance minima (tension, clusters). Density cloud by default; .peak() for the most dissonant point.

edge

edge(min_hz, max_hz) -> Placement

Target the consonance/dissonance boundary (C near its midpoint). The metastable region between fusion and beating. Density band by default; .peak() for the point closest to the boundary.

gap

gap(min_hz, max_hz) -> Placement

Target empty registers (low subjective intensity); fill the room. Density cloud by default; .peak() for the emptiest position.

peak

peak()

Applies to: Placement.

Realize a field target as its deterministic extremum.

density

density()

Applies to: Placement.

Realize a field target as a stochastic cloud (the default).

tension

tension(degree)

Applies to: Placement.

Place at a metastable consonance below the strongest (degree 0..1). Consonance placement only. 0 targets the strongest consonance (resolved); higher targets a weaker, more tense step (target = L_max - degree*(L_max - L_min) over the range, in field_score). Pairs with peak() (nearest step, sharp) or density (a broader cloud around it).

random

random(min_hz, max_hz) -> Placement

Log-uniform random placement inside a frequency range.

line

line(start_hz, end_hz) -> Placement

Linear interpolation of positions between two frequencies.

count

count(n)

Applies to: Placement.

Number of voices to place (default 1).

range

range(min_mul, max_mul)

Applies to: Placement.

Multiplier range relative to the root; only valid on consonance(root).

spacing

spacing(min_erb)

Applies to: Placement.

Minimum ERB distance between placed voices; valid on field placements.

Timeline & Staging

Staging verbs commit voices and move script time. place() stages a draft Participant; wait()/flush() commit drafts; scopes release groups automatically.

place

place(material, placement) -> Participant

Stage a Participant at the current script time. Committed by the next wait() or flush(). Until then, builder methods on the returned Participant shape the initial spawn; afterwards, live-patchable methods update running voices. Calling place(participant, placement) on a draft re-places it (sets frequency, count, and strategy).

wait

wait(seconds)

Commit pending drafts, then advance the timeline cursor.

flush

flush()

Commit pending drafts without advancing time.

release

release(participant)

Release a live group; its voices enter their release phase and fade out.

section

section(name, callback)

Named scope; groups placed inside are released when the callback returns.

play

play(callback)
play(callback, arg1)
play(callback, arg1, arg2)
play(callback, arg1, arg2, arg3)
play(callback, [args])

Scoped callback execution with automatic group release. Accepts 0-3 positional arguments, or an array of arguments.

parallel

parallel(callbacks)

Run an array of closures on parallel timelines from the current cursor. Each branch starts at the current cursor; the cursor advances to the latest branch end.

seed

seed(value)

Set the random seed for reproducible scenarios.

Body & Timbre

Sound body parameters of a voice: level, spectrum, detuning, and envelope.

amp

amp(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Amplitude in 0.0-1.0.

freq

freq(hz)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Frequency lock in Hz; implies an anchored pitch (see anchor()).

brightness

brightness(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Spectral brightness in 0.0-1.0 (harmonic/modal bodies).

spread

spread(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Detuning spread.

unison

unison(count)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Number of unison detuning copies.

modes

modes(pattern)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Set the mode pattern of a modal body. See the Mode Patterns section for constructors and modifiers.

adsr

adsr(attack_sec, decay_sec, sustain_level, release_sec)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

ADSR amplitude envelope.

Phonation & Rhythm

When and how long a voice sounds. Tier 1 picks a region on the rhythm coupling continuum (metric(), entrained(), flow()); Tier 2 sets explicit when/duration; Tier 3 is expert tuning. Calls on the same axis are last-write-wins, and modifiers are remembered and applied when their matching preset is selected.

brain

brain(name)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Articulation brain: "entrain" (default), "seq", or "drone". entrain synchronizes with detected rhythms in the field, seq does fixed-duration note sequencing, drone sustains with slow frequency sway.

sustain

sustain()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Sustain phonation mode (default).

repeat

repeat()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Repeated/pulsed phonation mode.

metric

metric()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

High coupling to the shared emergent meter: deep attractor, stable pulse. Takes no rate argument: the tempo region is director-level terrain (temporal_basin).

entrained

entrained()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Medium coupling: synchronization emerges over time, still drifts.

flow

flow()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Near-zero coupling: free renewal process, non-metric texture.

entrainment

entrainment(strength)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Override coupling strength in 0-1 (free .. locked). Order-independent with presets: entrainment(0.8).metric() and metric().entrainment(0.8) are equivalent.

rhythm_role

rhythm_role(name)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Metrical job: "beat", "subdivision", "accent", or "texture". accent emits a stronger onset that drives the shared meter harder, so a recurring downbeat can seed an emergent measure.

microtiming

microtiming(amount)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Signed beat-phase offset in -0.5..0.5; 0.5 reads as syncopation.

cycles

cycles(n)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Duration of n rhythm cycles.

Pitch Movement

How a voice moves through the consonance field. seek_consonance() plus glide() is the curated surface; the rest tunes the hill-climb and peak-sampler mechanisms.

anchor

anchor()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Hold the voice at its pitch: an anchored voice never moves. Voices placed with at() or given freq() are anchored implicitly; use anchor() to hold strategy-placed voices (consonance(), dissonance(), …) at their settled position, or to state the intent explicitly.

seek_consonance

seek_consonance()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Free hill-climb movement toward better field positions, with glide defaults. How pitch decisions land is resolved at commit unless pitch_apply_mode() was called: sustained voices glide, re-attacking voices (pulse(), metric(), entrained(), flow()) snap at onsets.

glide

glide(tau_sec)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Pitch glide time constant in seconds.

Neighbor Awareness

How a voice perceives other voices and its own spectral footprint when evaluating the field.

avoid_neighbors

avoid_neighbors(strength)
avoid_neighbors(strength, sigma_cents)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Crowding repulsion from neighboring voices. With one argument the repulsion width uses the default; the two-argument form sets it explicitly in cents.

Lifecycle & Viability

Energy budget and survival. Viability makes field fit matter over time: consonance_viability() defines the consonance window and viability_rate() controls continuous recharge, with environment-relative scoring by default.

metabolism

metabolism(rate)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Energy consumption rate.

initial_energy

initial_energy(value)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Starting energy.

energy_cap

energy_cap(value)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Maximum energy after recharge/reward.

recharge_rate

recharge_rate(rate)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Energy gained per attack.

action_cost

action_cost(cost)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Energy cost per attack.

sustain_drive

sustain_drive(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Continuous drive level for sustained voices.

viability_rate

viability_rate(rate)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Continuous consonance-driven energy recharge rate.

consonance_viability

consonance_viability(low, high)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Consonance window used for viability scoring. Enables environment-relative scoring by default: a voice is evaluated against the field with its own footprint approximately removed (see viability_scope()).

dissonance_cost

dissonance_cost(cost)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Extra energy cost at low consonance.

Respawn

Population turnover. A respawn policy decides where replacements appear when voices die; capacity and acceptance thresholds shape ecology-scale behavior.

respawn_random

respawn_random()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Respawn at random locations.

respawn_hereditary

respawn_hereditary(sigma_oct)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Hereditary respawn with frequency variance in octaves.

respawn_consonance

respawn_consonance()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Respawn from consonance-biased parental peaks.

respawn_capacity

respawn_capacity(count)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Maintain population up to a capacity.

respawn_settle

respawn_settle(placement)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Placement used for replacements. Requires a strategy-bearing placement: consonance(), dissonance(), edge(), gap(), random(), or line() (not at()).

respawn_min_c_level

respawn_min_c_level(level)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Minimum consonance level for respawn acceptance.

respawn_background_death_rate

respawn_background_death_rate(rate)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Background turnover rate per second.

Mode Patterns

Frequency relationships for modal() bodies. Constructors return a ModePattern; modifiers are chainable. Landscape-aware patterns sample the live field.

harmonic_modes

harmonic_modes() -> ModePattern

Harmonic series: f, 2f, 3f, …

odd_modes

odd_modes() -> ModePattern

Odd harmonics: f, 3f, 5f, …

power_modes

power_modes(beta) -> ModePattern

Power law: f * n^beta.

stiff_string_modes

stiff_string_modes(stiffness) -> ModePattern

Stiffness-adjusted harmonics.

custom_modes

custom_modes(ratios) -> ModePattern

Custom frequency ratios from an array.

modal_table(name) -> ModePattern

Named mode table lookup. Falls back to harmonic_modes() with a warning if the name is unknown.

landscape_density_modes

landscape_density_modes() -> ModePattern

Modes sampled from the live landscape density.

landscape_peaks_modes

landscape_peaks_modes() -> ModePattern

Modes sampled from live landscape peaks.

count

count(n)

Applies to: ModePattern.

Number of modes.

range

range(min_mul, max_mul)

Applies to: ModePattern.

Frequency range; only valid on landscape_*_modes().

spacing

spacing(min_erb)

Applies to: ModePattern.

Minimum ERB distance between modes; only valid on landscape_*_modes().

gamma

gamma(g)

Applies to: ModePattern.

Density sharpening exponent; only valid on landscape_density_modes().

jitter

jitter(cents)

Applies to: ModePattern.

Randomize mode frequencies by up to the given cents.

seed

seed(value)

Applies to: ModePattern.

Random seed for jittered patterns (>= 0).

Routing

Each voice contributes to two independent mono buses: the presentation bus (the work as heard) and the habitat bus (what the ecology senses through NSGT analysis). By default a voice feeds both.

send

send(bus)

Applies to: Material only.

Route the voice to specific buses; accepts a Bus or a BusSet. Material only. send(habitat_bus) makes a voice sensed by the ecology but absent from the presented sound; send(presentation_bus) is heard without perturbing the ecology; send(habitat_bus | presentation_bus) feeds both (the default).

Director & Global Parameters

Scene-global terrain shaping and research controls. Director verbs are soft priors: they shape the terrain and never schedule a beat or force a measure.

meter_stability

meter_stability(value)

Attractor depth in 0-1: how readily a pulse forms. A soft prior: it only deepens the basin for a real periodicity; it never fabricates a beat from non-metric input.

temporal_basin

temporal_basin(min_hz, max_hz)

Tempo region the emergent beat gravitates toward. The time-axis analogue of consonance(min, max): it shapes the terrain, does not place a beat, and never forces a measure.

Experimental

Candidate core verbs under audition: composing surface by intent, but with research-grade stability until validated.

Empty right now.

Mechanism Tuning

Fine-grained control over the mechanisms behind the core verbs. Defaults are calibrated; reach for these when a piece needs a specific behavior the core surface does not express.

Placement

reject_targets

reject_targets(anchor_hz, targets_st, exclusion_st, max_tries)

Applies to: Placement.

Reject sampled positions near specified semitone targets. targets_st is an array of semitone offsets from anchor_hz, exclusion_st is the exclusion zone width in semitones, and max_tries is the retry limit. Wraps any strategy-bearing placement (consonance(), dissonance(), random(), line(), …).

Phonation & Rhythm

once

once()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Single trigger.

pulse

pulse(rate_hz)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Pulse at an explicit rate in Hz.

while_alive

while_alive()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Hold/sustain until release.

adaptive_duration

adaptive_duration()

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Duration follows field support.

pulse_lock

pulse_lock(depth)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Low-level pulse phase weighting in 0-1.

social

social(coupling)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Social coupling for entrained() or pulse(), in 0-1.

duration_range

duration_range(min_cycles, max_cycles)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Adaptive duration range in rhythm cycles.

duration_curve

duration_curve(k, x0)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Adaptive duration curve parameters.

shorten_on_drop

shorten_on_drop(gain)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Shorten adaptive duration when field support drops.

rhythm_freq

rhythm_freq(freq_hz)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Internal theta/rhythm oscillator frequency.

rhythm_coupling_vitality

rhythm_coupling_vitality(lambda_v, v_floor)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Vitality-modulated rhythm coupling.

rhythm_reward

rhythm_reward(rho_t, metric)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

Energy reward for timing fit; metric is "attack_phase_match" or "none".

Pitch Movement

pitch_smooth

pitch_smooth(tau_sec)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Pitch smoothing time constant in seconds.

pitch_apply_mode

pitch_apply_mode(name)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

"gate_snap" or "glide": override how pitch decisions are applied. Without an explicit call, moving voices resolve this at commit from their phonation: sustained voices glide, re-attacking voices snap at onsets.

landscape_weight

landscape_weight(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Weight of the landscape objective.

temperature

temperature(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Search temperature shared by both pitch cores: 0 settles greedily, higher is more exploratory. The single stochastic-search knob. At 0 the hill-climb is greedy (move only on a clear improvement, otherwise stay) and the peak sampler takes the argmax candidate. Higher values let the hill-climb accept downhill moves (Metropolis) and soften the peak sampler’s candidate choice.

neighbor_step_cents

neighbor_step_cents(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Step size for neighbor exploration in cents.

move_cost

move_cost(coeff)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Cost multiplier for pitch changes.

move_cost_exp

move_cost_exp(exp)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Exponent for the move cost.

proposal_interval

proposal_interval(seconds)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Proposal generation interval in seconds.

tessitura_gravity

tessitura_gravity(value)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Gravity toward the tessitura center.

window_cents

window_cents(width)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Peak-sampler search window width in cents.

top_k

top_k(count)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Number of top candidates kept by the peak sampler.

sigma_cents

sigma_cents(spread)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Gaussian spread of the peak sampler in cents.

random_candidates

random_candidates(count)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Number of random candidates considered.

global_peaks

global_peaks(count)
global_peaks(count, min_sep_cents)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Global field peaks as movement candidates, with optional minimum separation.

ratio_candidates

ratio_candidates(count)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Ratio-based movement candidates; 0 disables.

Neighbor Awareness

crowding_target

crowding_target(same_visible, other_visible)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Which voices are visible to crowding: own group, other groups (booleans).

leave_self_out

leave_self_out(enabled)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Subtract the voice’s own spectral contribution when evaluating the field.

leave_self_out_harmonics

leave_self_out_harmonics(count)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

Number of harmonics used for approximate self-subtraction.

Lifecycle & Viability

viability_scope

viability_scope(name)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

"environment" (default) or "total" viability scoring scope. Use "total" only when the selection question should include the voice’s own contribution.

Director & Global Parameters

set_roughness_k

set_roughness_k(value)

Roughness tolerance of the landscape.

set_global_coupling

set_global_coupling(value)

Voice interaction strength.

Research Controls

For studying the instrument, not composing with it. Normal composing never touches this tier, and it has the weakest stability guarantee: entries may change or disappear as their research questions settle.

Pitch Movement

pitch_core

pitch_core(name)

Applies to: Material and Participant. Draft-only: ignored with a warning on a live Participant.

"hill_climb" or "peak_sampler". Research control.

move_cost_time_scale

move_cost_time_scale(name)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

"legacy"/"integration_window" or "proposal"/"proposal_interval".

Neighbor Awareness

leave_self_out_mode

leave_self_out_mode(name)

Applies to: Material and Participant. Live-patchable: updates running voices on a live Participant.

"approx"/"approx_harmonics" or "exact"/"exact_scan".

Lifecycle & Viability

selection_approx_loo

selection_approx_loo(enabled)

Applies to: Material only.

Override environment-relative viability scoring; research/reference control. Material only. Use only for older reference assays that need the previous implementation-level control.

Director & Global Parameters

set_pitch_objective

set_pitch_objective(name)

"consonance"/"positive" or "dissonance"/"negative". Research control.

set_control_update_mode

set_control_update_mode(name)

"snapshot_phased"/"snapshot" (default) or "sequential_rotating"/"sequential". Research control.

set_scaffold_off

set_scaffold_off()

Disable the external rhythm scaffold. Scaffolds are external comparison controls for demos and assays, not the rhythm-composition abstraction.

set_scaffold_shared

set_scaffold_shared(freq_hz)

Shared external scaffold pulse.

set_scaffold_scrambled

set_scaffold_scrambled(freq_hz, seed)

Per-voice scrambled scaffold pulse.

Études

The samples are a book of twelve études — small pieces, in order. Played and read one after another, they are the instrument: each is written around one of its capacities, and the script itself is the score.

cargo run --release -- samples/01_a_single_voice.rhai
  1. A Single Voice — one voice appears, holds its breath, and leaves.
  2. Constellation — four ways to enter: a line, the peaks, the density, chance.
  3. Gravity — the same root under two suns; the peaks listen to what is sounding, not to a chart.
  4. Tension — the director leans on the terrain until it strains, then lets the gravity come home.
  5. Settling — scattered voices glide to where the field can hold them.
  6. Bells — struck bodies; the last bell lets the field choose its partials.
  7. Heartbeat — no external scaffold is imposed; the population locks into a shared pulse.
  8. Murmuration — a flock drifts into step, never commanded.
  9. Rain — time without a beat, falling along the field.
  10. Generations — voices live, starve, and are reborn where harmony can hold them.
  11. Autumn Cycle — a directed harmony; the season turns and comes home.
  12. Emergence and Resolution — everything at once, bent into a single arc.

Études 1–6 walk the consonance terrain (placement, gravity, tension, movement, timbre); 7–9 walk the rhythm continuum one region at a time; 10 closes the loop into life; 11–12 are directed by the composer. All études are compile-checked by the test suite, so they always match the current API.

One craft rule holds across the path: scaffolding is inaudible or embodied. Terrain anchors sing to the habitat bus, not to the audience, unless the drone is itself the subject, as with the suns of Gravity. Pulse carriers get resonant bodies and lives of their own, or the pulse is left to condense from the colony.

Research assays

samples/research/ holds comparison fixtures — heredity/selection ablations, external-scaffold rhythm controls, and mechanism studies. They study the instrument rather than play it, and are not part of the path.

Offline rendering

The conchordal instrument never writes audio to disk — performances are ephemeral by design. For offline WAV rendering use the separate conchordal-render binary, which shares the core engine but is not the instrument.