Sound and music

How a phone game in plain JavaScript can make all its sound in code, effects, places and music, and give each world a sound of its own the way it already has eight inks of its own. Sound effects in games have been made from a dozen numbers since sfxr in 2007: a wave, a pitch and its slide, an envelope, a filter. ZzFX packs the same idea into 21 numbers and under a kilobyte, and renders each sound once into a buffer. Places are noise through filters (wind), many tiny pitched events (water, gravel, birds) and a slow signal steering both. Adaptive music has two moves, adding layers (vertical) and choosing the next section (horizontal), and a tune stored as scale degrees can change mode with the time of day without being rewritten. The finding: Tycho already has the hard half of this (Lodestar’s engine: the graph, the browser’s rules, voices, an offline bank, drones and noise bands for places). What it lacks is the musical half: a sample-loop effect synth driven by a few seeded numbers, a beat clock with a note scheduler, scales and modes, and a “sound palette” a quest declares like its palette, with a small, fixed voice budget, as the chips had.

Proposed: none of this is in Tycho yet. The demos run here in the page, written in plain JS with the study’s own kit (demos/kit.js), not Tycho; each says how it would run in Tycho: per frame (the GPU stand-in’s calls), pre-bake (painted once offscreen), or shader (per-pixel work every frame). For sound, pre-bake means rendered once into a buffer and per frame means scheduled live as the game runs. What would move into Tycho, and when, is in the roadmap.

Text marked (inference) is reasoning from the sources, not a sourced fact. Browsers allow sound only after a tap, so every demo here is silent until you press a button; the pictures run either way. Timings were measured in Chrome on a laptop; no phone was measured.

What exists

Tycho.audio is Lodestar’s sound engine, moved into Tycho bit for bit and held to Lodestar’s own by a test that traces every Web Audio call (the audio page, audio.ts). It has three parts:

Part What it does
Engine Two buses (effects and music), each sending to a “space” of fed-back delays, then a high pass, a low pass and a limiter. Cues by name with a least gap between plays and a cap on voices. Quieter and panned by distance from a listener. Nothing before the first tap; asleep while the page is hidden
Bank Rich sounds rendered ahead in an OfflineAudioContext, a few variants each, one at a time between frames, through a saturation and a convolution reverb; a play is then a buffer at a slightly different speed
Music Drones built once and kept running silent until wanted, noise bands for a place’s wind or water, a slower tick than the frame’s, events due now and then, a duck, and presence(s, at), a smooth curve for a layer coming in as a state rises

Its voices are tone (an oscillator, gliding, through a low pass), noise (looped noise through a sweeping filter), fm and stack (detuned voices through a sweeping filter). Variation comes from the sound’s own seeded stream, never the game’s, “so sound never moves the world”.

Elsewhere’s js/snd.js is a thin shell over that engine: four cues of its own (a tap, a page of the log, a quest, a souvenir) and a pad, one note of a chord as a slow stack. Each quest brings its own sounds and a score: { bpm, beats, root, chords, w, lp, g }, played a bar at a time, 0.3 s ahead of the audio clock. The slice’s scores are already tiny sound palettes:

Quest Tempo Root Wave Low pass Chords
The Road 48 bpm D3 (146.83 Hz) sawtooth 1100 Hz minor, four bars
The frogs 104 bpm C4 (261.63 Hz) triangle 1500 Hz I vi IV V
The lighthouse 42 bpm A2 (110 Hz) triangle 700 Hz minor with rests, major once the lamp is lit

What neither has (inference, from reading the code): no effect synth that runs as a sample loop (every live voice is a node graph), no beat clock or note sequencer (Music follows a state, not a tempo), no scale or mode helpers beyond hz(semitones, root), and no recipe made from a seed. A score is chords only: no melody, no rhythm, no layers that follow play.

Sound effects from a dozen numbers

sfxr was written by DrPetter (Tomas Pettersson) for the 10th Ludum Dare in December 2007, as “a simple means of getting basic sound effects into a game” for entrants with 48 hours and no time to find sounds, and “a lot of the entrants used it” (sfxr). Its sound is about 22 numbers, each normalized to 0..1 (jsfxr’s sfxr.js):

Group Parameters
Wave square (with duty and duty sweep), sawtooth, sine, noise
Envelope attack, sustain, punch (a sustain that starts loud), decay
Pitch start frequency, a floor, slide, slide of the slide, vibrato depth and speed, an arpeggio step (amount, when), repeat (restart the pitch)
Filters low pass (cutoff, sweep, resonance), high pass (cutoff, sweep), a phaser (offset, sweep)

