The voice
The generated sound an organism makes, played live from its own shader source and fed back into the image through uniforms.
The voice is the sound an organism makes: an ambient piece generated in the visitor’s browser from the organism’s own shader source, and returned to the image through uniforms the shader reads back. No audio file exists; the engine reads the code into a feature vector, blends that reading with the organism’s id used as a seed, and plays the result. One organism has the voice at a time on a page, and a given organism sounds the same on every visit.
Genome reading
extractGeneFeatures strips comments and preprocessor lines from the shader body, then measures ten
features. Every density measure is counted per 1000 collapsed characters and passed through the
saturating curve 1 - exp(-x / k), so a value approaches 1 without reaching it.
| Feature | Read from the body | What it moves |
|---|---|---|
literalCount | float literal tokens | rhythmic fill: the pulse counts of both euclidean patterns |
literalMean | log-scaled mean magnitude of those literals | drone root, and drone warmth inversely |
literalSpread | dispersion of those magnitudes | pad cutoff, detune width, drone filter, harmonic wander, melodic range |
trigDensity | sin cos tan and their inverse and hyperbolic forms | mode brightness, pad brightness |
noisiness | chaos markers: fract floor mod hash noise fbm and kin | tempo upward, all three trigger probabilities, pad grit, harmonic tension, melodic density, the arc’s energy ceiling |
loopWeight | summed constant iteration counts of every for header | tempo downward, patience |
length | line count, saturating near 140 lines | tempo downward, bars per chord, patience |
branchiness | if switch ? and the selection builtins | rotation of the euclidean patterns, melodic angularity |
vectorness | vector and matrix constructors, geometric builtins | stereo width, echo send |
functionCount | user-defined function definitions, main excluded | restores percussion voices the seed left silent |
A for header with no readable constant bound counts as 8 iterations. An empty or non-string body
returns every feature at zero.
Blend with the seed
A seed alone already decides a full musical identity: mode, root, tempo, pad envelope and
cutoff, the euclidean tick and blip patterns, per-voice levels, echo and reverb sends, and the input
scalars handed to the harmony, melody and form planners. The genome does not replace that draw. Each
affected scalar becomes 0.6 * genome + 0.4 * seed, and every result is then clamped into a fixed
range, so a genome can never push a value past the bounds the engine enforces for itself.
- The blend draws no random numbers, so an organism’s identity stream sits where the seed left it.
- It runs before the harmonic plan, motif, form, oscillator spectra and drone are built, so every derived structure sees the blended values.
- Mode is chosen from six scales ordered darkest to brightest (aeolian, minor pentatonic, dorian, mixolydian, major pentatonic, lydian); the seed’s own mode contributes its position in that order.
- Roughly one identity in five is missing any given percussive voice. Two user-defined functions restore a silenced blip, three restore a tick, four restore a thump. The genome only raises a silent voice and never silences a sounding one, and an identity that drew all three silent falls back to blips.
Separate generator streams carry the harmonic plan, the motif and the form, each seeded from the organism id mixed with a different constant, so a change in one planner’s branching does not shift another’s draws.
Instrumented alleles
instrumentShader rewrites the body so that selected float literals become alleles driven by a
uniform array. Each chosen literal x at index i is replaced by (x * u_gene[i]), and the
declaration uniform float u_gene[12]; is prepended. At most 12 alleles are instrumented.
Candidates are picked to spread across the file rather than clustering at the top: the body is cut into 12 positional buckets and the buckets are walked round robin, a random surviving literal drawn from each.
Literals never touched:
- anything inside a
forheader, where GLSL ES requires constant bounds - anything in a
constdeclaration, through to its semicolon - anything inside square brackets, which covers array sizes and indices
- magnitudes below 1e-4
- any literal whose offsets do not match the original text exactly
Each allele carries two constants fixed at instrumentation time. Sensitivity is a value between 0.3 and 1.0 setting how far that allele travels. Band selects which measure of the sound drives it, with large magnitudes weighted toward bass and small ones toward high.
| Band index | Drives from |
|---|---|
| 0 | overall level |
| 1 | bass |
| 2 | mid |
| 3 | high |
Instrumentation returns the original body unchanged, with a count of zero, when the code is empty,
when it already contains the token u_gene, when no literal survives the exclusions, or when
anything in the pass throws.
Both pages compile the instrumented body behind the prelude first. If that fails and the count was above zero, the pristine code gets exactly one further attempt, so an organism broken only by the extra uniform still draws. When neither program builds, the arena reports the failure to the lab and skips the pair, which is a canary strike rather than a cull; the gallery replaces the canvas with a legible note naming the failure as a shader error, a link error, or a missing WebGL2 context.
On the pristine retry the u_gene lookup is null, and the per-frame upload is skipped entirely.
Per-frame modulation
Each frame the page reads four smoothed levels from the analyser and uploads them.
u_gene entry i is set to 1 + 0.12 * sensitivity[i] * band level. Every entry beyond the
instrumented count has a sensitivity of zero. With sound off the engine reports all four levels as
zero, so every entry of the array is exactly 1.0 and the organism draws its pristine self.
u_audio is the reverse direction: a vec4 of level, bass, mid and high, uploaded only when the
compiled program exposes that uniform. Organisms bred before the audio pass do not declare it, so the
lookup is null and the whole reactive path stays idle.
Shader time is accumulated rather than read off a clock. Each frame it advances by the frame delta,
capped at 0.1 seconds, multiplied by 1 + 1.6 * level, and wraps at 20 seconds. Loudness can push an
organism forward faster but never steps it backwards.
The four levels come from one analyser on the master output, at an FFT size of 2048 and a smoothing constant of 0.6. Bins below 250 Hz form bass, 250 to 2000 Hz mid, 2000 to 12000 Hz high, and everything below 8000 Hz the overall level. The four are not a partition: the high band runs past the ceiling the overall level is measured to, so a bright frame can raise high without raising level. Each band is averaged over its own bin count and trimmed by its own gain. Each follows its target quickly upward and slowly downward, so the picture responds to an attack and relaxes after it.
Structural limits of the graph
The graph is closed by construction. It has no microphone and no other input node, so acoustic feedback cannot occur.
- The echo feedback gain is 0.5, under a stated ceiling of 0.6, and the loop passes through a lowpass at 1800 Hz, so every repeat strictly decays.
- The convolution reverb sits outside the feedback loop. The echo output bleeds into the reverb, not the reverse.
- The impulse response is generated from a fixed seed, so the room is a constant of the site and identities differ only in how much of it they use.
- The master chain runs a 25 Hz highpass, a compressor and a limiter at -3 dB with a ratio of 20 before the analyser and the output.
- The master gain starts at zero and ramps in, so nothing is ever heard before the graph has settled.
- Concurrent transient sources are capped at 26; the scheduler skips a voice rather than exceeding it. Sustained sources, the drone oscillators and the filter LFO, are exempt from that budget and bounded instead by the retirement rule below.
Changing the voiced organism crossfades identities over 1.5 seconds: the outgoing bus and its send ramp to zero while the incoming pair ramp up. Setting the seed already voiced does nothing. At most two identities may retire in parallel; a faster sweep cuts the oldest immediately, which bounds the number of live sustained sources however fast the pointer moves.
The voiced organism
One engine serves a page, and it touches no browser API before it starts. Audio may only begin on a
real gesture: the shared preference key rorschach_sound holds on or off, and a remembered on
arms a single pointer or key listener rather than starting anything at load.
The organism id is the seed. The gallery folds a non-numeric id to a stable number, so the same organism always sounds alike.
| Page | Default holder | Pointer | Hold |
|---|---|---|---|
| The arena, where pairs are judged | The two sides trade the voice every 8 seconds | Entering a stage takes the voice | Hovering, a hidden tab, or silence suspends the trade |
| The gallery | On-screen cards take it in turn every 14 seconds, in document order | Entering a card takes the voice | The detail overlay holds it while open, and hands it back to the previous card on close |
A ring marks the element the engine is voiced by. It is drawn only while sound is running, so a silent page never marks one side as sounding.
Bound to
The source this page's claims were read against, named by symbol and pinned by the digest of the file it lives in.
| Symbol | Digest |
|---|---|
| audio.ts | d355ff71c8c7 |
| form.ts | a2695e1d7ba2 |
| gallery.astro | 005d7b298795 |
| genome.ts | 09b333a8984b |
| melody.ts | 0f0631615330 |
| rorschach.astro | b439b3c1f84b |
| theory.ts | cf4f69960a45 |
| timbre.ts | 748837c5e9e4 |
What links here
- Judging sends readers here
- The contract sends readers here
- The gallery sends readers here
- The Rorschach Lab sends readers here
Near this subject
These are the subjects this one is joined to in the field. Step out of this card and they are the wells around the one you are standing in.
- Judging each sends you to the other
- The contract each sends you to the other
- The gallery each sends you to the other
- The Rorschach Lab each sends you to the other
- Organisms this page sends you there
- Death this page sends you there