score
Everything the film is heard through: a score of five cues crossfaded against film time, eight world sources that are the sound of the place, and thunder that arrives behind its own flash by the time sound takes to cross the distance.
greensky/score — the sound of Green Sky
Everything the film is heard through: a score of five cues crossfaded against film time, eight world sources that are the sound of the place, and thunder that arrives behind its own flash by the time sound takes to cross the distance.
local Score = require("greensky.score")
Score.thunder(2400, 0.9) -- the sky slice, on the frame a discharge fires
Score.gainsAt(t) -- the whole mix at any film time, pure
Score.bedLevelAt(t) -- how much non-spatial sound is up at time t
Score.mixState() -- what is sounding right now, and what thunder did
The score
Five cues, and the film is always carried by at least one of them.
| cue | what it is | up from | down by |
|---|---|---|---|
cue_dry | high, still, bleached. No bass at all — the weight arrives with the storm and this leaves room for it. | 0:00 | 0:34 |
cue_approach | a low throb that has no business being in act I, and a swell that never resolves. | 0:14 | 1:10 |
cue_dark | the bruise. A minor second held against the root, low enough to read as pressure rather than as a chord. | 0:32 | 1:36 |
cue_max | everything in cue_dark plus a tritone, a churning mid band, and eight slow pounds. The storm at its worst. | 1:00 | 1:34 |
cue_arc | the whole spectrum back at once, and major. The only cue in the film with a third in it. | 1:33 | — |
Both act transitions are pinned to the cut frame rather than eased across it, so the score turns over on the cut and not near it:
- t = 32 s, shot 6 — the bright pad goes and the black drone arrives.
- t = 94 s, shot 15 — the drone and the churn go and the major cue arrives.
The place
Four beds heard everywhere, because they are everywhere, and four sources heard from where they are.
| source | spatial | what it does over the runtime |
|---|---|---|
wind | no | over the flat from the first frame; rises with the cell and never stops |
drift | no | dry salt streaming over the crust in act I; stops dead when the rain arrives at 0:44 |
rain | no | in at 0:41, up through the middle, gone by 1:52 |
water | no | standing water settling, from 1:34 to the end |
curtain | yes | the wall crossing the pan west to east at 40 m/s, over the origin at 0:44, carrying 3 km |
hail | yes | one burst over the field, 1:26 to 1:33 |
airframe | yes | rides KILO, through gliders.poseAt |
thunder | yes | one report per discharge, placed out over the cell |
A spatial source is heard at its mix gain times the distance attenuation
from wherever the shot stands — so the curtain arrives, passes and leaves,
and the hail is loud in the shot that is about hail and faint in the one
three kilometres up. Which is why the film's continuity never rests on
them: bedLevelAt(t) is the sum of the non-spatial gains and its minimum
across the whole 122 seconds is 1.29, at t = 0.
Thunder
Score.thunder(distance, energy)
distance in metres, energy roughly 0..1. The report is queued and
played distance / 343 seconds later, from a point that far out in the
direction of the cell, with one of three clips chosen by how far it came:
a crack under 900 m, a roll under 2.6 km, and past that only the low end,
because that is all that survives the distance.
Until the sky slice starts calling, a fallback schedule of fourteen discharges carries act II on its own. The first external call stands the fallback down, and it comes back if the calls stop for ten film seconds — so the demo is never without thunder and the two never double up.
Where the sound comes from
Nothing here is generated by a service and nothing is downloaded. All
fifteen clips are synthesised from raw interleaved f32 PCM by
greensky/score/bake.luau and written next to it as
.soundClip assets. audio.encodePcm is pure CPU: it works headless and
it never fails for credit or network reasons. Run that file to re-bake.
Every looping bed is seamless by construction, and the bake measures it — oscillators snapped to a whole number of cycles per loop, noise filtered with wrap-around indices, impulse grains written wrapping. See the bake script's header for the three rules and the numbers.
Levels
The mix is nothing but per-frame gain. Every source exists and plays from
build() at volume zero; a cue is never started or stopped, only faded.
There is no moment in the film at which a clip has to begin, which is what
guarantees there is no gap and no restart click.
The film's master level is two channel trims in build() — music at
0.60 and world at 0.70. With those, the mixer peaks at 0.70 at the
storm's maximum; at unity it clipped at 1.15.
What it needs from the scene
An AudioListener, which film_camera carries. Without one every spatial
source in the world is inaudible. The four beds are unaffected.
Scoped to this part · feeds back into the world's score.