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

Quick Start Guide

Conchordal v0.3.0 scripting API overview. For full details, see the API Reference.

Basic Concepts

  • Species: Agent templates created with derive() from presets (sine, harmonic, saw, square, noise, modal).
  • Groups: Collections of agents spawned from a species via create(species, count). Returns a GroupHandle.
  • Timeline: wait(seconds) advances the clock. flush() forces immediate dispatch. scene() scopes groups with auto-release. parallel() runs branches concurrently.
  • Strategies: Placement methods for multi-agent frequency assignment: consonance(), consonance_density_pmf(), random_log(), linear().

Minimal Example

// Single sine agent at 440 Hz for 2 seconds
create(sine, 1).freq(440.0);
wait(2.0);

Common Patterns

Custom Species Definition

let voice = derive(harmonic)
    .amp(0.5)
    .sustain()
    .brightness(0.7)
    .spread(0.2)
    .voices(4);

Body parameters are individual methods: brightness(v), spread(v), voices(n). Use energy(v) as an alias for amp(v).

Multiple Agents with Consonance Strategy

let base = derive(harmonic).energy(0.1).sustain();

// Place 4 agents using harmonic consonance relative to 220 Hz
create(base, 4)
    .place(consonance(220.0).range(1.0, 4.0).min_dist(1.0));
wait(2.0);

// Or use landscape-derived density placement
create(base, 4).place(consonance_density_pmf(110.0, 880.0));
wait(2.0);

Scene with Auto Cleanup

let drone = derive(sine).amp(0.3).sustain();
let seeker = derive(sine).amp(0.2).sustain();

scene("Introduction", || {
    let a = create(drone, 1).freq(110.0);
    let s = create(seeker, 1).freq(220.0);
    wait(3.0);
    // All groups auto-released when scene ends
});

Parallel Timelines

parallel([
    || {
        // Bass layer
        create(sine, 1).freq(110.0).sustain();
        wait(2.0);
    },
    || {
        // Melody layer
        create(harmonic, 1).freq(440.0).sustain();
        wait(1.0);
        create(harmonic, 1).freq(550.0).sustain();
        wait(1.0);
    }
]);

The modal preset uses inharmonic mode patterns for bell/bar/glass timbres. Configure modes with modes(pattern) and control brightness.

let bell = derive(modal)
    .sustain()
    .pitch_mode("lock")
    .freq(220.0)
    .amp(0.11)
    .brightness(0.7)
    .modes(modal_table("vibraphone_1").count(5));

create(bell, 1);
wait(2.0);

Mode pattern constructors: harmonic_modes(), odd_modes(), power_modes(beta), stiff_string_modes(stiffness), custom_modes([ratios]), modal_table(name). Chain .count(n), .jitter(cents), .seed(n) to refine patterns.

Landscape-aware modes sample from the live perceptual field:

let adaptive = derive(modal)
    .sustain()
    .pitch_mode("free")
    .amp(0.05)
    .brightness(0.7)
    .modes(landscape_density_modes().count(16).range(1.0, 3.5).min_dist(0.9));

Phonation Control

Phonation follows a tiered system.

Tier 1 – Presets set sensible defaults for common use:

derive(sine).sustain();   // Sustained tone, tied to lifecycle
derive(sine).repeat();    // Interval-based retriggering

Tier 2 – Explicit when/duration for finer control:

derive(sine).once();              // Single trigger
derive(sine).pulse(2.0);         // Retrigger at 2 Hz
derive(sine).while_alive();      // Duration spans full lifecycle
derive(sine).gates(4);           // Hold for 4 gate cycles
derive(sine).field();            // Field-driven duration

These compose: derive(sine).pulse(3.0).gates(2) pulses at 3 Hz, each lasting 2 gates.

Pitch Control

// "lock" keeps initial frequency; "free" allows hill-climbing
derive(sine).pitch_mode("lock");
derive(sine).pitch_mode("free");

// "gate_snap" applies pitch on gate boundaries; "glide" interpolates
derive(sine).pitch_apply("gate_snap");
derive(sine).pitch_apply("glide").pitch_glide(0.05);

Live Parameter Patching

Groups support live updates on spawned agents:

let g = create(sine, 1).freq(220.0).sustain();
flush();
wait(1.0);

g.freq(440.0);   // Slide to new frequency
g.amp(0.3);      // Adjust amplitude
flush();
wait(1.0);

Global Parameter Modulation

// Overtone series emphasis (major quality)
set_harmonicity_mirror_weight(0.0);
create(sine, 4).place(consonance(261.63).range(1.0, 3.0));
wait(2.0);

