# deadlight.abyss.column
Movement I's water column at x = 2000, two kilometres from the wreck so no shot
of the descent can see the ship before shot 6 reveals it.
`build()` puts the sea's underside in the scene — a 680 m plane at the surface
height the world contract names, wearing the `sea_surface` material — and puts
the `sunlight` pass in the frame, which draws the caustic net on everything
below the surface and the shafts standing in the water between.
`apply(t)` writes how much sun there is in the water at film time `t`: full
through shot 1, eaten by the fall, and zero from twelve seconds on. At zero the
surface is taken off screen entirely — there is nothing above the lens in
movements II and III, and a plane hanging there would be a lie in every upward
angle — and the pass returns on a uniform before touching a texture, so it costs
one branch for the seventy seconds it is not wanted.
# deadlight.abyss.medium
The water itself: extinction, the colour of the column, and the absence of any
sky or sun. One number carries the whole descent.
`install()` kills both defaults — the blue sky the engine draws with no `Skybox`
in the scene, and the directional sun it lights with no `sun` entity — and puts
the `water_extinction` pass in the frame. `apply(t)` writes the water's optics
for film time `t`: the per-channel extinction, the light standing in the column
at that depth, and the three lamp positions the pass charges the excess light
path against.
`filmDepth(t)` is the depth the film is claiming in front of the LENS.
`Film.depthAt` is the authority at both ends — 4 m at t = 0 and 3,800 m from
t = 24 on — and between them it runs ahead of the shot list, so these keys are
the same curve made to pass through the depths the shots are staged at.
`columnColorAt(depth)` interpolates `Film.PALETTE` on the square root of depth,
so the first hundred metres get most of the travel, the way losing light
actually looks. `sample(t)` reports what `apply` would write without writing it.
# deadlight.abyss.life
The things that live down here. Each one is on screen for one shot and gone, and
each carries the window it lives in — outside that window it is hidden rather
than moved, because a rattail swimming through the debris field at eighty
seconds is a continuity error nobody would see coming.
| body | shot | what it does |
|---|---|---|
| a shoal of 44 silver fish | 2 | drifts across the sunlit column and scatters out of the vehicle's path |
| a siphonophore | 3 | a colony of eleven bells on one stem, hanging while the vehicle falls past |
| a comb jelly | 3 | crosses the viewport, its eight comb rows beating in a wave |
| a sea cucumber | 5 | crawls, at the pace a holothurian crawls, at the edge of the light |
| a rattail | 7 | noses out of the hawse pipe, looks at the light, and goes |
| a squat lobster | 9 | runs the deck plate ahead of the beam and ducks behind a bitt |
| a small pale fish | 10 | in through the open deadlight and out again |
| a shrimp | 12 | sits on the porcelain cup, then lifts off in one flick |
| five cyan flashes, one rose | II & III | a hard rise and a long decay — the shape a photophore fires with, and a point light that reaches the water around it |
Every body is a procedural body of revolution with a fin on it: one builder
covers a fish, a rattail, a shrimp, a sea cucumber and a siphonophore bell, and
what differs is the radius profile and the length.
Nothing is animated from wall time. Every position, every heading and every
flash is a function of `Film.t`.
# deadlight.abyss.motes
Everything suspended in the water, as six GPU emitters stepped by exactly
`Film.DT` once per rendered frame from the film's own clock.
- **snow** — marine snow, a box the camera lives inside. World-static and
sinking at four centimetres a second, so what makes it stream is the camera's
own travel through it. It is the only readout that the submersible is falling.
- **wake** — snow the vehicle drags with it, thrown along its travel, killed by
drag in about a second, then drifting back up. That is what "the snow inverts
direction as it passes" is made of.
- **silt** — the slow roll ahead of the downwash, the curtain hanging in the
beams over the deck, the curl out of an open porthole.
- **bloom** — the same sediment thrown hard instead of lifted: the thruster jet
that blanks the frame, and the cloud the take-off kicks up.
- **amphipods** — the scavengers that boil out of the sediment the moment a
light hits it.
- **flakes** — corrosion the thrusters shake off the plating. It falls rather
than billows.
Each disturbance names the window it runs in and where it comes from, and rises,
holds and falls across it — a disturbance rather than a switch. Every one is a
function of `Film.t` and of nothing else.
`tick(t, dt, stepped)` takes `stepped = false` while the clock is being scrubbed
through many film frames inside one rendered frame, and leaves the emitters
alone; the driver catches them up afterwards over a bounded run.
**A mote is visible at its own colour whether a lamp reaches it or not.** The
sprite program adds a point-light pickup on top of the particle's colour and
`lightInfluence` reaches no shader (#10085), so what keeps the void empty is the
colour itself: dark enough to disappear into the water, brought up several times
over by a flood that actually reaches it.
# 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.abyss.floor
The abyssal plain the wreck lies on. `build()` spawns the `Seafloor` entity —
a 460 x 560 m mesh on a 2 m grid, deliberately coarse, carrying only the long
drift dunes and the shallow scour the hull dug when it landed. Everything a
camera 30 cm off the floor actually reads lives in the `seafloor` shader,
computed per pixel from world XZ.
`heightAt(x, z)` is the same field the mesh was built from, so anything that has
to sit ON the floor — a sea cucumber crawling, the silt roll the downwash
lifts — can ask where the floor is rather than guessing y = 0.