audio
Procedural storm audio. The module computes rain and thunder samples in Luau and bakes them into `.soundClip` assets, so the storm has a voice made from code alone. Every sample comes from a small deterministic xorshift generator, so every bake of the same seed is identical. Samp…
audio
Procedural storm audio. The module computes rain and thunder samples in Luau and bakes them into
.soundClip assets, so the storm has a voice made from code alone. Every sample comes from a small
deterministic xorshift generator, so every bake of the same seed is identical. Samples are mono,
32-bit float, at 32000 samples per second.
bake() creates the clips storm_rain, storm_thunder_1, storm_thunder_2 and storm_thunder_3.
The world holds soundClips under those names (the rain stored as PCM and the thunder as Opus, as
bake sets them), and the fx_storm scene plays them: StormRain loops the rain, and
StormDirector plays the thunder through Thunder1 to Thunder3.
Exports
M.thunder(seed, seconds): returns abufferofsecondsworth of rolling thunder: a sharp crack with three snaps, then a long low rumble that swells in seven bumps (distant reflections arriving over the first few seconds) and dies away. The result is normalised, soft-clipped withtanh, and fades out over its last 0.6 seconds so the clip ends silent.M.rainBed(seed, seconds): returns abufferofsecondsworth of rain and wind: hiss, drops on the ground and roof, and a slow gusting low howl. It generates 0.8 seconds past the end and crossfades that tail into the start, so the clip loops cleanly. The result is normalised to a peak of 0.7.M.bake(): bakes every storm clip that is missing and returns the names present afterwards,{ "storm_rain", "storm_thunder_1", "storm_thunder_2", "storm_thunder_3" }.storm_rainisrainBed(7, 8.0)stored as PCM.storm_thunder_1tostorm_thunder_3arethunder(100 + i * 17, 9 + i)forifrom 1 to 3, stored as Opus. Each clip is created only when its name is free, so repeated calls are idempotent.
Usage
local StormAudio = require("storm.audio")
-- create any of the four storm clips that are missing
local names = StormAudio.bake()
-- or compute samples directly
local rain = StormAudio.rainBed(7, 8.0)
local thunder = StormAudio.thunder(117, 10)
Scoped to this part · feeds back into the world's score.