Sound
The audio of **First Light: Nearside** — the bed under the whole run, the music that reads the danger, the furniture of the drive, and Peary Station's voice on the radio.
Sound
The audio of First Light: Nearside — the bed under the whole run, the music that reads the danger, the furniture of the drive, and Peary Station's voice on the radio.
Mounted on mount_sound by the scene entrypoint. It needs nothing configured:
it stands up its own sources, sets the mix and subscribes to the game's events,
so the game sounds right whether or not any other part of it calls in.
What it builds
Five temporary children of the mount, one per job:
| Entity | Channel | What |
|---|---|---|
snd_bed | ambience | The cabin: a 55 Hz hum breathing under a thin radio hiss. Loops seamlessly, forever. |
snd_music_1 / snd_music_2 | music | Two decks. A state change fades one up while the other falls, so nothing ever cuts. |
snd_motor | sfx | The drive, pitched and levelled by the rover's speed. |
snd_shots | sfx | The host every one-shot plays through. |
snd_radio | voice | Peary Station. Ducks the music and the bed while she talks. |
Nothing is spatial: this is a mix a player wears, not a place they walk around.
The surface
Published as sound on game.ns.
local sound = require("game.ns").get("sound")
sound.play("hop") -- a one-shot from the library
sound.play("land", { pitch = 1.1 })
sound.say("halfway") -- speaks the line AND puts its words on screen
sound.text("halfway") -- the written form on its own
sound.seconds("halfway") -- how long that caption should stand
sound.music("danger") -- "title" | "run" | "danger" | "won" | "lost" | "off"
sound.musicState() -- what is playing now
sound.speaking() -- true while a line is in the air
sound.captions(false) -- stop putting spoken lines on screen
sound.ids() -- every line id
The library
One-shots — hop, land, land_crater, can, stall, warn_hot,
warn_cold, fuel_low, ui_move, ui_confirm, ui_back, win, lose.
Synthesised sample by sample in Luau and baked to .soundClip, so they cost
nothing to make and cannot fail to exist.
Music — music_title, music_run, music_danger, music_won,
music_lost. Each loop is crossfaded tail-into-head so its seam is inaudible.
Voice — voice_<id> for every line in lines.module.
What it listens to
It subscribes to game.ns and plays what each event means, so no other slice
has to remember to make a noise:
| Event | What is heard |
|---|---|
phase → title / howto | the title theme, and Peary opens the run |
phase → intro / playing | the run bed |
phase → won / lost | the ending music, its sting, and Peary's last word |
introDone | "Stay in the copper." |
hop | the thruster; the first one earns a line |
land | dust, or the deeper thump of a crater |
stall | the grind of soft regolith |
can | the chime of xylidine; the first one earns a line |
band → hot / cold | the warning, the danger music, and Peary saying why |
fuelEmpty | the low-tank triple |
It also watches the rules ledger for the halfway mark and for a tank under a quarter, and speaks those without being asked.
The mix
Voice over music over ambience, with the music and the bed stepping back while
a line is in the air. The bus levels are public fields — masterVolume,
musicVolume, sfxVolume, voiceVolume, ambienceVolume, motorVolume —
and each one-shot carries its own weight on top, so the sounds that decide a
run sit above the furniture.
Every line is also text
A spoken line arrives as a narration event carrying the words, the speaker,
how long they should stand and whether the player wants captions drawn — and
the words are handed to screens.caption directly as well. Two routes, because
the text is the half of a line that has to survive: a player with the sound off,
a player in a noisy room, and a clip that failed to generate all still get it.
The event fires whichever way the caption preference is set; captions(false)
says the player would rather not read them, and whoever draws them obeys.
Scoped to this part · feeds back into the world's score.