The buttons are the clever part. Each category, pickupCoin, laserShoot, explosion, powerUp, hitHurt, jump, blipSelect, is a few lines of seeded choices within ranges. In jsfxr, pickupCoin is a sawtooth with a start between 0.4 and 0.9, a short sustain, a punchy decay and a 50% chance of an arpeggio jump. Jump is a square with a random duty and a rising slide of 0.1 to 0.3. hitHurt is square, sawtooth or noise with a steep falling slide. Explosion is noise at a low, squared random start with a falling slide, high punch, and optional repeat, phaser, vibrato or arpeggio. Mutate moves each parameter, with probability one half, by up to ±0.05. The loop renders at 8× oversampling, and its noise is a table of 32 random values refilled every period, so noise has a pitch (jsfxr).

The descendants added waves and effects, not new ideas. bfxr (increpare) added five waves (triangle, breaker, tan, whistle, pink noise), a one-bit noise after the BBC Micro’s SN76489, and compression, harmonics and a bitcrusher (rekkerd.org, a C++ port). ChipTone (Tom Vian, SFBGames, 2015, first in Flash) descends from bfxr by way of Vian’s own as3sfxr and releases its sounds as CC0 (ChipTone).

ZzFX (Frank Force, 2019, MIT) is the one built for the web: the micro version is “under 1 kilobyte” (ZzFX). A sound is an array of numbers in a fixed order, in seconds and hertz rather than 0..1 (ZzFX.js):

# Parameter Default # Parameter Default
1 volume 1 12 pitchJumpTime 0
2 randomness 0.05 13 repeatTime 0
3 frequency 220 Hz 14 noise 0
4 attack 0 s 15 modulation 0
5 sustain 0 s 16 bitCrush 0
6 release 0.1 s 17 delay 0
7 shape (sine, triangle, sawtooth, tan, noise, square) 0 18 sustainVolume 1
8 shapeCurve (the square’s duty) 1 19 decay 0
9 slide 0 20 tremolo 0
10 deltaSlide 0 21 filter (sign: low or high pass) 0
11 pitchJump 0      

The 1.4 code takes 21, though its header still says “20 controllable parameters”. ZzFX writes samples at 44,100 Hz in a plain JS loop into a mono AudioBuffer and plays it with a buffer source, with no oscillator nodes, and sounds can be made ahead and played later (ZzFX). Keith Clark’s ZzFXM is a tracker-style player built on ZzFX instruments, for js13k games (ZzFXM).

The demo is a generator in this manner. A sound is up to four layers, each one voice described by sfxr’s kind of numbers plus one more, grains: the voice heard only in short random bursts, which is what turns noise into grass, gravel or the stick-slip of a creaking hinge. A category is a function from a seeded stream to layers, and Mutate nudges half the numbers. Each sound is rendered once into a Float32Array and played as a buffer.

