# FormantBoard API (Machine Reference)

## Entry Point

- Global runtime object: `window.fb`
- Native WebMCP progressively exposes the complete API through `document.modelContext` when the browser supports it. Its tools cover discovery and schemas, both validators, both sequence players, single-note and keyboard triggering, stop, voice and vowel settings, looping, and formant activation.

## Validate First

- `window.fb.validatePlay(events)`
- `window.fb.validateFromJSON(payload)`

Both return:

- success: `{ ok: true, value: ... }`
- failure: `{ ok: false, error: string }`

Do not call playback methods until validation succeeds.

## Playback Methods

- `window.fb.play(events, options?)`
- `window.fb.fromJSON(payload)`

### `PlayEvent`

```ts
{
  note: number | string;
  time: number; // seconds from now
  dur: number;  // seconds
  velocity?: number;
  vowel?: "ɑ" | "ɛ" | "ə" | "æ" | "ɔ" | "u" | "ʊ" | "ɪ" | "i";
  volume?: number;
  tilt?: number;
  formants?: Array<{ index: number; on?: boolean; frequency?: number; Q?: number; gain?: number }>;
}
```

### `PlayOptions`

```ts
{
  loop?: false | true | number | "infinite";
  // false: play once
  // true or "infinite": repeat until window.fb.stop()
  // number (>=1): total number of full sequence iterations
}
```

### `PerformancePayload`

```ts
{
  bpm?: number; // if present: time/dur are beats
  loop?: false | true | number | "infinite"; // same behavior as PlayOptions.loop
  voice?: {
    vowel?: "ɑ" | "ɛ" | "ə" | "æ" | "ɔ" | "u" | "ʊ" | "ɪ" | "i";
    volume?: number;
    tilt?: number;
    formants?: Array<{ index: number; on?: boolean; frequency?: number; Q?: number; gain?: number }>;
  };
  notes: PlayEvent[];
}
```

Legacy aliases are accepted in `fromJSON` payload notes (`m/t/d/v/ipa/vol/formantOverrides`) but canonical fields are preferred.

## Voice Methods

- `window.fb.setVoice(opts)`
- `window.fb.setLoop(mode)` // sets default loop mode for future play/fromJSON calls
- `window.fb.getLoop()` // returns current default loop mode
- `window.fb.setVowel(vowel)`
- `window.fb.setFormantActive(index, on)`

## App Methods

Everything the UI can do is also in the API (and in WebMCP tools of the same names).

- `window.fb.getState()` // page, vowel, drone, volume, playback, loop, visualization, theme, mic, MIDI, settings, current formants, held notes
- `window.fb.getSettings()` / `window.fb.updateSettings(patch)` // groups: `vibrato`, `f0` (source), `harmonics` (incl. `tilt`), `flutter`, `compression`; only given fields change
- `window.fb.resetSettings()`
- `window.fb.setFormant(index, { on?, frequency?, Q?, gain? }, vowel?)` // persistent formant change
- `window.fb.setCascade(percent, "vowel" | "all")`, `window.fb.setCompensation(on)`, `window.fb.setEffects(on)`
- `window.fb.setVolume(0..100)` // master volume
- `window.fb.setDrone({ note?, transpose?, playing? })` // the Fundamental drone
- `window.fb.noteOn(note, velocity?)` / `window.fb.noteOff(note)` // sustained notes
- `window.fb.stopAll()` // silence everything, including the drone
- `window.fb.setMicListening(on)`, `window.fb.setMidiEnabled(on)`
- `window.fb.setVisualization("spectrum" | "wave" | "mixed")`, `window.fb.setTheme("light" | "dark" | "system")`, `window.fb.setHotkeyHints(visible)`
- `window.fb.navigate("home" | "sandbox" | "api_runner" | "api_docs" | "vowels")`
- `window.fb.getAllIPA()` // `setVowel` accepts any of these 44 symbols

Setting ranges and meanings are in the WebMCP tools' input schemas (`set_formant_vibrato`, `set_formant_source`, `set_formant_harmonics`, ...).

Loop guidance:

- Keep looping off by default.
- Only enable loop when the user explicitly asks for it.

## Discovery Helpers

- `window.fb.discovery`
- `window.fb.schemas`
- `window.fb.schemaJson`
- `window.fb.getSchemaJson()`
