# Museum ambience, reader sounds, and automatic doors

Status: IMPLEMENTED (issue #28, 2026-08-12; owner round two 2026-08-13). The
controller lives in `src/inception/audio/museumSoundController.ts` over the
pure rules in `src/inception/audio/museumSound.js`; the corridor portal doors
and in-room return doors carry the automatic inside-out aperture; the Menu
has a **Background sound** volume slider whose zero mutes every museum sound.
Rooms have no ambient bed; their R readers and boundary doors may play the
short interaction effects below. This plan does not add narration to the
museum runtime.

## Experience rule

- Museum corridors carry a very quiet deep-space air/noise bed.
- Enclosed exhibit rooms have no ambient sound.
- ANY visitor — the local player or a networked one — approaching a door
  opens it automatically and plays one short opening sound, whether or not
  they go on to enter. The room title above a door is always readable.
- Opening and closing the R reader plays one short holographic gesture each.
- The Menu has one **Sound** volume slider, and it moves EVERY museum sound
  — the bed, the door effects, the reader gestures, and any future
  non-narration sound. 100% is the museum's own mix, 0 mutes it, and it goes
  to 200% for a quiet room or a laptop speaker (owner round nine).
- Sound supports the space; it must never become the main event.

The generated review assets are:

- `content/inception/assets/audio/museum/deep-space-ambience-loop-v1.mp3`
- `content/inception/assets/audio/museum/door-open-energy-v1.mp3`
- `content/inception/assets/audio/museum/reader-open-hologram-v1.mp3`
- `content/inception/assets/audio/museum/reader-close-hologram-v1.mp3`

They are deterministic procedural originals generated by
`scripts/generate-museum-audio.mjs`; no third-party recording or model output
is involved.

## Deep-space ambience

The ambience is filtered, pink-leaning noise with a barely perceptible low
body and no melody, voice, alarm, impact, or repeating rhythmic event. The
source loop is 24 seconds and is crossfaded at the sample level for a clean
loop.

Runtime rules:

- Start only after a user gesture satisfies browser audio policy.
- Fade in over 1.5 seconds; fade out over 0.8 seconds.
- Target an intentionally quiet perceived level, approximately 24–30 dB below
  ordinary narration. Begin with runtime gain `0.20`; tune by ear on laptop,
  headphones, phone, and iPad rather than normalizing it louder.
- The bed is the museum's REFERENCE LEVEL: every short effect is mixed as a
  multiple of it (`EFFECT_LEVELS`), and its runtime gain is computed from
  the peak its own bytes were normalized to (`ASSET_PEAK`). Hand-set gains
  had the door arriving at thirteen times the bed (owner round eight); the
  door is now 3x and the reader gestures 2x. Re-normalizing an asset changes
  its gain, not its loudness in the room.
- Play only while the local visitor is in the main museum corridor or another
  explicitly declared deep-space corridor zone.
- Do not play inside Van Gogh House or any other exhibit room. Whether the Van
  Gogh learning corridor should inherit this sound is a curator decision;
  default it to silent because its visual course already carries a guided
  narration in the demo.
- Keep one shared loop and one gain node. Do not create an audio element per
  corridor segment.
- Pause when the page is hidden and resume with a fade, not from a new random
  point.

## Automatic door behavior

Opening is a visual state, not an automatic teleport. The visitor approaches,
the door clears, and crossing the existing portal plane begins the existing
transition.

Use a small state machine per local active door:

```text
closed → opening → open → closing → closed
```

- Begin opening when ANY visitor — local or networked — is within range of
  the door rectangle (owner round two: no facing requirement; everyone in
  the space sees the same door answer whoever walks up to it, whether or not
  they enter). The range depends on where the door is: **3.0 m in the
  corridor**, where a door is something you cross toward, and **1.5 m for a
  room's own way out** (owner rounds eight and nine) — inside a place you
  are standing in, a door opening metres off reads as the room reacting to
  you.
- Keep it open until every visitor is beyond 3.6 m (2.1 m in a room). The
  wider close threshold is hysteresis; it prevents a door and sound from
  chattering at one distance.
- The opening animation should take about 0.65–0.9 seconds and dissolve
  FROM THE INSIDE OUT: a clear aperture grows from the door's centre with a
  soft rim, not a uniform whole-sheet fade and not hinged metal-door motion.
- The room title (nameplate) above a door is ALWAYS shown — it no longer
  breathes or fades with its door.
- Play the opening sound once on the `closed → opening` transition — never
  once per frame or on the closing transition. The level the LOCAL device
  plays it at is attenuated by the local player's own distance to that door
  (full within 3 m — as far as a corridor door opens — and silent beyond
  20 m), so a remote visitor's far door is a whisper.
- Add a per-door local cooldown of at least 1.5 seconds between SOUNDS; the
  door's motion itself never refuses.
- A muted Background sound setting (volume zero) suppresses playback but
  does not change door movement or portal behavior.
- A room still loading remains sealed. Approaching may show a quiet loading
  response, but it must not open into an unloaded space or play a false success
  sound.
- The Van Gogh connector keeps its own reviewed distance curve and remains
  local-only.

## R reader interaction sounds

- A real closed-to-open R reader transition plays the 0.46-second rising
  holographic gesture once.
- Closing an open reader by R, Escape, the visible Close control, walking out
  of range, or leaving the room plays the 0.34-second folding gesture once.
- Maximize, Restore, scrolling, and replacing content inside an already open
  reusable panel do not play either sound.
- The guided Demo's scripted readers and narration remain unchanged; these
  cues belong to the visitor-triggered R reader in the museum runtime.
- The effects are quieter than the door and much shorter than narration. They
  contain no speech, melody, alarm, or sharp click.
- The existing Menu **Sound: On / Off** preference silences both reader
  effects immediately. A failed or still-loading audio buffer never blocks the
  reader.

## Menu and persistence

- One **Sound** volume slider in the existing Menu (owner round two
  supersedes v1's plain On/Off switch; round nine widens it to everything),
  not a separate floating HUD.
- It scales EVERY museum sound: the bed through the next frame's fade, the
  short effects on their shared bus — including one already in flight.
  0% mutes the museum, 100% is its own mix, 200% is the ceiling.
- Default to the mix for a new visitor, but do not autoplay until a real
  gesture. Older settings carry forward: v1's Off reads as muted, v2's
  bed-only percent reads as the same number for everything.
- Persist the preference locally under a versioned museum setting key
  (`inception.sound.v3`, an integer percent).
- The setting must be available to keyboard, pointer, and touch, announce
  its value to assistive technology, and update audio immediately.

## Architecture

Create one reusable museum audio controller owned by the main runtime:

- lazy Web Audio context or equivalent browser-safe audio layer;
- one loop source/gain path for ambience;
- a small reusable pool for short effects;
- explicit `setZone`, `setVolume`, `doorOpened`, `readerOpened`,
  `readerClosed`, `suspend`, and `dispose` methods;
- no independent animation or timer loop; transitions use the museum's frame
  clock or Web Audio automation;
- failed audio loads are silent and non-blocking.

Asset URLs should be build-resolved and content-local. Published room documents
must not supply arbitrary audio URLs. A future room-specific sound requires a
reviewed asset registry and a schema decision; it is outside this batch.

## Acceptance criteria

- The corridor loop begins only after user interaction, stays subtle, loops
  without a click, and fades out before entering a room.
- Every exhibit room has no ambient bed; only its boundary door and an
  explicitly opened/closed R reader may play short effects.
- Volume zero immediately silences all museum ambience/effects and survives
  a reload; raising it resumes the bed only where the active zone allows it,
  and the slider scales the bed alone.
- One R open produces one opening gesture and one actual close produces one
  closing gesture; Maximize/Restore, the image view, and redundant cleanup
  calls stay silent.
- One approach produces one opening sound — from the local player or a
  networked one, entering or not — at a level set by the local player's own
  distance. Standing near or crossing back and forth inside the hysteresis
  band produces no repeated sound.
- A loading or failed room never opens early.
- Page hide/show, room unload, sign-in UI, and runtime disposal leave no orphan
  audio playing.
- Tests cover zone changes, menu persistence and migration, autoplay
  rejection, fades, hysteresis, cooldown, nearest-visitor triggering with
  local-ear attenuation, and silent failure.
- Manual QA includes speakers and headphones at normal system volume; if the
  ambience is the first thing a reviewer notices, it is too loud.

## High-level LOE

| Work | LOE |
|---|---:|
| Shared audio controller, asset loading, fades, lifecycle | 1.5–2.5 days |
| Menu toggle and persistence | 0.5–1 day |
| Generic automatic-door state and integration | 1.5–3 days |
| Tests and multi-device/manual audio QA | 1–2 days |

Total: approximately **4.5–8.5 engineering days**, depending on how many door
types are unified in the first implementation.
