# 09 · Lo-fi listening room

Built from `prompt.txt` only. The app uses `index.html`, `style.css` and `app.js`, with no build step, external library, service, font or audio asset.

## Decisions

- The sound starts on the Start listening button. The AudioContext is created only inside that click handler. Pause fades down and suspends the context; resume fades back in.
- A sustained C major 9 chord uses five sine/triangle voice pairs. Small fixed detuning adds a gentle beating. A locally generated, low-pass-shaped noise buffer runs independently, so the chord and noise sliders can also isolate either sound.
- Filter, saturation and tremolo run in that order. Each stage has a switch with a short complementary dry/wet transition. Original bypasses the entire chain. The UI keeps these two kinds of bypass separate.
- The filter is a low-pass BiquadFilter with a logarithmic cutoff range of 250 Hz to 15 kHz. Saturation uses an oversampled tanh WaveShaper. Tremolo connects a sine LFO only to a GainNode gain parameter. It does not change oscillator pitch, noise playback rate or sample speed.
- The tremolo range is 0.05–5 Hz and 0–45% depth. The presets increase filtering, drive, noise and tremolo for an audible progression.
- Source and pre-tremolo processed RMS are compared continuously to trim the processed level. Tremolo receives an analytical average-power correction instead of instantaneous normalization, so its intended volume movement survives even at slow rates. The trim is bounded between about -22 and +12 dB. A separate output gain, soft startup and compressor ceiling keep the initial 45% listening level conservative.
- The page has labelled native sliders, keyboard focus states, switch semantics, pressed states for preset/comparison buttons and a reduced-motion rule. No microphone or recording permission is requested.

## Checks

- `node --check app.js` passed.
- Static checks confirmed unique HTML IDs, label targets and the existence of the two local linked files.
- Audio was not auditioned and the browser was not opened by this agent. The root agent owns browser verification at `http://127.0.0.1:4191/apps/09-lofi/`.

## Limitations

- RMS matching controls signal level but is not a perceptual loudness standard. Filtered and distorted timbres can still feel different in loudness. Compensation settles after a short interval when controls change.
- The noise is an eight-second loop. It is generated on each first start and is not a recording of tape or vinyl.
- The implementation deliberately uses no pitch wobble or playback-speed modulation. The worn preset comes from the requested stages.
- Sustained chord voices continue internally while listening; Pause suspends their processing. There is no session persistence or export.

## Snapshot and revisions

- `attempts/001/` preserves the first complete implementation, unchanged.
- After that snapshot, the level-matching meter moved before tremolo. An analytical tremolo correction replaced measurement after tremolo, avoiding compensation that would flatten slow volume movement.
- Removed an unnecessary empty CSS import and added status semantics to the playback indicator.
