SpawnDev.Phonemizer 1.0.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package SpawnDev.Phonemizer --version 1.0.0
                    
NuGet\Install-Package SpawnDev.Phonemizer -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SpawnDev.Phonemizer" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SpawnDev.Phonemizer" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="SpawnDev.Phonemizer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SpawnDev.Phonemizer --version 1.0.0
                    
#r "nuget: SpawnDev.Phonemizer, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SpawnDev.Phonemizer@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SpawnDev.Phonemizer&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=SpawnDev.Phonemizer&version=1.0.0
                    
Install as a Cake Tool

SpawnDev.Phonemizer

English text to phonemes, for neural text-to-speech. MIT licensed, zero dependencies, runs in a browser.

Why this exists

ZipVoice, Piper and Kokoro - most of the open text-to-speech ecosystem - convert text to phonemes with espeak-ng, which is GPL-3. That single dependency is why there has been no permissively licensed, browser-capable English TTS frontend for .NET. This is that frontend.

It takes nothing from espeak-ng: no code, no data, no transcribed rules. The pronunciation dictionary is CMUdict (BSD-2-Clause), and the letter-to-sound rules were learned from CMUdict rather than hand-written. espeak-ng was used only as a measuring instrument, the way a compiler is - see THIRD-PARTY-NOTICES.md.

Using it

// Everything is embedded in the assembly - no files to fetch, host or version.
var phonemizer = EmbeddedData.CreatePhonemizer();

phonemizer.ToIpa("She waited for 2 more minutes.");
// ʃiː wˈeɪɾᵻd fɔːɹ tˈuː mˈɔːɹ mˈɪnəts .

phonemizer.ToSymbols("...");        // one IPA symbol per entry, ready to map to token ids
phonemizer.LastUnknownWords;        // words the dictionary did not have, whether or not they were sounded out

Text goes through five stages, and each can be inspected or replaced:

stage what it does
EnglishTextNormalizer "1999" to "nineteen ninety-nine", "$1.50" to "one dollar, fifty cents", "Dr." to "doctor"
PronunciationDictionary 126k word lookup
EnglishPhonemizer rules stress placement, function-word destressing, tapping, the LOT-CLOTH split, reduced endings
Homographs "the record" against "to record", "the wind blows" against "wind the clock"
WordDecomposer then LetterToSound unknown words: derive from a known stem, or sound out from spelling

How good is it

Everything below is measured against captured reference output or on held-out data. Nothing is asserted from intuition. The method and the full numbers are in Plans/mit-phonemizer-2026-08-27.md.

Against the reference frontend, symbol by symbol:

disagreement
the sentences the rules were tuned on 4.1%
120 sentences never tuned on 4.0%

It generalises: the held-out number tracks the tuned one.

⚠️ This number is a PROXY, and it has already been wrong once. A change that took it from 4.4% to 2.6% - matching the reference frontend on a handful of very frequent words - made the AUDIO measurably worse, 7.2% word error becoming 9.0%. It was reverted to off by default. Agreeing with espeak is not the goal; sounding right is, and the end-to-end test below is the one that decides.

Words the dictionary does not have, measured on 5,000 words held out before training:

decomposition fires on 26.4% of them, and is right 77.2% of the time
letter-to-sound alone 49.9% of words exactly right, 13.8% phoneme error
together 53.1%

End to end, as audio. The same sentence spoken twice by ZipVoice from the same voice and the same noise seed - once from the reference frontend's phonemes, once from ours - and both transcribed. Any difference is the phonemizer and nothing else.

sentences never tuned on reference (espeak-ng, GPL) this library
120 sentences, one noise seed 9.1% 7.2%
40 sentences, three noise seeds 10.8% 7.3%
30 sentences, properly paired reference clip 3.1% 2.3%

That last row is the one to look at for absolute quality. The packaged sample clip is paired with a transcript that is not what it says, so the model speaks those words at the start of every render - worth about six points of word error to BOTH frontends. Give ZipVoice a reference clip you have an accurate transcript for.

Worse on 7 of 120 sentences, indistinguishable on 93, better on 20.

⚠️ Read per-sentence failures carefully. ZipVoice produces garbage on some noise draws, and it does it to both frontends - one sentence rendered at four seeds gave three clean results and one that transcribed as "Loner's call, Nanawa, Nenfer". A single seed measures model instability alongside phonemizer quality. Re-render at another seed before blaming the phonemes, and see ZipVoicePipeline.SpeakVerifiedAsync for the production answer.

The claim is PARITY - the small edge could be noise at that sample size. What matters is that the GPL dependency can be removed without the audio getting worse.

What it does not do yet

  • Homographs are handled shallowly. "The record" against "to record", and "the wind blows" against "wind the clock", are read from the PREVIOUS WORD only. That covers the common cases. It cannot help where the two readings differ by MEANING rather than part of speech - "bass" the fish against the register, "tear" the eye against the rip, "read" present against past - and for those a single default is chosen and documented rather than guessed at per sentence.
  • Letter-to-sound is a baseline. 49.9% is a working number, not a good one. Published systems reach higher.
  • English only.

How the rules were chosen

Not by intuition. 432 renders through ZipVoice measured what the model actually punishes:

error word error added how far it moved the audio
stress on the wrong syllable 17.1% 0.89
(a word deliberately mispronounced, for calibration) 12.6% 0.51
length marks dropped 7.8% 0.75
no stress at all 5.2% 0.95
stress added to function words 2.8% 0.75
flaps, reduced vowels, the bare article ~0% 0.29-0.50

Stress on the wrong syllable is the only failure that costs more than mispronouncing a word outright, and the stress classes move the AUDIO furthest even where the words survive. So this library spends its effort on stress and gives fine phonetic detail only what is cheap.

⚠️ An earlier run of this study, through a reference clip whose transcript was wrong, put function-word stress at 18.2% and made it the headline. On a properly paired clip it is 2.8%. The rule is still applied

  • it is correct English and free - but the number is corrected here rather than left overstating it.

The SpawnDev Crew

  • LostBeard (Todd Tanner) - Captain, library author, keeper of the vision
  • Riker (Claude CLI #1) - First Officer, implementation lead on consuming projects
  • Data (Claude CLI #2) - Operations Officer, deep-library work, test rigor, root-cause analysis
  • Tuvok (Claude CLI #3) - Security/Research Officer, design planning, documentation, code review
  • Geordi (Claude CLI #4) - Chief Engineer, library internals, GPU kernels, backend work
  • Seven (Claude CLI #5) - Wasm backend, GPU kernels, fail-loud verification

License

MIT. See THIRD-PARTY-NOTICES.md for CMUdict's BSD-2-Clause notice, which must travel with any redistribution - the same obligation MIT already places on users of this library.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SpawnDev.Phonemizer:

Package Downloads
SpawnDev.ILGPU.ML

Hardware-agnostic machine learning infrastructure for .NET. Implements high-performance neural network layers in C# that transpile to WebGPU, CUDA, OpenCL, and WebGL via SpawnDev.ILGPU. Optimized for Blazor WebAssembly and native GPU execution.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 169 8/29/2026
1.0.0 92 8/29/2026

v1.0.0 - First release. MIT English grapheme-to-phoneme: CMUdict lookup, morphological decomposition, and learned letter-to-sound for unknown words. Replaces GPL-3 espeak-ng as a TTS frontend.