Mix
Everything Hollowbell is heard through: the sound library, the mixer, the music beds and the voice.
Mix
Everything Hollowbell is heard through: the sound library, the mixer, the music beds and the voice.
Attach it to one entity and the whole game has sound. The scene's entrypoint
does that — it spawns HollowSystems/sound and puts this component on it —
so nothing else needs to know the mix exists. Slices ask for sound by name
through Hollow.sound.play(name, opts) and for a spoken line through
Hollow.say(key), and this answers both.
What it does
The library. Every name in Hollow.SOUND_NAMES is a .soundClip, built
at load out of arithmetic — sound.library writes the recipes, sound.synth
does the arithmetic. Nothing is downloaded and no service is called, so the
crypt sounds the same on its first frame in a browser as it does here, and a
sound is never missing because a generation failed. The eighteen sounds the
game can ask for in its first second are baked in one pass before the
provider goes up (about 90 ms); the rest follow across the next few frames.
The mixer. Six channels — music, ambient, sfx, ui, voice, and
the master over them — each a field on the component, so the balance is
editable without touching code. A one-shot carrying opts.position is
thrown from a ring of spatial emitters and is heard from that point in the
room, fading out by maxDistance; everything else plays flat.
Eleven impacts inside one breath sum past what the mixer can render, so the crowd pulls each new voice down: the first two are at full weight and each one after that takes a little off. A repeat of the same sound inside 45 ms is dropped — two copies of one impact phase-add into one louder impact, which is not more information.
The beds. One looping piece per state of the run — title, Nave,
Cloister, Belfry, boss, death, victory — crossfaded over 1.6 seconds on the
events the run emits, with the crypt's own air under all of them. Each bed
is written so its last sample runs into its first: sustained partials are
snapped to whole cycles across the buffer and noise is cross-faded onto
itself, which clipRef:loopSeam() confirms rather than assumes.
The voice. The ten lines of Hollow.LINES, spoken once by audio_gen
and kept as clips under /zero/source/sound/voice/. The service reads in
one voice, so the two speakers are separated by where they are and how they
are played: the narrator is dry, close and unpitched; the Bellwarden is
pitched down, thrown spatially from the bell itself, and arrives with bronze
under him. Music and ambience duck while either is speaking and lift again
after. A line whose clip is missing is logged and the caption still carries
it.
Fields
| field | what it sets |
|---|---|
masterVolume | the level of the whole mix |
musicVolume | the bed under each state of the run |
ambientVolume | the crypt's air, water and distant stone |
sfxVolume | hammer, congregation, gates, loot, the bell |
uiVolume | the sounds a card and a cursor make |
voiceVolume | the narrator and the Bellwarden |
spatialVoices | how many one-shots can sound from a point at once |
Reading it back
sound.state is the blackboard: State.summary() reports how many clips,
beds and voices exist, what each one measured, which bed is playing, and any
name a slice asked for that the library could not answer.
Scoped to this part · feeds back into the world's score.