# Observer Effect — doomsday clock A period-styled (1964) clock for running Delta Green: *Observer Effect* online. - **Player screen** — `observer.ellie.town/` (`clock.html`): an Olympian Holobeam Array master chronometer. Nixie tubes counting toward 22:03:37, warning lamps that flash on each pulse, a red wash and dissonant flutes/drums as communion nears. Read-only; safe to share with the players. - **Handler panel** — `observer.ellie.town/control` (`control.html`): run/hold, scrub the clock, jump to any scenario beat, set the iteration (which resets reality to its wake point), and fire pulse / communion / text effects on the players' screen. ## How it syncs A tiny Rust WebSocket relay (`relay/`) holds the one authoritative clock. The panel sends authenticated commands; every connected screen gets the new state and free-runs the digits locally between actions. Late joiners get the current state immediately. Served behind nginx with TLS; the relay only listens on loopback. ## Deploy 1. DNS: point `observer.ellie.town` at the VPS (A/AAAA). ACME does the cert. 2. It's already wired into `flake.nix` (vps modules) as `./services/observer-effect.nix`. Rebuild the VPS: `nixos-rebuild switch --flake .#vps` (however you normally deploy). ## The control key Commands are authenticated with a shared token in `services/secrets/observer_vps.yaml` (sops). Enter it once in the panel's **CONTROL KEY** field (saved in that browser). Rotate any time with `sops services/secrets/observer_vps.yaml` then rebuild. ## Running a session The clock starts paused at iteration I, 17:00:00 (the Agents' arrival). Drive it by hand to match table pacing — **JUMP TO BEAT** for the scripted moments, or **RATE ×N** + **RUN** to let it tick. Fire **PULSE** on the live shudders and **COMMUNION** at 22:03:37; then hit **ITERATION II/III/IV** to reset reality nearer the end as the loop tightens. **SHOW** broadcasts a line of text over the players' screen (e.g. *"I see the throne of God."*). ## Sound effects The player screen's pulse / communion / broadcast-text sounds are pre-rendered mp3s in `site/sfx/`, synthesized from pure ffmpeg `lavfi` filtergraphs (no samples). Regenerate with: ```sh cd site/sfx && nix-shell -p ffmpeg --run ./generate.sh ``` If a file fails to load the screen falls back to an equivalent WebAudio synth. The ambient dread drone is always synth (it's driven by the tension level). ## Local dev (no Nix) ```sh cd relay OBSERVER_TOKEN=test cargo run # relay on 127.0.0.1:8770 # serve ./site on :8080 and proxy /ws -> 127.0.0.1:8770, or just open # site/clock.html and append ?, then point common.js' WS at ws://localhost:8770/ws ``` The pages connect to `wss?:///ws`, so behind nginx everything is same-origin.