# How Hold-to-Record Mode Works with Modifier Keys in OpenSuperWhisper

> Discover how OpenSuperWhisper uses modifier keys for push-to-talk recording. Learn about instant transcription startup and stop on key-up with a 0.3-second threshold.

- Repository: [Starmel/OpenSuperWhisper](https://github.com/Starmel/OpenSuperWhisper)
- Tags: how-to-guide
- Published: 2026-07-07

---

**OpenSuperWhisper enables push-to-talk recording by holding a single modifier key (⌘, ⌥, ⇧, ⌃, or Fn), using a low-level event tap to start transcription immediately on key-down and stop on key-up after a 0.3-second threshold.**

OpenSuperWhisper is an open-source macOS dictation application that supports hold-to-record functionality using single modifier keys without complex shortcuts. The implementation relies on a preference-driven architecture that coordinates between user settings, event monitoring, and recording state management. Understanding how hold-to-record mode works with modifier keys requires examining the flow from `AppPreferences` through `ShortcutManager` to the low-level `ModifierKeyMonitor`.

## Architecture Overview

The hold-to-record system combines preference storage, trigger selection, and event monitoring to provide seamless voice transcription control.

### Preference Configuration

User settings drive the hold-to-record behavior through two key properties in [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift):

- **`AppPreferences.shared.holdToRecord`**: A boolean toggle that enables the feature (defaults to `true`)
- **`AppPreferences.shared.modifierOnlyHotkey`**: Stores the selected modifier key as an enum value (`.none` indicates standard shortcut mode should be used instead)

When `modifierOnlyHotkey` is set to a specific key like `.rightOption` or `.leftCommand`, the system switches from standard keyboard shortcuts to modifier-only monitoring.

### Trigger Selection Logic

The `ShortcutManager.setupRecordingTrigger()` method in [`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift) determines which input method to activate based on preference hierarchy:

1. **Mouse button hotkey** takes precedence if configured
2. **Modifier-only mode** activates if `modifierOnlyHotkey` ≠ `.none`, which starts a `ModifierKeyMonitor` instance for the selected key and disables the normal keyboard shortcut
3. **Standard shortcut** operates if neither specialized mode is active

This priority system ensures only one trigger mechanism is active at any time to prevent conflicts.

### Low-Level Event Monitoring

When modifier-only mode is selected, `ModifierKeyMonitor` (implemented in [`ModifierKeyMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ModifierKeyMonitor.swift)) creates a **Core Graphics event tap** that monitors system-wide `flagsChanged` events. The monitor:

- Filters for events matching the chosen modifier key's `keyCode`
- Invokes `onKeyDown` when the key is pressed
- Invokes `onKeyUp` when the key is released

This low-level access allows the app to detect modifier key states even when OpenSuperWhisper is not the active application.

### Recording State Management

Both the standard shortcut and modifier-key monitor forward events to the same callbacks in `ShortcutManager`:

**On key-down (`handleKeyDown`):**
- Recording starts immediately
- A `DispatchWorkItem` is scheduled to execute after the `holdThreshold` (0.3 seconds)
- When the work item fires, it sets an internal `holdMode` flag to `true`

**On key-up (`handleKeyUp`):**
- The pending `DispatchWorkItem` is cancelled
- If `holdMode` is `true` (meaning the key was held longer than 0.3 seconds), the recording stops and `holdMode` resets to `false`

This timing mechanism distinguishes between a quick tap (which might trigger other actions) and an intentional hold-to-record gesture.

## Implementation Examples

Configure the hold-to-record preference and select a modifier key in the settings UI:

```swift
// Settings.swift - Toggle for enabling the feature
Toggle("Hold-to-record", isOn: $viewModel.holdToRecord)
// Stores to AppPreferences.shared.holdToRecord

// Picker for selecting a single modifier key
Picker("Modifier", selection: $viewModel.modifierOnlyHotkey) {
    ForEach(ModifierKey.allCases.filter { $0 != .none }) { key in
        Text(key.displayName).tag(key)
    }
}
// Stores to AppPreferences.shared.modifierOnlyHotkey

```