// Shift toward undertone series (minor quality)
set_harmonicity_mirror_weight(1.0);
wait(2.0);

Preset Species

PresetBodyDescription
sineSinePure sine wave
harmonicHarmonicHarmonic series synthesis
sawHarmonicSawtooth-like (bright)
squareHarmonicSquare-like (odd harmonics)
noiseHarmonicNoise-like (full spectrum)
modalModalInharmonic mode synthesis

Next Steps

See the API Reference for the complete function list, type details, and advanced parameters.

Rhai Scripting API Reference (v0.3.0)

This reference documents the complete Rhai scripting API for Conchordal’s Life Engine. Scenarios are scripts that define species, spawn agent groups, control the timeline, and tune global parameters.


Registered Types

TypeDescription
SpeciesHandleSpecies template, built with the builder pattern
GroupHandleHandle to an agent group (draft or live)
SpawnStrategyFrequency allocation strategy for placement
ModePatternFrequency pattern for modal synthesis bodies

Preset Species

Global constants available in every script:

PresetBodyNotes
sineSinePure sine wave
harmonicHarmonicHarmonic series synthesis
sawHarmonicbrightness = 0.85
squareHarmonicbrightness = 0.65
noiseHarmonicbrightness = 1.0, motion = 1.0
modalModalResonator-based modal synthesis
let voice = derive(harmonic).amp(0.4).brightness(0.7);

Species Definition

derive(parent) -> SpeciesHandle

Clone a preset or existing species for modification.

let pluck = derive(harmonic).amp(0.3).adsr(0.01, 0.1, 0.3, 0.2);

All species methods are chainable and return SpeciesHandle.

Body and Amplitude

MethodDescription
amp(value)Amplitude 0.0–1.0
energy(value)Alias for amp
freq(value)Frequency lock in Hz
brightness(value)Spectral brightness 0.0–1.0 (harmonic/modal bodies)
spread(value)Detuning spread
voices(count)Number of unison voices
modes(pattern)Set mode pattern (modal body); see Mode Patterns
let pad = derive(harmonic).amp(0.5).brightness(0.6).spread(0.02).voices(3);
let bell = derive(modal).modes(stiff_string_modes(0.02).count(8));

Pitch Control

MethodDescription
pitch_mode(name)"free" or "lock"
mode(name)Alias for pitch_mode
pitch_core(name)"hill_climb" or "peak_sampler"
pitch_apply(name)"gate_snap" or "glide"
pitch_apply_mode(name)Alias for pitch_apply
pitch_glide(tau_sec)Glide time constant in seconds
pitch_smooth(tau_sec)Pitch smoothing time constant
let glider = derive(sine).pitch_mode("free").pitch_core("hill_climb")
    .pitch_apply("glide").pitch_glide(0.1);

Hill-Climb Tuning

MethodDescription
landscape_weight(value)Weight of landscape objective
neighbor_step_cents(value)Step size for neighbor exploration
tessitura_gravity(value)Gravity toward tessitura center
exploration(value)Exploration tendency
persistence(value)Persistence bias
anneal_temp(value)Annealing temperature
temperature(value)Alias for anneal_temp
improvement_threshold(value)Minimum improvement to accept move
move_cost(coeff)Cost multiplier for pitch changes
move_cost_exp(exp)Exponent for move cost
move_cost_time_scale(name)"legacy"/"integration_window" or "proposal"/"proposal_interval"
proposal_interval(seconds)Proposal generation interval

Peak Sampler Tuning

MethodDescription
window_cents(width)Search window width in cents
top_k(count)Top-k candidates
sigma_cents(spread)Gaussian spread in cents
random_candidates(count)Number of random candidates
global_peaks(count)Global peak candidates (min_sep = 0)
global_peaks(count, min_sep_cents)Global peak candidates with separation
ratio_candidates(count)Enable ratio-based candidates (0 to disable)

Crowding

MethodDescription
crowding(strength)Auto-sigma from roughness kernel
crowding(strength, sigma_cents)Explicit sigma
crowding_target(same_visible, other_visible)Target visibility (booleans)
leave_self_out(enabled)Subtract own spectral contribution
leave_self_out_mode(name)"approx"/"approx_harmonics" or "exact"/"exact_scan"
leave_self_out_harmonics(count)Number of harmonics for self-subtraction
let social = derive(harmonic)
    .crowding(0.8)
    .crowding_target(true, true)
    .leave_self_out(true).leave_self_out_mode("approx");

Brain (Articulation)

