Inspiration
Most dictation tools force the same sequence:
- Stop the thought.
- Reach for a hotkey.
- Start recording.
- Wait for the system.
- Begin speaking.
The delay is small, but it happens every time. More importantly, it interrupts the moment when the thought is already ready.
I wanted dictation to work in the opposite order: speak first, press later.
Roma Just Talk began as an experiment in removing activation from the critical path. Instead of waiting for the microphone before speaking, the user begins talking naturally and presses the hotkey while continuing the sentence.
What it does
Roma Just Talk is a macOS dictation app that captures a short rolling audio buffer locally.
When the user presses the hotkey, RJT includes the speech from the previous few seconds, continues recording live audio, transcribes the complete thought, and inserts the result into the active application.
The interaction becomes:
Think -> speak -> press the hotkey while speaking -> receive text
instead of:
Think -> find the hotkey -> wait -> speak -> receive text
RJT does not continuously transcribe ambient audio. It retains only a short rolling buffer so the beginning of the thought can be recovered when the user intentionally activates dictation.
This does not eliminate speech-model processing time. It removes the human activation wait from the critical path by overlapping activation with speech.
How I built it
Roma Just Talk is built in Swift for macOS and evolved from the VoiceInk codebase.
The central system maintains a short in-memory rolling audio buffer. When activation occurs, RJT sends the buffered audio first and then appends live microphone audio in chronological order. The goal is for both parts to behave like one continuous utterance rather than two recordings joined afterward.
The application also handles:
- Global hotkeys
- Microphone capture
- Active-application text insertion
- macOS Accessibility and input permissions
- Local and cloud speech-to-text paths
- Application-aware behavior and transcription context
- Personal vocabulary and custom transcription endpoints
- Release builds and automated macOS testing
I have also been separating reusable components for an iOS version while keeping the existing macOS behavior stable.
Challenges I ran into
The hardest problem was not basic speech recognition. It was making the interaction reliable enough to disappear.
The buffer-to-live hinge
Early implementations treated pre-activation audio and post-activation audio as separate pieces. Joining them afterward could introduce a pause, duplicate audio, lose words near the boundary, reduce recognition quality, or add latency.
The better architecture is one ordered transcription session: buffered audio first, followed immediately by live audio.
macOS input behavior
Global hotkeys interact with Accessibility permissions, Secure Input, active application focus, modifier-only shortcuts, and tools such as Karabiner-Elements. A shortcut can work correctly in one application and fail or behave differently in another.
Reliable text insertion
A successful transcription is not enough. RJT must insert the result into the correct application, at the correct cursor position, without losing focus or triggering an unrelated shortcut.
Reliability
The core feature already exists. The current limitation is not a missing feature set; it is reliability. Repeated use exposes race conditions, permission problems, application-specific behavior, and end-to-end failures that a controlled demo may hide.
That has made automated release builds, CI, remote desktop tests, and multi-application end-to-end testing important parts of the project.
Accomplishments that I am proud of
I built a working version of a different dictation interaction rather than another interface around the usual record-then-speak flow.
The project demonstrates that activation and speaking do not need to happen sequentially. They can overlap.
Other improvements made while developing RJT include:
- Up to 4x my normal manual typing throughput in suitable dictation tasks
- An 87% reduction in application bundle size
- An 83% reduction in memory use, from roughly 780 MB to 132 MB
- Support for both local and cloud transcription workflows
- A reusable architecture that can extend toward iOS
I am also proud that the privacy model remains bounded: a short local rolling buffer, activated intentionally, rather than continuous ambient transcription.
What I learned
The speech model is only one part of a dictation product.
The larger experience depends on interaction timing, audio continuity, operating-system integration, permissions, cursor focus, application compatibility, and whether the product works repeatedly without requiring attention.
I also learned that a novel interaction can be difficult to explain in text. “Speak first, press later” becomes obvious when demonstrated, but unclear when described with implementation terminology.
The most important lesson has been that reliability is part of the core feature. A strong idea that works only sometimes is interpreted as an idea that does not work.
What's next for Roma Just Talk
The immediate priority is to make the existing critical path dependable:
- Begin speaking.
- Activate within the rolling-buffer window.
- Merge buffered and live audio without a boundary defect.
- Return the transcription quickly.
- Insert it into the intended application.
- Repeat the process reliably.
After that, the next steps are:
- Improve onboarding for the speak-first interaction
- Produce a clearer side-by-side demonstration
- Validate the workflow with first users
- Expand automated end-to-end application testing
- Continue the iOS implementation
- Reduce perceived transcription latency further
- Improve application-aware formatting and context
The long-term goal is simple: dictation should begin when the thought begins, not when the user finishes operating the interface.
Built With
- dictation
- macos
- speech-to-text
- stt
- swift
- wisprflow
Log in or sign up for Devpost to join the conversation.