# deadlight.cut.sound
The whole sound of DEADLIGHT: three beds, three score cues and twenty-two
one-shots, every one of them mixed against `Film.t`.
## The layers
- **Beds** run for the whole film and are never retriggered, only faded. The
surface wash dies over the first ninety metres of depth; the abyss pressure
bed comes up behind it and never leaves; the thruster is spatial and rides
`Film.subPoseAt(t)`; the silt roar is spatial and exists only where the
vehicle has disturbed something.
- **Score** is three looping cues — descent, the ship, the leaving —
crossfaded across the cuts at 24 s and 70 s, with a 62% duck through the
reach in shot 13 and an 80% fade out over the last eight seconds.
- **One-shots** are re-cued by writing `state`, and each fires exactly once at
its own film time. The two that matter land on shot boundaries: the swell at
32.0 s opens shot 6 (the bow) and the ballast vent at 84.0 s opens shot 14
(the leaving).
## Spatial and not
The listener rides `FilmCamera`, so a spatial source is heard from where the
camera is standing. The thruster (`maxDistance` 95 m, rolloff 1.6), the silt,
the hull ticks and the bioluminescent flash are spatial. The beds, the score,
the sonar and the comms are not: they are the film's own room.
Nobody speaks in this film. The comms clip is the carrier opening and closing
and a two-tone acknowledgement — no words.
## The clips are synthesised
Thirteen `.soundClip` assets, generated from a deterministic 32-bit LCG so a
re-bake is bit-identical, at 24 kHz mono. Looping clips are closed with an
equal-power crossfade of the tail over the head so the seam does not tick.
`Sound.bake()` is an **authoring** step, not a runtime one: it resolves what
is there and generates only what is missing. `ensure()` never generates. Run
`bake()` once, commit the assets.
## Reading it
```luau
require("deadlight.cut.sound").report()
```
returns the mixer channels and, per source, its gain, its channel, whether it
is spatial and where it is standing.
# film
The shared constants and the film clock for DEADLIGHT. Every slice of the
demo reads this module and nothing restates its numbers.
It holds three things:
- The timing spine — the 14 shots, their durations and their windows.
`Film.SHOTS`, `Film.shotAt(t)`, `Film.window(n)`, `Film.movementAt(t)`.
Durations live here and nowhere else.
- The world contract — the shared coordinate frame (`Film.WORLD`), the
named entities each slice owns (`Film.NAMES`), the submersible's contracted
path (`Film.SUB_PATH` / `Film.subPoseAt(t)`), the anchor fallbacks that let
the film play while it is still being built (`Film.ANCHORS`), the palette
(`Film.PALETTE`) and the water's optics (`Film.WATER`).
- The clock — a fixed 1/24 s step advanced once per RENDERED frame by
`deadlight.FilmDirector`, never from wall time. 2,256 rendered frames is
exactly the 94-second film, every time.
```luau
local Film = require("deadlight.film")
Film.restart() -- t := 0, every slice resets, playing := true
Film.seek(48.0) -- deterministic scrub to a moment
Film.status() -- where the film is and who has joined it
Film.register("abyss", { -- join from your own component's awake()
reset = function() end,
tick = function(t, dt) end,
shot = function(n, tLocal) end,
})
```
Handlers run inside `pcall`, so one slice erroring cannot stop the film for
the others. Registering a name twice replaces its handlers.
# deadlight.cut.look
The grade of DEADLIGHT. Lens, meter, colour and grain, all as functions of
`Film.t`.
## The chain
- **DepthOfField** — `focalLength` is derived from the shot's own field of
view against a 24 mm sensor (84 deg becomes 13 mm, 24 deg becomes 56 mm) and
`focusDistance` is the distance from the eye to the point the shot is aimed
at, both read off `cutscene.path`. A lens is never authored twice. Shots 7,
10, 12 and 13 run between f/1.8 and f/2.8.
- **MotionBlur** — a 180 degree shutter for the whole film, which at 24 frames
is a 1/48 s exposure.
- **AutoExposure** — a meter, but integrated on the fixed 1/24 s step rather
than adapted from the frame, so its travel repeats bit for bit. It runs from
-8.1 stops under the sunlit surface to +6.8 in the last shot: **fifteen
stops**, opening at 0.95 stops per second and closing at 3.2, which is what
makes the travel visible in shots 2 to 4 rather than instant.
- **dl_lut** — one of three 32-entry tables, cut at the movement boundaries.
Both cuts land on shot boundaries (t = 24 and t = 70), so the swap happens
on an edit and is invisible.
- **dl_vignette** — opens from 0.16 to 0.44 across the film.
- **LensFx** — the flare, huge on the sunball in movement I and small on a
lamp afterwards, and the grain, which grows with `Film.depthAt(t)` from
0.010 at the surface to 0.055 on the bottom.
## The three tables
| table | movement | what it does |
|---|---|---|
| `dl_lut_descent` | I | cobalt and rich — the last colour in the film |
| `dl_lut_ship` | II | crushed black, warm mids, so lamplit rust reads ochre |
| `dl_lut_leaving` | III | the same darkness, desaturated: emptier |
## Reading it
```luau
require("deadlight.cut.look").report()
```
returns the shot, the field of view, the derived focal length, the focus
distance, the aperture, the meter's current stop, the grain, and which table
is bound.
## Where it lives
Four render-feature components on one entity, `CutGrade`, spawned lazily and
marked temporary. The tables are `.texture` assets under
`/source/deadlight/cut/luts`, authored once and resolved by literal name
afterwards.