MethodDescription
brain(name)"entrain", "seq", or "drone"
BrainBehavior
entrainSynchronizes with detected rhythms in the field (default)
seqFixed-duration note sequencing
droneSustained tone with slow frequency sway

Phonation

Phonation is configured in three tiers of increasing detail.

Tier 1 – Presets:

MethodDescription
sustain()Sustain mode (default)
repeat()Repeated/pulsed mode

Tier 2 – Explicit when/duration:

MethodDescription
once()Single trigger
pulse(rate_hz)Pulse at given rate
while_alive()Duration: while agent alive
gates(n)Duration: n theta gates
field()Duration: field-based

Tier 3 – Expert tuning:

MethodDescription
sync(depth)Sync depth 0–1 (requires pulse)
social(coupling)Social coupling 0–1 (requires pulse)
field_window(min, max)Field theta window 0–1 (requires field)
field_curve(k, x0)Field curve parameters (requires field)
field_drop(gain)Field drop gain (requires field)
let pulsed = derive(harmonic).repeat().pulse(3.0).sync(0.6).social(0.3);
let fielded = derive(sine).field().field_window(0.2, 0.8);

Lifecycle

MethodDescription
metabolism(rate)Energy consumption rate
adsr(attack_sec, decay_sec, sustain_level, release_sec)ADSR envelope
let pluck = derive(harmonic).metabolism(0.5).adsr(0.01, 0.1, 0.3, 0.2);

Rhythm

MethodDescription
rhythm_coupling(mode)"temporal" / "temporal_only"
rhythm_coupling_vitality(lambda_v, v_floor)Vitality-modulated coupling
rhythm_reward(rho_t, metric)"attack_phase_match" or "none"

Respawn

MethodDescription
respawn_random()Random respawn locations
respawn_hereditary(sigma_oct)Hereditary with frequency variance in octaves

Telemetry

MethodDescription
enable_telemetry(first_k)Enable for first k agents
enable_plv(window)Enable Phase Locking Value computation

Sustain Drive

MethodDescription
sustain_drive(value)Continuous drive level

Mode Patterns

Mode patterns define frequency relationships for modal synthesis bodies.

Constructors

All constructors return ModePattern.

FunctionDescription
harmonic_modes()Harmonic series: f, 2f, 3f, …
odd_modes()Odd harmonics: f, 3f, 5f, …
power_modes(beta)Power law: f * n^beta
stiff_string_modes(stiffness)Stiffness-adjusted harmonics
custom_modes(ratios)Custom frequency ratios from array
modal_table(name)Named mode table lookup
landscape_density_modes()Modes from landscape density
landscape_peaks_modes()Modes from landscape peaks

Modifiers

All modifiers are chainable and return ModePattern.

MethodDescription
.count(n)Number of modes
.range(min_mul, max_mul)Frequency range (landscape modes only)
.min_dist(d)Min ERB distance (landscape modes only)
.gamma(g)Gamma parameter (landscape_density only)
.jitter(cents)Randomization in cents
.seed(s)Random seed
let bell_modes = stiff_string_modes(0.03).count(12).jitter(5.0);
let adaptive = landscape_density_modes().count(6).range(1.0, 8.0).gamma(2.0);

Spawn Strategies

Strategies define how frequencies are allocated when spawning a group.

Constructors

All constructors return SpawnStrategy.

FunctionDescription
consonance(root_freq)Highest consonance positions (default range 1x–4x)
consonance_density_pmf(min_freq, max_freq)Weighted-random from density PMF
random_log(min_freq, max_freq)Log-uniform random
linear(start_freq, end_freq)Linear interpolation

Modifiers

MethodApplies toDescription
.range(min_mul, max_mul)consonanceMultiplier range relative to root
.min_dist(d)consonance, consonance_density_pmfMin ERB distance between agents
.reject_targets(anchor_hz, targets_st, exclusion_st, max_tries)AnyReject positions near specified targets

reject_targets wraps any strategy: 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.

let strat = consonance(220.0).range(1.0, 3.0).min_dist(0.9);
create(harmonic, 6).place(strat);

let density = consonance_density_pmf(100.0, 800.0).min_dist(1.0);
create(sine, 8).place(density);

// Avoid octaves and fifths of 220 Hz
let filtered = random_log(100.0, 1000.0)
    .reject_targets(220.0, [0.0, 12.0, 7.0, 19.0], 0.5, 50);

Group Creation and Operations

create(species, count) -> GroupHandle

Create a draft group of count agents. The group spawns on the next flush() or wait() call.

let g = create(sine, 4).amp(0.3);
flush();  // spawns the group

place(strategy) -> GroupHandle

Set spawn strategy (draft only).

