# Dictation

> Speak messages into the conversation composer and choose the engine that turns speech into text.

Dictation lets you speak a message into the conversation composer instead of
typing it. You dictate in the app. The `hansard dictation` command sets up and
checks the engines that turn your speech into text.

- App: **Conversation composer → Dictate**
- CLI: `hansard dictation`

## Dictate in the app

1. Open a conversation that shows a composer, or start a new conversation. See
   [Continue in another harness](https://www.hansard.dev/guides/continue/).
2. Press and hold the microphone button in the composer (**Hold to dictate**)
   and speak.
3. Release the button. The composer shows **Transcribing…**, then inserts the
   text where the cursor was. When the browser's own speech recognition is in
   use, words appear while you speak instead.
4. Edit the text if needed and send it.

The first time you dictate, the Mac app or your browser asks for permission to
use the microphone.

To start and stop with separate clicks instead of holding, open
**Dictation options** (the arrow beside the microphone) and turn off
**Hold to record**. The button then reads **Dictate**: select it once to start
and again to stop. With the button focused, `Enter` or `Space` also starts and
stops recording.

**Dictation options** also lists your microphones under **Microphone**, and
its last line names the engine that will transcribe your speech. The app
remembers your microphone and **Hold to record** choice in this browser or app.

Dictation needs the app running on the same machine as Hansard. It is not
available when the app is served read-only or reached from another machine.

## Set up dictation from the CLI

Check which engines are ready:

```sh
hansard dictation status
```

Unless you choose an engine, Hansard uses the first one that is ready, in this
order:

| Engine    | Setup                                                                                          | Where your audio goes          | Punctuation            |
| --------- | ---------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------- |
| `apple`   | Built into the Mac app on macOS 26 or later. For `hansard web`, run `hansard dictation setup`. | Stays on this Mac              | Yes                    |
| `whisper` | Install whisper.cpp, then run `hansard dictation setup --whisper`.                             | Stays on this machine          | Yes                    |
| `openai`  | Set `OPENAI_API_KEY` in the environment that runs the app.                                     | Sent to OpenAI                 | Yes                    |
| `command` | Set `dictation.command` to your own program.                                                   | Wherever your program sends it | Depends on the program |

When none of these is ready, the composer uses the browser's own speech
recognition. It has no punctuation and can need a network connection.

### On-device Apple speech

The Mac app includes the speech helper. To use Apple speech with `hansard web`,
build the helper into `~/.hansard/bin`:

```sh
hansard dictation setup
```

This needs macOS 26 or later and Xcode or its command line tools. Audio never
leaves the Mac.

### whisper.cpp

Install whisper.cpp, then download a model into `~/.hansard/models`:

```sh
brew install whisper-cpp
hansard dictation setup --whisper
```

The default model is `base.en`. To download a different one, add
`--model <name>`, for example `--model small`. Models download from the
whisper.cpp model repository on Hugging Face. Transcription then runs on this
machine.

### OpenAI transcription

Set `OPENAI_API_KEY` in the environment of the process that serves the app, for
example the shell that runs `hansard web`:

```sh
export OPENAI_API_KEY=<your-key>
hansard web
```

The default model is `gpt-4o-mini-transcribe`. To use another OpenAI
transcription model, run `hansard config set dictation.model <model>`.

> **OpenAI receives your recordings:** With the `openai` engine, each recording is sent to OpenAI with your API key.
> Use `apple` or `whisper` to keep audio on your machine.

### Your own command

Set `dictation.command` to a program that reads an audio file and prints the
transcript. Hansard replaces `{file}` in the command with the path of a 16 kHz
mono WAV recording, or adds the path at the end when `{file}` is absent. The
command runs without a shell and must finish within two minutes.

## Choose an engine

To use one engine and never fall back to another, set `dictation.backend`:

```sh
hansard config set dictation.backend whisper
```

| Value                                   | Effect                                                     |
| --------------------------------------- | ---------------------------------------------------------- |
| `auto`                                  | Use the first ready engine, then the browser. The default. |
| `apple`, `whisper`, `openai`, `command` | Use only that engine.                                      |
| `browser`                               | Use only the browser's own speech recognition.             |
| `off`                                   | Turn dictation off and hide the microphone.                |

To set the language, use a locale such as `en-US`:

```sh
hansard config set dictation.locale en-US
```

The app reads these settings when it loads. Reload the browser tab or reopen
the app after a change.

## Troubleshooting

**The microphone button is missing.** `dictation.backend` is `off`, or the
browser cannot open a microphone. Run `hansard dictation status`.

**Dictation needs a transcription backend.** No engine is ready and the browser
has no speech recognition. Set up an engine with `hansard dictation setup`.

**Microphone access was denied.** Allow microphone access for the Mac app or
the browser in your system or browser settings, then try again.

**Dictated text has no punctuation.** The browser's recognizer is in use. Set
up `apple` or `whisper` for punctuated text.

**No speech was recognized.** The recording was silent or too short. Hold the
button a little longer and speak closer to the microphone.
