# Prism Light: design notes

The keeper of Kelda Head Light, on the north cape of Lumen Reach, has five nights to keep the cape's ships off the reefs. The lamp turns a lens cut from a shard of Doorly Orbital's glass crown. The shard fell on the cape the night the station broke up, the same night Seven's escape pod came down at sunrise. The lens has six facets. Each is one element of the Prism Forge VFX pack, and each has a job.

## The brief

- A new GameLab entry with a game nobody here has made yet, tied to the earlier projects in an unexpected way.
- Assets from the 3D collection, remodelled in Blender for the web.
- The Island V5 loader recipe: every file checked and shown as it lands, then every shader compiled before the start screen.
- Borrow freely from the rest of the site.
- Use the Prism Forge VFX pack (uploaded with the brief).

## The idea

A lighthouse keeper game. You are not on a ship, you are not a robot and you never move. You hold the light.

The lineage runs sideways instead of forward:

- **Island V5 (Lumen Reach).** The game happens on the island's north cape. Island V5 points north "toward a lighthouse that only matters after dark". This is that lighthouse.
- **Starlight Menagerie.** The lens is cut from the station's glass crown. The Falling Crown night drops more of it out of the sky.
- **Lodestar.** The Lodestar thrush migrates by night. Lodestar was about finding the way by starlight; here the danger is a light that is too bright. Real lighthouses killed migrating birds by the thousand, and keepers' records of those strikes were among the early data on migration. The light the ships need is the light the birds can't survive.
- **Breathglass.** Glass that sings. The lens panels shimmer with dispersion and each facet glows its own colour.
- **FIELDWORK and Island V5.** The Glassmire Kraken, FIELDWORK's rigged Kraken, which Island V5 tamed, now guards the reefs.

## The rules

- **Bearing.** Every ship has a ring that empties in the dark (in 30 to 38 seconds, faster in fog and storm) and fills while the beam is on it. Halo fills it fastest; the other coloured facets guide at about half strength; Void doesn't guide at all. Inside the cove's reach, the quay lamps take over.
- **Lost.** At zero the crew loses its position. The ship drifts with the tide toward the nearest reef. Light her again and she takes a new bearing and replans her lane.
- **Lanes.** `src/nav.js` lays a 12 m grid over the bay. Land and the water within 26 m of a reef are walls, water near reefs costs more, and A* finds each ship a way from the horizon to its berth. The path is pulled tight by line of sight.
- **Hazards** (`src/hazards.js`). Each one charges while the right facet is on it, faster with Focus:
  - Fog bank, cleared by Ember. Ships inside can't see the beam, and their bearing drains three times faster.
  - Rogue wave, cleared by Rime. A ridge of water stands up and runs at a hull. Two hits sink a ship.
  - Waterspout, cleared by Volt. It wanders across the lanes.
  - Kelp tangle, cleared by Venom. A snared hull drags onto the ledge after 26 seconds.
  - Wreckers' false light, cleared by Halo. Ships with a weak bearing steer for it and onto the rocks.
- **Thrushes** (`src/birds.js`). A steady beam that holds a flock catches it: the birds circle the lantern and start to drop after a few seconds. Turn to Void and hold the lamp dark and they find the stars again.
- **Wrecks and lifeboats.** A wreck puts one or two boats in the water. They row only while the beam is on them.
- **The Kraken** (`src/kraken.js`). From night 5, and in the Endless Watch, every cleared hazard lights one band. With six, the next hull that touches a reef is caught and turned back to sea.
- **Dawn.** At 05:30 every ship still afloat can see the coast and counts as home. Three ships lost in a night and the light has failed.

## The nights

1. **First Watch.** Ships and fog. Learn Halo and Ember.
2. **Rime Night.** Rogue waves.
3. **The Night Migration.** Thrush flocks all night. Learn Void.
4. **Wreckers' Moon.** False lights and kelp.
5. **The Falling Crown.** Storm, waterspouts, everything, and glass from the old station streaking across the sky. The Kraken wakes.
6. Then the **Endless Watch**, generated from a seed per night.

Each dawn card ends with one true note from the margins of lighthouse history: Fresnel's lens, the Draupner wave, birds and lights, the wreckers legend, sector lights.

## Models (Blender)

All built by Python scripts in `blender/` (run with the `bpy` module or `blender -b -P`):

- `kit.py`: shared helpers. Synty and Wolf Pack models arrive textured from palette atlases; every face's colour is sampled from its UV triangle (centroid and corners, averaged) into a corner colour attribute, so the web build needs no textures and pieces share one vertex-coloured material per role.
- `build_ships.py`: the fleet. Synty POLYGON Pirate hulls (sloop, schooner, frigate, rowing boat, skiff) and Wolf Pack Games' Merchant Brig and Naval Snow. Each is flattened, its furled-sail and collision variants dropped, scaled to a real length, turned bow to +Y, set on its waterline, and fitted with the night fittings no pack had: stern lantern, masthead light, red and green sidelights and lit stern windows. Each ship is four meshes (hull, sails, lamps, windows). The frigate's hull is decimated by half.
- `build_coast.py` with `world.py` (the map, shared with the game through `data/layout.json`):
  - the terrain of the cape: a coastline from signed distances (shore, the tapered headland, the west horn, Gull Rock, four sea stacks, the cove), cliffs that rise 24 m within a few metres of the sea, a sand beach in the cove, coloured per face by height and slope;
  - the lighthouse, modelled from scratch: plinth, a tapered tower in fourteen courses with two red day-mark bands, a stair-window spiral, sixteen corbels, the gallery and its railing, the watch room, the glazed lantern with vertical and helical astragals, a copper dome with ventilator ball, vane and lightning rod, the keeper's cottage and the compound wall;
  - the lens: six bullseye panels on a hexagonal drum, each with a central lens, six concentric prism rings and seven catadioptric prism bars above and below, in a brass frame on a geared clockwork pedestal;
  - Kelda Cove's quay village from Synty docks, shanties, lamps and clutter;
  - the kit: four reef clusters (wet-darkened and weed-lined), two wrecks, palms, bushes, stones, kelp, far islands, the bell buoy (modelled), the wreckers' camp, and a low-poly Swainson's thrush whose wing weights live in vertex alpha.
