Score
Boot mounts this on the scene's `score` anchor. It is the only thing in the game that plays music, air, the field kit, the acts on the hill, and the Almanac's voice. It **listens** — no other slice calls into it, and it calls into no other slice.
Score — the sound of FIRST LIGHT: RESOWN
Boot mounts this on the scene's score anchor. It is the only thing in the
game that plays music, air, the field kit, the acts on the hill, and the
Almanac's voice. It listens — no other slice calls into it, and it calls
into no other slice.
What it plays
| layer | clip | when |
|---|---|---|
| score | bed_night / bed_firstlight / bed_day / bed_dusk | crossfaded on phase, 2.6 s |
| air | amb_valley | always, louder at night and dusk |
| threat | amb_threat | level tracks FL.state.shrineThreat, silent at 0 |
| beam | beam_hum | held for exactly as long as beam(true) lasts |
| field kit | ui_move ui_confirm ui_back ui_open ui_close | the ui event |
| the hill | sow harvest harvest_leavened burn plate_ring zyme_well | sow / harvest / burn / beam / a new night |
| dawn | dawn_chime, then a bird every 7–20 s | first light and day |
| the end | sting_won / sting_lost | won / lost |
Events it answers
start · screen · phase · burn · sow · harvest · beam ·
shrineThreat · won · lost · ui.
It also re-reads FL.state.phase, .screen and .shrineThreat every frame,
so a change the rules slice makes without emitting is still heard. Whichever
arrives first wins; the second is a no-op.
Events it emits
caption(speaker, text, seconds)— every time the Almanac speaks, whether or not there is audio behind it. The presentation slice prints it.voiceMissing(lineId)— only when that line's.soundClipis absent. The caption still fires, so the words are never lost.
The Almanac
Six lines, one voice, generated once through audio_gen.speech and cached as
.soundClips in ../voice/. They are spoken on the title, the first night,
the first first-light, the first leavened harvest, the win and the loss —
each exactly once per run (screen == "title" clears the record). A line
that has already been said is never said again, so a phase that repeats does
not repeat the narration.
The mix
Loudest first: the Almanac (and it ducks everything else by 58% while it
speaks), then the burn and the beam, then the hill answering the Facer, then
the score, then the valley under all of it, and the field kit quietest so a
menu never argues with the hillside. The channel levels are set once in
build() from Bank.CHANNELS; per-clip levels live in Bank.LEVEL.
Fields
| field | default | meaning |
|---|---|---|
master | 1.0 | the whole mix, in one number |
crossfade | 2.6 | seconds a bed takes to hand over |
soundOn | true | off entirely, without unmounting |
Play only
Every emitter is built on the first update, which is play-only, and torn
down in editorUpdate and onDestroy. An editor session stays silent.
Rebuilding the bank
Every clip is arithmetic (../synth.module) baked by ../bake.module. Run
the bake from an execute, never from a component:
local B = require("game.sound.bake")
B.bakeBed("night") ; B.bakeBed("firstlight") ; B.bakeBed("day") ; B.bakeBed("dusk")
B.bakeAir()
B.bakeShots("ui") ; B.bakeShots("act") ; B.bakeShots("birds") ; B.bakeShots("stings")
B.report() -- audio.info on every clip: duration, channels, codec
The noise is a seeded LCG, so a re-bake is byte-identical.
amb_zyme is baked and unused here — it is the ferment's own spatial bed,
left in the bank for the zyme slice to place near a bloom.
Scoped to this part · feeds back into the world's score.