Programmatically configure the system to use Right Option key with hold-to-record:

```swift
// Configure preferences directly
AppPreferences.shared.modifierOnlyHotkey = ModifierKey.rightOption.rawValue
AppPreferences.shared.holdToRecord = true

// ShortcutManager automatically wires the monitor
// No additional code required - the shared instance listens for key-down/up

```

Alternative configuration using a mouse button instead of a modifier key:

```swift
// Enable mouse-button mode (takes precedence over modifier keys)
AppPreferences.shared.mouseButtonHotkey = MouseButton.right.rawValue
// Same hold-to-record logic applies via handleKeyDown/handleKeyUp

```

## Key Source Files

| File | Role |
|------|------|
| [`OpenSuperWhisper/ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ShortcutManager.swift) | Central coordinator that decides trigger type, manages the 0.3-second hold threshold via `DispatchWorkItem`, and interfaces with the recording engine |
| [`OpenSuperWhisper/Utils/AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Utils/AppPreferences.swift) | Persists user settings including `holdToRecord` boolean and `modifierOnlyHotkey` enum value |
| [`OpenSuperWhisper/ModifierKeyMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ModifierKeyMonitor.swift) | Implements low-level event tap for `flagsChanged` events and translates physical key presses into `onKeyDown`/`onKeyUp` callbacks |
| [`OpenSuperWhisper/Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Settings.swift) | SwiftUI interface that exposes the hold-to-record toggle and modifier key picker to users |

## Summary

- **Preference-driven activation**: The feature is controlled by `AppPreferences.shared.holdToRecord` and `modifierOnlyHotkey`, allowing users to choose between standard shortcuts and single-key modifiers.
- **Hierarchical trigger selection**: `ShortcutManager.setupRecordingTrigger()` prioritizes mouse buttons, then modifier keys, then standard shortcuts to prevent input conflicts.
- **Low-level event access**: `ModifierKeyMonitor` uses a Core Graphics event tap to detect modifier key states system-wide through `flagsChanged` events.
- **Timed state transitions**: A 0.3-second `DispatchWorkItem` distinguishes between taps and holds, ensuring recording only commits when the user intentionally holds the key.

## Frequently Asked Questions

### What modifier keys can be used for hold-to-record?

OpenSuperWhisper supports the standard macOS modifier keys: **Command** (⌘), **Option** (⌥), **Shift** (⇧), **Control** (⌃), and **Function** (Fn). Each key can be configured independently for left or right variants (e.g., Left Command vs. Right Command) through the `ModifierKey` enum in [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift).

### How does the app distinguish between a quick tap and a hold?

The system uses a **0.3-second threshold** managed by `ShortcutManager`. When a key is pressed, a `DispatchWorkItem` is scheduled to run after 0.3 seconds and set `holdMode = true`. If the key is released before this timer fires, the work item is cancelled and `holdMode` remains `false`, preventing the recording from stopping prematurely. Only keys held longer than this threshold trigger the hold-to-record behavior.

### Can I use a mouse button instead of a keyboard modifier?

Yes. `ShortcutManager.setupRecordingTrigger()` checks `AppPreferences.shared.mouseButtonHotkey` before examining `modifierOnlyHotkey`. If a mouse button is configured (such as `MouseButton.right`), the system enables mouse-button mode and uses the same `handleKeyDown`/`handleKeyUp` callbacks, applying identical hold-to-record timing logic.

### Where are the hold-to-record preferences stored?

Preferences are persisted in [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift) using a singleton pattern (`AppPreferences.shared`). The `holdToRecord` boolean and `modifierOnlyHotkey` string are stored in the user's standard preferences domain, allowing settings to persist across application launches and sync with the Settings UI defined in [`Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/Settings.swift).