Releases: doublej/utt
Release list
utt 2.0.0
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
Plugins are now extensions, and one of them can no longer start working just by
appearing on disk. Alongside that: utt can tidy a transcript with Apple's
on-device model, and everything that receives a transcript now also receives
what was actually heard.
Plugins are extensions
The word changed everywhere — the settings page, the manifest directory, the
documentation. "Plugin" suggested something bolted on; these are programs that
speak to utt, and several of them are the whole reason a transcript leaves the
app at all.
This is the breaking part of 2.0, and it falls on the programs, not on you.
utt moves your existing plugins/ folder to extensions/ once, on first
launch, so everything installed carries across with its settings and its state
intact. What does not move is the program on the other side: a companion that
writes into plugins/ is now writing into a folder utt no longer reads, and
needs its author's updated build. The deep link changed with it —
utt://show?section=plugin:<id> is now section=extension:<id>.
Nothing about the file formats changed, only where they live.
An extension asks before it can do anything
Installing an extension has always been dropping a file in a folder — no
installer, no registry, nothing signed. That is worth keeping, and it is why
utt now asks.
What it could not stay is silent. A manifest that appeared could ask for your
audio, every transcript you dictate and the API token, and until now it got all
three the moment it landed. A manifest that appears now lands pending: inert,
with no clips transcribed, no transcripts delivered and no token written, until
you say yes on its page. The page tells you what it asked for in plain words
before you decide.
Everything you already had keeps working, and you will not be asked about it.
Extensions present when you upgrade are carried over as approved — or as
switched off, if that is what you had already chosen.
An extension that sends a clip before you have ruled on it gets an answer saying
so, rather than watching a file that never appears.
You decide whose clips go first
Several extensions can want a transcription at the same time, and until now the
oldest clip simply won. Each extension that sends audio now has a place in the
queue you choose: First, In turn, or Last. Its own clips still come
back in the order it sent them.
It decides who goes next, never who gets interrupted — whatever is being
transcribed finishes either way.
utt can tidy what you said
Switch on Text → Cleanup and utt runs the transcript through Apple's
on-device language model before it lands: the "um", the false start, the
sentence you corrected halfway through, and the punctuation a recogniser does
not add. It is off by default, and it stays off until you turn it on.
The model is never trusted. Everything it is allowed to do is a deletion or a
punctuation change, and utt checks that its answer really is your words with
some taken out — not a summary, not a rewrite, not an answer to a question it
thought you asked. Anything else and the raw transcript is pasted instead,
whole. It costs about a second a paragraph, which is why it is a choice rather
than the default.
docs/transcript-cleanup.md has the full account, including what it will not do
and why the numbers are what they are.
What was heard, beside what was typed
Once something rewrites your words, "the transcript" stops being one thing. So
every place a transcript goes now carries both: what the recogniser heard, what
finally landed, and which stages changed it — your replacement rules, the
cleanup stage, formatting, a filtering extension, or an API caller's own hints.
That is in the panel after you dictate, in the history, in an extension's file
and in the API response. A stage that ran and left your words alone is now
distinguishable from a stage that never ran, which it was not before.
Each of those stages also reports what it cost, in milliseconds, so a dictation
that felt slow can be attributed rather than guessed at.
The Text settings are the pipeline, in order
The old Output section had grown into a drawer. Text now has its own group in
the rail — Replacements, Cleanup, Formatting — listed in the order
they actually run, and Output keeps delivery and saving. Old links to the
retired pages still land somewhere sensible.
Fixes
Your clipboard comes back whatever was on it. The restore only ever put back
plain text, so a clipboard holding an image, a file or styled text got nothing
back and kept the transcript instead.
utt's own files are created private, 0600 and 0700, rather than inheriting
whatever the umask happened to be. Your history and your API token live in them.
The menu bar no longer shifts while its menu is open, and a rail taller than the
window scrolls instead of uncovering it.
Updating
Everything you have set up carries over. Your extensions move folders by
themselves and keep working without being approved again.
The one thing to watch is an extension whose own program has not been updated
for 2.0: utt will show it, because its manifest moved, but the program will be
writing its clips and its status into the old folder and nothing will happen.
That needs a new build from whoever wrote it.
utt 1.1.1
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
A correction to how plugins reach the menu bar, and two things about them that
were quietly wrong.
A plugin belongs under utt's icon, not beside it
1.1.0 gave a plugin that asked for it an icon of its own in the menu bar. That
was the wrong shape. The menu bar is yours; a plugin is something you installed
into utt, and two of them meant three icons where there had been one.
A plugin now appears as a submenu inside utt's own menu, labelled with its name
and its symbol. What is in it has not changed — whatever the plugin reports
about itself, its daemon's state and a Restart, its own buttons, and a way
straight to its page. utt stays one mark in the menu bar however many plugins
you install.
It asks before something you cannot take back
A plugin can mark a button as one that should not happen by accident, and utt
asks first. It was only asking on the plugin's page — pressed from the menu bar,
the same button went straight through. The menu bar is where a mis-click is
likelier, not less likely.
A dropped setting no longer disappears in silence
utt refuses anything in a plugin's description it cannot show you honestly, and
drops it rather than guessing. That is still right, but the person who wrote the
plugin had no way to see it happen: they shipped three buttons, one appeared,
and nothing said why. utt now names what it refused in its log, once, so that is
a minute's work to find instead of an afternoon's.
Updating
Nothing you have set up needs redoing. A plugin that asked for the menu bar
needs no change to move into utt's menu — it is the same flag.
utt 1.1.0
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
A release about plugins standing on their own.
A plugin can have its own menu bar icon
Until now a plugin lived inside utt's window. If it wanted to be reachable
without opening that window — the way a background program usually is — it had
to ship a second menu bar app of its own, built and signed and updated
separately, whose entire job was to be an icon.
A plugin can now ask for a menu bar item beside utt's, drawn with its own symbol
and its own colour. There is nothing extra to set up: its menu is built from
what the plugin already tells utt. Whatever it reports about itself is at the
top, its daemon's state and a Restart below that, then its own buttons, and
last a way straight to its page in utt.
utt's own icon has not changed. It is still utt's, and it still flashes a
plugin's colour while it is transcribing that plugin's audio.
Two things that were quietly wrong
A plugin that named a button or a setting in the usual openLog style was
having it thrown away. Keys were being held to the rule for a plugin's
identifier, which has to stay lowercase because it becomes a filename — but a
key never becomes a filename. A plugin could arrive with half its buttons
missing and nothing to say why.
And a plugin that utt knew about and you had installed was listed twice on the
Plugins page, once under each heading. It now appears once, where it belongs.
Updating
Nothing you have set up needs redoing.
utt 1.0.0
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
That sentence has been true since the first build, and it is the reason this one
is 1.0.0 rather than 0.6. Everything the app set out to do, it now does: it
hears you, it types for you, it stays out of the way, and the audio never goes
anywhere. What is left is no longer "the missing part" — it is other things,
which is what a first version is supposed to feel like.
Other apps and devices can send audio to it
There is an API now. Switch it on under Connect → API and something else —
a phone, a script, another Mac — can send a clip and get the text back. The
transcription still happens here, on this machine, with the model you chose.
You decide how far it reaches: this Mac only, your local network, or anywhere.
"This Mac only" means the port does not appear on any network interface at all,
not merely that requests are turned away. Local network means a network this Mac
is actually on, which is not the same as an address that merely looks private —
a VPN is private and elsewhere, and a phone on your own Wi-Fi very often has a
public address.
Every request needs the token utt gives you. There is a reference you can open
in a browser, and a button that copies a brief for a language model, if you are
having one write the client.
Programs can put their own settings in this window
A plugin is another program you install yourself that wants a page in utt's
window instead of a settings screen of its own. It says what it is in a file;
utt draws the page, saves what you choose where the program can read it, and
tells it what it asked for.
Each plugin's page lists exactly what it is being given — whether it receives
your transcripts, whether it holds your API token — so it is visible rather than
something you would have to go looking for. If a plugin runs a background
daemon, utt shows what the system says about it: running, stopped, or never
installed. A crashed daemon can no longer claim to be fine.
Plugins can also hand utt audio directly, without the API being switched on at
all. Nothing goes over the network for that; it is one program passing a file to
another on the same machine. While utt is transcribing a plugin's clip, the menu
bar mark runs in that plugin's own colour, so work arriving from your phone
never looks like something being said at your desk.
Connect → Plugins explains all of it, and has a guide you can paste into a
language model if you want to write one.
It tells your microphones apart
Every input now says what it is: built-in, headphone jack, USB, Bluetooth,
Continuity, or a virtual device like BlackHole. A list of names told you nothing
about which one was the phone on your desk, and the phone is the one that stops
working.
Continuity says which kind, too — USB or Wi-Fi — because they are not
the same situation, and "plug it in" is only useful advice if you know which one
you are looking at. utt remembers, so a microphone you have unplugged still says
what it connected through.
A Continuity microphone can go quiet while still looking perfectly present: the
phone is listed, still selected, sending nothing. Each one now has its own
reconnect button, next to the device it actually reopens.
Words it was never going to get right
An app sending a clip usually knows the words that are coming — the name of the
project, the tool being talked about, its own name. utt did not, so those were
exactly the ones that came back wrong: "cogwheel" as "cockwheel", "TUI" as
"Tu Y".
A caller can now send that vocabulary along with the clip, and utt corrects near
misses against it. It is deliberately careful: a word is only replaced when it
was already nearly right, because a wrong correction reads perfectly well and
says something else. It will not rescue an ordinary word misheard as another
ordinary word — "three" heard as "tree" is not something a word list can reach.
This applies to clips sent by other apps. Dictation at your own keyboard is
unchanged.
A window you can find things in
The settings were four tabs that had become bins; "General" held permissions,
the model, sounds and the Dock icon, and the only way to find anything was to
read every card. There is now a rail down the side and one page per thing, each
with a line at the top saying what is on it.
The transcript list lives in the same rail, so you move between what you said
and how utt behaves the same way you move between any two pages. The old
cogwheel is gone — the window is either open or it is the pill.
Updating
If you are on 0.5.x, utt will offer this to you. Nothing you have set up needs
redoing: your hotkey, your microphones, your text rules and your history all
carry over.
utt 0.5.2
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
A pass over the words utt uses.
It says things you can act on
When a transcription failed, utt used to hand you the error its speech library
raised — a sentence with a number in it and no way forward. It now says what
happened and what to do about it. The same goes for a model that will not load,
and for the menu bar when setup is not finished: it points you at the window
where the buttons are instead of listing what macOS calls its permissions.
Elsewhere the wording just stopped describing the machinery. Setup no longer
says it is "compiling". A microphone you have unplugged is named, not reduced to
an identifier. The Text tab's rule tester is called Try it out, and its two
fields say what you say and what utt types.
Updates check on their own now
utt was only looking for a new version when you asked it to. The daily check now
starts with the app, which is what it said on the tin in 0.5.0.
Updates also carry their approval from Apple with them, so installing one works
the same on a network that cannot reach Apple as on one that can.
utt 0.5.1
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
A fix to the update panel itself: what changed in a release now arrives with the
update, so this page is no longer blank when you are deciding whether to install
it.
Nothing else changed.
utt 0.5.0
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
Until now, getting a newer utt meant noticing that one existed. This release is
the last one you have to go and fetch.
utt keeps itself up to date
utt looks for a new version once a day and tells you when there is one. You see
what changed and you choose: install it, be reminded later, or skip it. Nothing
installs on its own, and nothing is ever swapped in while you are recording.
There is a Check for Updates… button in the menu bar and in Settings →
About if you would rather ask than wait.
Checking sends nothing about you or your machine — not what you dictate, not
what you have installed, not who you are. Every update is signed, and utt
refuses to install one that is not, so nothing tampered with on the way to you
gets unpacked.
Your permissions carry over. Microphone, Accessibility and Input Monitoring stay
granted through an update, so you are not sent back into System Settings after
every release.
The window comes to the front
Clicking utt in the menu bar, or opening it while you were looking at something
else, sometimes put the window behind the app you were in. It now comes to the
front every time.
The walkthrough behaves
The first-run walkthrough appears once, on a genuinely new install, and then
stays out of the way. Settings → General has a Show the walkthrough again
button for when you want it back.
Everything you had set stays set.
utt 0.3.0
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the
machine.
This release is about what happens to the text after the model is done with
it: how it is rewritten, where it is delivered, and what you can do about it
when it comes out wrong.
The Text tab is a tool now, not a page of checkboxes
Replacements are one ordered list of literal rules — "claude code" → "Claude
Code", "new line" → an actual line break — applied top to bottom, so a later
rule can rewrite an earlier one's output. The list can be dragged into the order
you want, each rule can be switched off without deleting it, and leaving the
written side empty deletes the word instead of replacing it.
Above the list is a live bench: a sample of text run through the whole pipeline
as you type, with a dot next to every rule that fires against it. "Why is my
rule not doing anything" is now something you can see rather than something you
have to dictate to find out. A preset seeds the punctuation people expect to be
able to say, and every seeded rule is yours to edit from there.
Look before it lands
A paste into somebody else's document is not undoable in any way utt controls,
so delivery is now a choice. Paste immediately is the default and behaves as
it always has. Review first holds the transcript in a panel instead: ⏎
pastes it, ⎋ throws it away.
The panel itself is new either way. It shows what was transcribed without ever
taking focus, so you can keep typing straight through it, and it offers the one
action worth having at that moment: send a word straight to the replacement list
so the next transcript gets it right. It hides itself after a delay you set, or
stays until you dismiss it.
Pasting can also be switched off entirely, and whether utt leaves the text on
your clipboard is its own switch — honoured on the ⌥⇧V "paste that again" route
too.
Models say what they are doing
The model card lists every model for the selected engine at once, with what each
one speaks, what it weighs, whether it is on this disk and which one is in
memory. Downloads show a real percentage; the CoreML compile that follows shows
the time it has taken, because it reports nothing to be a percentage of.
Permissions, walked through
utt needs Accessibility, Input Monitoring and Microphone, and macOS grants each
one in a different place. First launch now opens a walkthrough that takes them
one at a time, with the app bundle draggable straight into the pane that wants
it.
Fixes
- Switching model while the previous one was still loading no longer reports
"could not load the transcription model" for a model that loaded fine. The
engines load one model at a time; before, two loads could overlap and the
slower one won, leaving the app holding the model you did not pick.
utt 0.2.0
Hold a key, speak, release. The text lands at your cursor. Nothing leaves the machine.
Signed, notarized, and installed by dragging
utt is now signed with a Developer ID certificate and notarized by Apple, and it
ships as a disk image. Open utt.dmg, drag utt.app onto Applications, eject.
No terminal command, no Gatekeeper override, no "utt is damaged" dialog.
Upgrading from 0.1.1 resets your permissions. macOS keys Accessibility,
Input Monitoring and Microphone grants to the certificate a build is signed
with, and this release changes that certificate. You will be asked for all three
again on first launch. This is a one-time cost: the new requirement pins the
team rather than a specific certificate, so renewals from here on will not
disturb your grants.
Pick a model, not just an engine
The settings picker now lists individual models rather than only Parakeet or
Whisper, and shows what each one speaks. An unrecognised model in an old
settings file falls back to the engine's recommendation instead of leaving the
app unable to transcribe.
Also
- A new app icon, drawn from the same dot matrix the app uses for its own mark.
- Double-clicking utt now opens it in front, like any other app, instead of
behind whatever was already on screen. - The recording overlay sits off pure black, and the level mark subdivides its
grid as loudness rises.
Requires macOS 26.0 or later on Apple silicon.
utt 0.1.1
Two fixes to how utt feels in use. Everything in 0.1.0 still holds: hold a hotkey,
speak, release, and the transcript lands at your cursor, transcribed on-device.
The first press after typing now records
The hotkey used to need pressing twice if you had not used it recently — and
pressing it twice quickly enough instead tripped the double-tap lock, so the way
out of the bug was the other bug.
The event tap reported a key-up as still holding the key it had just released,
which made a press and its release identical events. The processor tracks whether
other typing has happened since the hotkey was last idle, and that flag could only
clear on a genuinely empty chord — which, with a key-up that never let go, only
arrived when you lifted the last modifier. So the first hotkey after any typing was
spent clearing the flag, and only the second one recorded.
The recording indicator no longer bands on a light background
The darkening behind the dot grid was four hand-placed gradient stops, and every
stop is a kink in the slope. The eye finds a slope change far sooner than a value
change, so on a white document it read as concentric rings. It is now sampled off a
smootherstep curve, which arrives out of nothing and leaves into nothing.
What survives that is plain 8-bit banding: a ramp this wide holds each of its 255
available levels for tens of points. A layer of alpha noise, composited last so it
lands before the framebuffer rounds, dithers it away. Its amplitude is the new
dither key in overlay.json; 0 turns it off.
Install
Requires macOS 26 or later on Apple silicon.
This build is signed with a self-signed certificate and is not notarized by
Apple, so Gatekeeper will not open it unaided.
-
Download
utt.zipbelow, unzip, dragutt.appinto/Applications. -
Clear the quarantine flag:
xattr -dr com.apple.quarantine /Applications/utt.app
Without this, the first launch fails with "utt is damaged and can't be
opened" or "Apple could not verify utt is free of malware." Neither message
means what it says — both are the same missing-notarization check. -
Grant Microphone, Input Monitoring and Accessibility. macOS asks
for each exactly once per app, ever. Restart utt after granting the last two; a
hotkey listener created before the grant landed stays dead until the app
relaunches. -
First launch downloads Parakeet TDT v3 (~650 MB) into
~/Documents/huggingface/. That is the only network traffic utt makes.
Upgrading from 0.1.0: replace the app and relaunch. The signing certificate has not
changed, so the permission grants carry over.
Full guide, including uninstall: docs/INSTALL.md.
Known limits
Unchanged from 0.1.0: not notarized, no auto-update (Sparkle is wired in but has no
feed URL or signing key), Apple silicon only, macOS 26 floor.