Consonance Field — A Terrain for Evaluating Pitch
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 the habitat bus, every voice routed to that bus deforms the terrain for every other voice. A presentation-only voice does not. That feedback loop — not a chord chart — is where harmony comes from. See Voice and Landscape — Sound–Environment Feedback for the relationship between potential, score, level, mass, and density.
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(τ)
Without tension, Consonance Placement uses its ordinary realization:
.peak() chooses the strongest peak, while the default density produces the
ordinary Consonance cloud. tension(τ) biases that Placement toward a
field-score step below the maximum. τ ∈ [0, 1] is the tension degree;
0 leaves the ordinary realization unchanged, while 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).
Naming frequencies: ratios of a root
at(hz) and line(start_hz, end_hz) take absolute frequencies, and the
canonical idiom for filling them in is to name one sounding root in Hz and
derive every other pitch as a ratio of it: root_hz * 1.5 for a fifth,
root_hz * 4.0/3.0 for a fourth, root_hz * 2.0 for a register lift. A ratio
is the physical quantity the field itself reads, so writing intervals this way
keeps the thinking in frequency relationships.
Writing an equal-tempered decimal instead — 146.83 for a D3 — still runs,
but it re-imports the twelve-tone symbol grid through the back door: the
number stands in for a note name rather than describing a relationship to a
sounding partial. Prefer ratios of a root that is actually sounding in the
scene.
let root_hz = 110.0;
let voice = harmonic().amp(0.04).sustain();
place(voice, at(root_hz * 1.5));
wait(2.0);
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, while recovery(seconds) states how long a
full-scale recharge takes: a voice in a well-fitting place is sustained, a
voice in a poor place approaches its nominal endurance(seconds).
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() selects high field-score
peaks with a bias around an energy-weighted living parent;
respawn_capacity(count) sets the maximum living membership (defaulting to,
and never lower than, the founder count); respawn_settle(placement) adds
that Placement to the replacement candidate pool rather than replacing the
policy’s baseline candidate.
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)
.endurance(8.0)
.recovery(4.0)
.attack_cost_fraction(0.017)
.attack_recharge_fraction(0.70)
.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 (time-domain endurance/recovery and normalized per-attack fractions) and the respawn policies are documented in the API Reference. Population — A Persistent Unit of Voices separates articulation, phonation, pitch behavior, and survival; the Voice and Landscape — Sound–Environment Feedback follows the complete energy and respawn cycle.
Landscape-aware timbre
The field can shape timbre as well as pitch. The modal() body takes a mode
pattern. landscape_density_modes() deterministically chooses the strongest
separated density-mass positions; landscape_peaks_modes() chooses the
strongest separated local Field-level peaks. Thus 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.