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

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.