Seeded sound effects. Tap a category to make and hear one: Pickup, Jump, Hit, Explosion, a footstep on Grass, Stone or Wood, and a Door. The top box is the whole sound (the amber line is the first layer's pitch, on a log scale), the strip under it is 8 ms of the wave itself where it's loudest, and each line below is one layer's numbers: wave, pitch, slide (octaves a second), the arpeggio jump, when it starts (@), attack, sustain and decay, the filter and the grains. Mutate nudges half the numbers; New seed makes another sound of the same kind. Every sound is rendered once in plain JS, then played as a buffer.

Render once or build each time

Web Audio offers two ways to make a sound, and the choice is about cost and control:

  Rendered buffer Live node graph
How A JS loop (or an OfflineAudioContext) writes samples once; each play is one AudioBufferSourceNode Oscillators, filters and gains made for each play, with automation on their params
Cost per play One node, whatever the recipe Several nodes; a frequency change can force new wavetables
Memory length × channels × 4 bytes: 88 KB for half a second of mono at 44.1 kHz Almost none
Same on every device Yes, the samples are fixed Up to the browser’s own DSP
Follows the game Only by speed, level and pan Fully: a pitch that tracks speed, a filter that opens with the weather

Paul Adenot’s notes on Web Audio performance give the costs (Adenot): a BiquadFilterNode is “relatively cheap”, about five multiplies and four adds a sample; a ConvolverNode is “very expensive” (FFTs every block, in bursts); HRTF panning is “very expensive” (up to four convolvers while a source moves); equal-power panning is “rather cheap” and a StereoPanner cheaper; a GainNode is “very cheap to create and connect”. Blink and WebKit resample buffers linearly: cheap, a little rough. For phones he advises delays, all-passes and low passes instead of convolution reverb, a short reverb and an equal-power panner instead of HRTF, and a “degraded” mode for weak devices.

In the demo, a pickup (14,148 samples) renders in about 0.4 ms, a footstep in 3 to 5 ms, and a one-second, three-layer explosion in 6 to 11 ms once the code is warm (30 ms the first time). That is too long to do on the frame a sword lands, so effects are rendered at load or between frames, which is what Tycho’s Bank already does, then played instantly (inference from the timings). Ten effects with three variants each are under 3 MB of buffers (inference).

So effects with a fixed shape (pickups, hits, steps, doors) want rendering; sounds that follow a continuous value (wind, an engine, a rising danger) want live nodes, and music can be either: live oscillators per note are cheap at chip-like note rates (inference). Tycho already does both, live voices in Engine and offline renders in Bank. What a ZzFX-style synth would add is the compact, seedable description: 20 numbers a recipe, rendered in plain JS with no context at all, so a quest’s sounds could be generated from its seed and tested in Node.

Footsteps, doors and places

Andy Farnell’s Designing Sound (MIT Press, 2010, 688 pages) makes the case that any sound can be built from first principles, from the physics to a model to a Pure Data patch. His 2005 demo made “fire, water, wind, rain, some animals, some footsteps, some guns, some vehicles” for a first-person shooter, all in code (interview, 2012).

Footsteps. Perry Cook’s “Modeling Bill’s Gait” (AES, 2002) analyzes walking into gait timing and heel-toe events, finds a surface’s resonances by linear prediction, and resynthesizes loose surfaces with PhISEM, which treats them as particle collisions arriving as a Poisson process; gravel, grass and snow underfoot are among its uses (Cook 2002, Cook 2003). For a pixel game that reduces to two layers a step, heel then toe 50 to 90 ms apart (inference from Cook):

Surface Body Texture
Grass A faint low thump (90 Hz) Dense grains (350 to 700 a second) of noise in a band near 3 to 5 kHz
Stone A sharp click (noise in a narrow band near 2 kHz, 20 to 30 ms) and a short low knock A little grit: sparse high grains
Wood A ringing knock: noise through a narrow band pass (Q 6 to 11) at 330 to 620 Hz, plus a hollow triangle None
Gravel, snow A soft thump Grains slower and coarser (gravel) or fine and muffled (snow)

A door in the demo is a creak, a low sawtooth heard in bursts through a resonance (the stick-slip of a hinge), then the latch: a low thud and a click. The same recipe with a different resonance is a ship’s timber or a gate (inference).

Water is bubbles. Van den Doel’s “Physically Based Models for Liquid Sounds” (2005) builds streams, pouring, rain and waves from single bubbles: each a damped sine at the Minnaert frequency of its radius, rising in pitch as it rings, and a user study found a rise parameter near 0.1 sounded most real (van den Doel 2005, Moss et al. 2009). Minnaert’s formula puts a 1 mm bubble near 3.3 kHz and a 3 mm one near 1.1 kHz, so a babbling brook is a dozen or more tiny sines a second between 800 Hz and 4 kHz over a bed of low noise (the constant from Minnaert; inference to apply it).

Wind is noise through a band pass whose center and level follow a slow gust signal, and a second, narrow band for the whistle once it’s strong. Birds are short sine chirps, 40 to 90 ms, sweeping fast between 1.5 and 7 kHz, in phrases of two to five. Rain is high-passed noise plus more, smaller bubbles; thunder a low rumble that arrives after its flash (inference, in the manner of Farnell). Tycho’s Music.band is already the wind’s and the stream bed’s building block: a band of looped noise, silent until set.

The demo drives the picture and the sound from one seeded event stream at 30 steps a second: every bubble you hear is a sparkle on the stream, every phrase a bird crossing the sky. That is the rule worth keeping in the game (inference): sound events are the world’s events, so a quest that’s silent (sound off) or deaf (a phone on silent) shows exactly the same thing.

Wind, water and birds. A meadow by a stream, all of its sound made from noise and filters. Press Play and move Weather: wind is two bands of noise steered by a gust signal (the grass and the tree lean with it); the stream is low noise and single bubbles, each a sine rising as it rings at the pitch of its size (a sparkle where it bursts); birds sing in short sine chirps, fewer as the weather turns; past halfway rain comes in, and at the top thunder rolls a moment after its flash. The bars at the top left are the three levels.

The chip as a style

The consoles’ sound chips gave every game the same tight budget, and composers made that budget a style:

Machine Voices What each could do
NES (2A03) 2 pulse, triangle, noise, DMC Pulses at duties of 12.5, 25, 50 and 75%; a 32-step, 4-bit triangle with no volume control, an octave below a pulse at the same timer; noise from a 4-bit period table, with a mode that shortens it to a periodic buzz; 1-bit delta samples. Envelopes clock at about 240 Hz (NESdev: APU)
Game Boy 2 pulse, wave, noise Four duties, a frequency sweep on the first pulse, a user waveform on the third, LFSR noise; envelope ticks at 64 Hz (Pan Docs)
SNES (S-DSP) 8 sample voices BRR-compressed samples at any pitch, 16-bit stereo at 32 kHz, ADSR, echo through an 8-tap FIR, all sharing 64 KB with the program (SNESdev)
Amiga MOD (1987) 4 sample channels Patterns of 64 rows; 8-bit samples; 15 instruments, later 31; each cell starts a note with one effect (MOD, Ultimate Soundtracker)

From those limits came the chip idioms: a chord played as a fast arpeggio on one voice; the triangle as bass, its pitch dropping fast as a kick; noise shared between the hi-hat, the snare and the effects; each part keeping one timbre for the whole tune (inference).

This is the eight-ink rule’s twin. Eight inks force a world to choose what it is; four voices and a noise force a tune to choose what it says, and a player hears the limit as a style rather than a lack (inference). The game needn’t copy any one chip; each world can declare a budget, its voices and their waves, the way it declares its inks. Two worlds then sound as different as they look: a pulse-12.5% lead and a triangle bass at 128 bpm is a toy shop, sines and a slow pad at 70 bpm is a lake at night.

Music that follows the game

Adaptive music has two moves, and every system is some mix of them (Winifred Phillips at GDC 2021, hybrids):

What shipped games did:

Game Year What the music does
Monkey Island 2 (iMUSE) 1991 Michael Land and Peter McConnell’s engine in SCUMM 5, patented in 1994 (US 5,315,057). A “pit orchestra” that plays longer or shorter while waiting for the game; branch and loop markers in the sequences; in Woodtick each building brings its own instrument’s variation of the town theme, and leaving closes with a flourish (iMUSE)
Super Mario World 1990 Riding Yoshi adds a bongo layer to the overworld theme and its arrangements (MarioWiki)
Animal Crossing 2001 on 24 hourly tracks, switching on the hour, in variants for sun, rain and snow (Animal Crossing World)
Rez 2001 Shots make sounds in time with the music; Mizuguchi said quantizing them synced players who weren’t on the beat, “and it felt like magic” (oral history)
Spore 2008 Music generated live in EA’s own Pure Data (EApd), with Brian Eno; notes filtered into scales and modes “so that it started to sound like music instead of noise” (PdCon)
Proteus 2013 Ed Key and David Kanaga: the land, plants and animals make the music, which shifts as you walk; four seasons (Proteus)
No Man’s Sky 2016 Paul Weir’s Pulse, “a glorified random file player”: instruments (sets of variations of one sound) with rules for how often and at what pitch, pan and volume; 24 sets making 60 basic soundscapes from 65daysofstatic’s material. “Very little of the audio is procedurally created, only the creature vocals and background fauna” (A Sound Effect)

Behind the generative ones is Brian Eno. Music for Airports (1978) phases tape loops of different lengths; in 2/1 seven loops each carry one sung note, the C repeating every 20.1 s and the high F every 19.6 s, and “they all repeat in cycles that are called incommensurable — they are not likely to come back into sync again” (Reverb Machine). Generative Music 1 (1996) was a floppy of 12 pieces for SSEYO’s Koan player (Intermorphic).

Three lessons for a small game (inference). No Man’s Sky, the most “procedural” game on the list, streams nearly all its audio: the generative part is the choosing, not the synthesis. Mario’s bongos and Animal Crossing’s hours are the cheapest adaptive music there is, one layer and one clock, and both are remembered. And Spore’s line is the whole trick of generative melody: constrain the notes to a scale, and randomness starts to sound like music.

A tune from a seed

The demo makes a tune for a world in five steps, each small enough for a phone:

  1. A palette from the seed: a family of three modes (day, dusk, night), a root, a tempo (70 to 130 bpm), swing or not, one wave for each of four voices, and a drum kit (a triangle kick falling fast, as on the NES; noise snare and hat).
  2. Chords by a Markov step. Four bars of chord roots, each a move of a third, fourth or fifth likeliest; then the same four again ending home. A chord is three scale tones a degree apart.
  3. Melody by a Markov walk over intervals: small steps likeliest, a leap followed by a step back, chord tones on the strong beats. That is the usual order-1 chain over scale-degree intervals (Melody Triangle).
  4. Repetition. Two 2-bar motifs make an eight-bar song, A A′ B A″, with A″ ending on the tonic. Repetition with small changes is what makes a generated line sound written rather than random (inference).
  5. Stored as scale degrees. Every note is a degree, mapped onto the current mode only when it plays, as Disasterpeace’s #January does (#January).

The modes, in order of brightness, each one note flatter than the last: Lydian, Ionian (major), Mixolydian, Dorian, Aeolian (minor), Phrygian. A family steps along that line at dusk and at night, Ionian → Mixolydian → Dorian, say, so the same tune darkens without being rewritten. The five-note families, major pentatonic → Japanese yo → minor pentatonic → in, give a world a place with few clashing notes. An equal-tempered approximation of other traditions’ scales (gamelan’s slendro or pelog, a maqam) is only a flavor of the real tuning, and should be called that (inference).

Layers by danger. Each voice has a threshold; the danger slider brings layers in at bar lines, as Mario’s bongos do: pad always, then bass (0.1), lead (0.25), hat (0.45), kick (0.55), arpeggio (0.7), snare (0.85). The bass also changes pattern as danger rises: half notes, then a syncopated figure, then octave eighths. Dusk drops the hat; night drops the drums (unless danger is near full), halves the lead and plays it an octave down, and slows the arpeggio to eighths. Tycho’s presence(s, at) would make the same entrances as crossfades rather than cuts.

Scheduling. Notes are scheduled on the audio clock a little ahead, never on the frame or setTimeout, following Chris Wilson’s pattern: “a good place to start is probably 100ms of ‘lookahead’ time, with intervals set to 25ms” (web.dev). The demo schedules each bar that starts within the next 150 ms, from its 30-a-second step, and makes bars again only from the next unscheduled one when the state changes, so a change lands on the next bar line, as iMUSE’s markers do.

A tune for a world. Each New world is a seed: a family of modes, a root, a tempo, a wave for each of four voices (bass brown, pad blue, lead amber, arpeggio green; drums in the bottom lane) and a drum pattern, written under the controls. Press Play to hear it scroll past the white playhead. The lighter rows are the notes of the current mode, the lightest the root: Dusk and Night move the same tune one and two modes darker and thin it out. Danger brings layers in at the bar lines (pad, bass, lead, hat, kick, arpeggio, snare) and drives the bass harder. The melody is a Markov walk over scale degrees, chord tones on the strong beats, in the shape A A′ B A″.

A sound palette per world

The proposal (inference throughout): a quest declares a sound palette beside its palette, a small, fixed set from which every cue and every note is drawn, and from which Tycho can generate what the quest doesn’t write by hand.

Field What it is Example
modes Day, dusk and night modes from one family ['dorian', 'aeolian', 'phrygian']
root, bpm, swing The key and the pulse D3, 48 bpm, straight
voices Three or four parts, each with one wave and register; the budget bass: 'triangle', pad: 'sawtooth', lead: 'pulse25'
drums A kit from noise and the triangle, and its patterns, or none kick: [0, 8], hat: none
layers The state each part enters at { lead: 0.25, kick: 0.55 }
tone A filter ceiling (bright or muffled) and a space (dry room, echo, hall) lp: 1100, space: 'hall'
accent The wave and register its cues use, so its coins and doors belong to it pulse12, high
ambience Its bands of noise and its events wind 0.4, gulls, waves

Sketches for the slice and one proposed quest, extending what their scores already say:

World Modes Voices Pulse Ambience
The Road Aeolian → Phrygian sawtooth strings, a triangle bass; no drums 48 bpm dusk wind
Mrs. Pell’s street Ionian → Mixolydian pulse-25 lead, triangle bass, a soft hat 100 bpm, swung sparrows, a distant radio
The frogs major pentatonic → yo pulse-12.5 lead, triangle bass, arpeggio 104 bpm the pond’s bubbles, croaks
The lighthouse Aeolian, turning Ionian when lit triangle pad, sine lead 42 bpm the sea, gulls, the lamp’s hum
Lost on the moor none: bell, dog and stream only none none fog wind; the bell, the dog and the stream panned by distance

The moor (in Elsewhere’s brief) is the case where sound is the play: the engine’s distance falloff and pan, which Tycho has, do the work.

Two rules from the eight-ink rule carry over. A palette is an identity, and a change of palette is an event: the lighthouse’s turn to major when the lamp is lit is the sound’s version of dusk at the frog stall. And the player is printed in every world’s inks by one recipe: the shell’s own cues (a tap, a page, a souvenir) could likewise be played in the quest’s accent wave and mode, so the Lantern’s chime is always the Lantern but always sounds like where you are.

Phones

Issue What happens What to do
Autoplay Chrome (for Web Audio since version 70 and 71) starts a context made without a gesture suspended; “call context.resume() after the first user gesture (e.g. a click, or tap)”; no exceptions (Chromium) Make or resume the context on the first tap. Tycho’s Engine.listen() does
The silent switch Safari’s default audio session behaves as ambient, so the ring/silent switch mutes Web Audio. Since iOS 17, navigator.audioSession.type = 'playback' plays through it (and pauses other apps’ audio); before, a looping silent <audio> element did. Only Safari has the API (MDN, WebKit 237322) Respect the switch by default; the player who flipped it meant it (inference). A setting could choose playback
Backgrounding Safari puts a context in an interrupted state when the page is left, the tab switched or the screen locked; it must be resumed. WebKit has open reports of contexts that say running yet are silent after a long background, and of a home-screen app whose resume() never returns (MDN, WebKit 276687, WebKit 291892) Suspend on visibilitychange, resume on the next tap, and don’t trust running alone
Latency A few milliseconds on iOS and macOS, about 10 ms on Windows, 30 to 40 on Linux, 12.5 ms to about 150 ms on Android (Adenot) Schedule ahead on the audio clock; quantize to the beat, as Rez does, where timing matters
AudioWorklet Safari 14.1 and iOS 14.5, April 2021 (web-features) Not needed: rendered buffers and built-in nodes cover all of this
Battery and CPU A running context keeps the audio thread and device busy even in silence (inference); convolution and HRTF are the expensive nodes (Adenot) Suspend when nothing is wanted (Tycho’s Engine sleeps when hidden or with both settings at 0); delays for space; a voice cap
Sample rate A context runs at the device’s rate; a buffer at another rate is resampled linearly on each play (Adenot) Render effects at the context’s own rate

What it means for Tycho

Tycho’s audio is a complete engine for one game’s hand-written sound. What Elsewhere needs on top is a way for each quest to have a whole sound from a few declared numbers. As options any game could use, in Tycho.audio (inference):

Part What it is Fit
sfx(recipe, rate) A sample-loop synth of a ZzFX-style recipe (layers of wave, pitch and slide, envelope, filter, grains), returning a Float32Array; runs in Node too, so recipes can be tested Pre-bake, through Bank
sfx.make(kind, rand), sfx.mutate(recipe, rand) sfxr-style generators (pickup, jump, hit, explosion, steps by surface, door, blip) from a seeded stream, and a nudge Pre-bake
scale(name), degree(scale, d, root) Modes and five-note scales as semitone lists, ordered by brightness; a degree to a pitch Core
Clock A beat clock on ctx.currentTime: tempo, swing, bars, a look-ahead scheduler, changes on the next bar line Per frame
Tune A seeded tune from a palette: Markov chords and melody as scale degrees, motifs in A A′ B A″, layers with thresholds read through presence Per frame
palette field in a game’s sound { modes, root, bpm, swing, voices, drums, layers, tone, accent, ambience }, the eight-ink rule for sound Data
ambience Wind, stream bed and rain from bands, and bubbles, chirps and grains as due events, driven by one event stream the picture can share Per frame

None of it changes Lodestar’s sound: Engine, Bank and Music stay as they are, and the new parts feed them (a rendered effect is a bank recipe; a tune’s notes are cues on the music bus) (inference). In Elsewhere, snd.js’s score would become a palette, with the four existing scores as its first examples.

Sources

The full research is in the note on sound and music.