create(harmonic, 6).place(consonance(220.0).range(1.0, 4.0));

release(group)

Release a live group. Agents enter their release phase and fade out.

let g = create(sine, 1).freq(440.0);
flush();
wait(2.0);
release(g);

Draft vs. Live Methods

Group methods work in two contexts:

  • Draft (before flush()/wait()): configures the group before spawning.
  • Live (after spawning): patches parameters on running agents.

Live-patchable (work in both draft and live):

amp, energy, freq, landscape_weight, neighbor_step_cents, tessitura_gravity, sustain_drive, pitch_smooth, exploration, persistence, anneal_temp, temperature, move_cost, move_cost_exp, improvement_threshold, proposal_interval, window_cents, top_k, sigma_cents, random_candidates, brightness, spread, voices, pitch_glide, leave_self_out, leave_self_out_mode, leave_self_out_harmonics, crowding, crowding_target, global_peaks, ratio_candidates, move_cost_time_scale, pitch_apply_mode, pitch_apply

Draft-only (ignored with a warning if called on a live group):

brain, pitch_mode, mode, pitch_core, sustain, repeat, once, pulse, while_alive, gates, field, sync, social, field_window, field_curve, field_drop, metabolism, adsr, rhythm_coupling, rhythm_coupling_vitality, rhythm_reward, respawn_random, respawn_hereditary, modes, place

let g = create(harmonic, 4)
    .brightness(0.7)          // draft: sets initial value
    .brain("entrain")         // draft-only
    .place(consonance(220.0));
flush();

g.brightness(0.3);            // live: patches running agents
wait(2.0);
release(g);

Timeline and Control Flow

wait(seconds)

Flush pending groups, then advance the timeline cursor.

create(sine, 1).freq(440.0);
wait(2.0);  // spawn + wait 2 seconds

flush()

Commit pending draft groups without advancing time.

create(sine, 1).freq(440.0);
flush();  // spawn immediately at current cursor

seed(value)

Set the random seed for reproducible scenarios.

seed(42);

scene(name, callback)

Named scope with automatic group release when the callback returns.

scene("Intro", || {
    create(sine, 2).freq(220.0);
    flush();
    wait(4.0);
    // all groups auto-released here
});

play(callback [, args…])

Scoped callback execution with automatic cleanup. Accepts 0–3 positional arguments, or an array.

let chord = |root| {
    create(sine, 1).freq(root);
    create(sine, 1).freq(root * 1.25);
    create(sine, 1).freq(root * 1.5);
    flush();
};

play(chord, 220.0);
wait(2.0);
// groups auto-released when play scope ends

parallel(callbacks)

Execute closures on parallel timelines. Each branch starts at the current cursor. The cursor advances to the latest branch end.

parallel([
    || {
        create(sine, 1).freq(220.0);
        wait(1.0);
    },
    || {
        create(sine, 1).freq(330.0);
        wait(3.0);
    },
]);
// cursor is now at +3.0 seconds

Global Parameters

FunctionDescription
set_harmonicity_mirror_weight(value)Overtone/undertone balance 0.0–1.0
set_roughness_k(value)Roughness tolerance
set_global_coupling(value)Agent interaction strength
set_pitch_objective(name)"consonance"/"positive" or "dissonance"/"negative"
set_harmonicity_mirror_weight(0.0);  // overtone-dominant (major)
wait(4.0);
set_harmonicity_mirror_weight(1.0);  // undertone-dominant (minor)

Complete Example

seed(1);

// Define species
let anchor = derive(sine).amp(0.6).sustain();
let voice = derive(harmonic)
    .amp(0.3)
    .brightness(0.5)
    .pitch_mode("free")
    .pitch_core("hill_climb")
    .pitch_apply("glide").pitch_glide(0.08)
    .crowding(0.5)
    .metabolism(0.4)
    .adsr(0.05, 0.1, 0.7, 0.3)
    .repeat().pulse(2.25).sync(0.5);

scene("Opening", || {
    // Anchor drone
    let root = create(anchor, 1).freq(220.0);
    flush();
    wait(1.0);

    // Consonant voices
    let strat = consonance(220.0).range(1.0, 3.0).min_dist(0.9);
    create(voice, 6).place(strat);
    wait(4.0);

    // Shift to undertone mode
    set_harmonicity_mirror_weight(1.0);
    wait(4.0);
});

scene("Development", || {
    let dense = derive(voice).exploration(0.8).anneal_temp(0.5);
    let strat = consonance_density_pmf(100.0, 800.0).min_dist(0.8);
    create(dense, 12).place(strat);
    wait(8.0);
});