# theyogram/souffleur architecture

> Architecture diagram of the GitHub repository theyogram/souffleur, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-12). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/theyogram/souffleur
- Repository: https://github.com/theyogram/souffleur

## Overview

Souffleur is a macOS voice-following teleprompter. Users manage local scripts, select one for playback, then the app captures microphone audio, recognizes speech on-device, matches spoken words against the script, and updates the floating prompter’s position. Sound-triggered and constant-speed modes, script import/editing, audio-device selection, appearance settings, and spoken-text display are also documented capabilities. Edges reflect sampled calls or explicit README statements; unsampled feature links are omitted where their wiring is not evidenced.

Main entry points: [`Souffleur/App/PlaybackController.swift`](https://github.com/theyogram/souffleur/blob/main/Souffleur/App/PlaybackController.swift) (Playback controller), [`Souffleur/Speech/AppleSpeechEngine.swift`](https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/AppleSpeechEngine.swift) (Apple speech engine), [`Souffleur/Prompter/PrompterWindowController.swift`](https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/PrompterWindowController.swift) (Prompter window), [`Souffleur/Library/ScriptStore.swift`](https://github.com/theyogram/souffleur/blob/main/Souffleur/Library/ScriptStore.swift) (Local script store)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_app["App orchestration"]
  node_controller["Playback controller"]
  node_settings["Preferences<br/>[SettingsView.swift]"]
  node_settingskeys["Settings keys<br/>[SettingsKeys.swift]"]
end

subgraph group_scripts["Script library"]
  node_libraryui["Script library UI<br/>[LibraryView.swift]"]
  node_scriptstore[("Local script store<br/>[ScriptStore.swift]")]
  node_scriptmodel["Script records<br/>[Script.swift]"]
end

subgraph group_speech["Audio and recognition"]
  node_capture["Microphone capture<br/>[AudioCapture.swift]"]
  node_devices["Audio devices<br/>[AudioDevices.swift]"]
  node_speechengine["Apple speech engine"]
  node_locale["Locale resolver"]
  node_permissions["Speech permissions"]
end

subgraph group_tracking["Speech tracking"]
  node_matcher["Script matcher"]
  node_tokenizer["Text tokenizer<br/>[Tokenizer.swift]"]
  node_vocabulary["Context vocabulary"]
  node_scrollmode["Scroll driver<br/>[ScrollDriver.swift]"]
end

subgraph group_prompter["Prompter interface"]
  node_window["Prompter window"]
  node_textview["Script text view"]
  node_animator["Scroll animator"]
end

node_user(("Presenter"))
node_speechservice["Apple speech services"]

node_user -->|"edits scripts"| node_libraryui
node_libraryui -->|"creates and edits"| node_scriptstore
node_scriptstore -->|"stores records"| node_scriptmodel
node_user -->|"starts playback"| node_controller
node_controller -->|"loads script"| node_scriptstore
node_controller -->|"tokenizes script"| node_tokenizer
node_controller -->|"resolves locale"| node_locale
node_controller -->|"extracts context"| node_vocabulary
node_controller -->|"starts capture"| node_capture
node_capture -->|"queries devices"| node_devices
node_controller -->|"starts recognition"| node_speechengine
node_speechengine -->|"requests access"| node_permissions
node_speechengine -->|"uses on-device recognition"| node_speechservice
node_speechservice -->|"streams transcript"| node_speechengine
node_controller -->|"matches transcript"| node_matcher
node_controller -->|"updates cursor"| node_window
node_matcher -->|"uses token rules"| node_tokenizer
node_window -->|"renders script"| node_textview
node_window -->|"advances scrolling"| node_animator
node_controller -->|"reads playback settings"| node_settingskeys
node_settings -->|"uses preference keys"| node_settingskeys
node_user -->|"configures display"| node_settings

click node_controller "https://github.com/theyogram/souffleur/blob/main/Souffleur/App/PlaybackController.swift"
click node_settings "https://github.com/theyogram/souffleur/blob/main/Souffleur/Settings/SettingsView.swift"
click node_settingskeys "https://github.com/theyogram/souffleur/blob/main/Souffleur/Shared/SettingsKeys.swift"
click node_libraryui "https://github.com/theyogram/souffleur/blob/main/Souffleur/Editor/LibraryView.swift"
click node_scriptstore "https://github.com/theyogram/souffleur/blob/main/Souffleur/Library/ScriptStore.swift"
click node_scriptmodel "https://github.com/theyogram/souffleur/blob/main/Souffleur/Library/Script.swift"
click node_capture "https://github.com/theyogram/souffleur/blob/main/Souffleur/Audio/AudioCapture.swift"
click node_devices "https://github.com/theyogram/souffleur/blob/main/Souffleur/Audio/AudioDevices.swift"
click node_speechengine "https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/AppleSpeechEngine.swift"
click node_locale "https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/LocaleResolver.swift"
click node_permissions "https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/SpeechPermissions.swift"
click node_matcher "https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/ScriptMatcher.swift"
click node_tokenizer "https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/Tokenizer.swift"
click node_vocabulary "https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/VocabularyExtractor.swift"
click node_scrollmode "https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/ScrollDriver.swift"
click node_window "https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/PrompterWindowController.swift"
click node_textview "https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/PrompterTextView.swift"
click node_animator "https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/ScrollAnimator.swift"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_controller,node_settings,node_settingskeys toneBlue
class node_libraryui,node_scriptstore,node_scriptmodel toneAmber
class node_capture,node_devices,node_speechengine,node_locale,node_permissions toneMint
class node_matcher,node_tokenizer,node_vocabulary,node_scrollmode toneRose
class node_window,node_textview,node_animator,node_user,node_speechservice toneIndigo
```

## Components

### App orchestration

- [Playback controller](https://github.com/theyogram/souffleur/blob/main/Souffleur/App/PlaybackController.swift): `Souffleur/App/PlaybackController.swift`
- [Preferences](https://github.com/theyogram/souffleur/blob/main/Souffleur/Settings/SettingsView.swift): `Souffleur/Settings/SettingsView.swift`
- [Settings keys](https://github.com/theyogram/souffleur/blob/main/Souffleur/Shared/SettingsKeys.swift): `Souffleur/Shared/SettingsKeys.swift`

### Script library

- [Script library UI](https://github.com/theyogram/souffleur/blob/main/Souffleur/Editor/LibraryView.swift): `Souffleur/Editor/LibraryView.swift`
- [Local script store](https://github.com/theyogram/souffleur/blob/main/Souffleur/Library/ScriptStore.swift): `Souffleur/Library/ScriptStore.swift`
- [Script records](https://github.com/theyogram/souffleur/blob/main/Souffleur/Library/Script.swift): `Souffleur/Library/Script.swift`

### Audio and recognition

- [Microphone capture](https://github.com/theyogram/souffleur/blob/main/Souffleur/Audio/AudioCapture.swift): `Souffleur/Audio/AudioCapture.swift`
- [Audio devices](https://github.com/theyogram/souffleur/blob/main/Souffleur/Audio/AudioDevices.swift): `Souffleur/Audio/AudioDevices.swift`
- [Apple speech engine](https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/AppleSpeechEngine.swift): `Souffleur/Speech/AppleSpeechEngine.swift`
- [Locale resolver](https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/LocaleResolver.swift): `Souffleur/Speech/LocaleResolver.swift`
- [Speech permissions](https://github.com/theyogram/souffleur/blob/main/Souffleur/Speech/SpeechPermissions.swift): `Souffleur/Speech/SpeechPermissions.swift`

### Speech tracking

- [Script matcher](https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/ScriptMatcher.swift): `Souffleur/Tracking/ScriptMatcher.swift`
- [Text tokenizer](https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/Tokenizer.swift): `Souffleur/Tracking/Tokenizer.swift`
- [Context vocabulary](https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/VocabularyExtractor.swift): `Souffleur/Tracking/VocabularyExtractor.swift`
- [Scroll driver](https://github.com/theyogram/souffleur/blob/main/Souffleur/Tracking/ScrollDriver.swift): `Souffleur/Tracking/ScrollDriver.swift`

### Prompter interface

- [Prompter window](https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/PrompterWindowController.swift): `Souffleur/Prompter/PrompterWindowController.swift`
- [Script text view](https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/PrompterTextView.swift): `Souffleur/Prompter/PrompterTextView.swift`
- [Scroll animator](https://github.com/theyogram/souffleur/blob/main/Souffleur/Prompter/ScrollAnimator.swift): `Souffleur/Prompter/ScrollAnimator.swift`

### Other components

- **Presenter**
- **Apple speech services**

## Connections

- Presenter → Script library UI: edits scripts (evidence: `Souffleur/Editor/LibraryView.swift`)
- Script library UI → Local script store: creates and edits (evidence: `Souffleur/Editor/LibraryView.swift`)
- Local script store → Script records: stores records (evidence: `Souffleur/Library/ScriptStore.swift`)
- Presenter → Playback controller: starts playback (evidence: `README.md`)
- Playback controller → Local script store: loads script (evidence: `Souffleur/App/PlaybackController.swift`)
- Playback controller → Text tokenizer: tokenizes script (evidence: `Souffleur/App/PlaybackController.swift`)
- Playback controller → Locale resolver: resolves locale (evidence: `Souffleur/App/PlaybackController.swift`)
- Playback controller → Context vocabulary: extracts context (evidence: `Souffleur/App/PlaybackController.swift`)
- Playback controller → Microphone capture: starts capture (evidence: `Souffleur/App/PlaybackController.swift`)
- Microphone capture → Audio devices: queries devices (evidence: `Souffleur/Audio/AudioCapture.swift`)
- Playback controller → Apple speech engine: starts recognition (evidence: `Souffleur/App/PlaybackController.swift`)
- Apple speech engine → Speech permissions: requests access (evidence: `Souffleur/Speech/AppleSpeechEngine.swift`)
- Apple speech engine → Apple speech services: uses on-device recognition (evidence: `Souffleur/Speech/AppleSpeechEngine.swift`)
- Apple speech services → Apple speech engine: streams transcript (evidence: `README.md`)
- Playback controller → Script matcher: matches transcript (evidence: `Souffleur/App/PlaybackController.swift`)
- Playback controller → Prompter window: updates cursor (evidence: `Souffleur/App/PlaybackController.swift`)
- Script matcher → Text tokenizer: uses token rules (evidence: `Souffleur/Tracking/ScriptMatcher.swift`)
- Prompter window → Script text view: renders script (evidence: `Souffleur/Prompter/PrompterWindowController.swift`)
- Prompter window → Scroll animator: advances scrolling (evidence: `Souffleur/Prompter/PrompterWindowController.swift`)
- Playback controller → Settings keys: reads playback settings (evidence: `Souffleur/App/PlaybackController.swift`)
- Preferences → Settings keys: uses preference keys (evidence: `Souffleur/Settings/SettingsView.swift`)
- Presenter → Preferences: configures display (evidence: `README.md`)