- Packed with gltf-transform and meshopt (`tools/pack-glb.mjs` from Starlight Menagerie).

## Rendering

- **One cone test.** `beam.litAt` decides what is lit. The same yaw, half angle and range drive the sea shader's lit water and glitter path, the fog puffs and waterspouts glowing in the beam, the visible shaft, the spotlight that lights hulls and cliffs, and the drum's turn in the lantern room. What you see lit is what the ships can see.
- **The sea** (`src/sea.js`) reads depth from the coast's height grid for colour and shoaling, foams at the shore and around every reef, mirrors Samhain in a warm glitter path, and carries rogue waves as uniform ridges. The JavaScript mirror of the wave function keeps every hull riding the water the shader draws.
- **The sky** (`src/sky.js`): stars, a faint galaxy band, cloud that thickens with the storm, lightning flashes, Samhain as a thin banded crescent, and the meteors of the Falling Crown.
- **Prism Forge** (`src/forge.js`): after the pack's Three.js adapter, with pooled geometry buffers and a world scale. Every clear plays the facet's element: Ember breath and detonation for fog, a Rime fault line along the frozen crest of a wave, a Volt sky lance down a waterspout, a Venom vortex in the kelp, a Halo nova over a false light. Charging shows the element's aura; docking plays a Halo mend; a freed flock leaves a Void blink at the lantern; the storm throws Volt sky lances on the horizon; the Kraken rises in a Rime summon; a crown shard lands as a Halo meteor.

## Loader and performance

- After Island V5 and Starlight Menagerie: `loader.js` and the `assets.js` manifest (`tools/make_manifest.py` writes it), version cache-busting, a reset that clears only this game's caches, and named build steps with an estimate of time left.
- Before the start screen, one of everything is put in front of the compiler (every ship, fog, a waterspout, kelp, a false light, a rogue wave, thrushes, the Kraken, an aura and a nova in every element, a meteor) and compiled through the full post chain, then tidied away.
- Weather, storm, flashes and dawn are uniforms; the light count never changes during play.
- Low quality (the default on touch): no antialiasing, pixel ratio up to 1.25, fewer palms and bushes, fewer rain streaks, fewer live effects, a 60 fps cap and adaptive resolution. On touch screens the HUD drops its backdrop blur. The light count is fixed for the whole game (false lights and the Kraken dim permanent lights), so nothing compiles mid-play. Every scripted ship lane is planned on the loading screen.

## Testing

- The mobile pass (1.0.2) is repeatable. `tools/mobileaudit.mjs` screenshots every screen on five devices in both orientations and flags overlaps, off-screen parts, small tap targets and sideways scroll. `tools/touchtest.mjs` drives the touch controls with real multi-finger touch events. `tools/reach.mjs` measures which bearings each screen shape can see and reach. `tools/mobileprof.mjs` counts draw calls, triangles and shaders compiled mid-game on the low preset.
- `tools/boatcheck.mjs` checks that ships sit on the water the player sees. It rebuilds the sea mesh's triangle under each hull, computes its vertex heights from the shader's own formula, and measures how far the drawn water stands above the waterline at bow, midships and stern on any night. It caught an inverted pitch and hulls flooding in the storm (1.0.1).
- The autopilot commits to one target at a time (a ship until her ring is full, a hazard until it clears), so its beam sweeps like a keeper's instead of flicking between two needs. An early recording caught the flicking, and a second caught freed flocks flying into the camera at full exaggerated size; both were fixed before the final video.

- `tools/play.mjs` drives the game headless (Chromium with SwiftShader) with the built-in autopilot (`?auto=1`), stepping the simulation in fixed steps through `window.__pl.advance` and saving a frame per step. It logs every spawn, wreck, clear and dock. It was used to tune the nights. `tools/record.sh` records the playthrough one night at a time (so a restart only costs one night) and `tools/stitch.sh` joins the nights and lays the music and sea beds under them. The tools expect Playwright and the gltf-transform packages from the cloud workspace; adjust the import paths at the top to run them elsewhere.
- `tools/shot.mjs` and `tools/probe.mjs` take screenshots and evaluate expressions in the running game.

## Sound

`assets/sfx-report.json` lists every source file from the SFX library, its levels and the processing. Beds are 30-second crossfaded loops. The gull and the flock are stand-ins built from other recordings, because the library has no gull or wing recordings. The two music loops are synthesized.
