Task: Build a managed TTS pipeline for educational syllables and words

Build a managed TTS pipeline for educational syllables and words

Set up a custom controlled TTS process to pre-generate audio for letters, syllables, and words instead of relying on unpredictable browser SpeechSynthesis.

Context

Related task to browser text-to-speech cmu326hnm0bbpqw0qh5fgsv2f.

Experiments have shown that SpeechSynthesisUtterance pronounces many whole words adequately, but performs poorly and unpredictably with individual letters, short syllables, and artificially cropped parts of words.

Observed examples:

  • Собака (Dog) — normal;
  • Со (So) — sounds roughly like С;
  • Р (R) — closer to Э;
  • Ры (Ry) — normal;
  • Свинь (Svin') — may break down into spelling out letter names;
  • Пель (Pel') — normal;
  • Ковь (Kov'), Солн (Soln) — may be spelled out letter by letter;
  • Ко (Ko) → roughly Ка;
  • Ар (Ar) → roughly А;
  • Ре (Re) → roughly Э;
  • Де (De) → Дэ.

Conclusion: browser TTS cannot be considered a reliable source of pedagogically correct pronunciation for the letter → syllable → word mechanic.


Objective

Build a custom managed TTS pipeline to pre-generate and verify educational audio.

Main idea:

card content
letters / syllables / words
controlled TTS
manual/automated review
ready audio files
app only plays pre-prepared sound

Do not rely on the user's system voice for critical pronunciation at runtime in the educational app.


What needs to be generated

Minimum:

  • individual letters where pedagogically appropriate;
  • educational syllables;
  • individual words for picture cards;
  • short character instructions/lines if necessary.

It is especially important to pre-generate all syllables and words actually used in static educational content.

A universal generator for all possible Russian language combinations is not needed. We only generate the vocabulary actually included in the app.


TTS Requirements

We need an engine/service where pronunciation can be controlled better than in browser SpeechSynthesis.

Desired capabilities:

  • stable voice across generations;
  • ru-RU language control;
  • rate control;
  • pitch control if supported;
  • SSML or similar pronunciation control mechanism;
  • ability to set pauses;
  • ability to influence the reading of individual letters/syllables;
  • export results to WAV/MP3/OGG;
  • reproducible results during batch generation.

If a specific TTS reads a particular syllable poorly, it should be possible to specify a special TTS string separately from the displayed text.

For example:

type PronunciationAsset = {
  text: string
  ttsText?: string
  audioSrc: string
}

where:

{
  text: 'КО',
  ttsText: '<special string/SSML for correct pronunciation>',
  audioSrc: '/audio/syllables/ko.mp3',
}

Content Pipeline

Provide a registry of generated units:

type TtsSourceItem = {
  id: string
  text: string
  kind: 'letter' | 'syllable' | 'word'
  ttsText?: string
  voiceId?: string
  rate?: number
  pitch?: number
}

Example:

const items: TtsSourceItem[] = [
  {
    id: 'word-dog',
    text: 'СОБАКА',
    kind: 'word',
  },
  {
    id: 'syllable-so',
    text: 'СО',
    kind: 'syllable',
    ttsText: '...',
  },
  {
    id: 'letter-r',
    text: 'Р',
    kind: 'letter',
    ttsText: '...',
  },
]

Then a separate script/build-step:

items
→ TTS API
→ audio files
→ manifest

Ready Audio Manifest

After generation, the app should work with ready assets rather than TTS.

For example:

type AudioAsset = {
  id: string
  text: string
  kind: 'letter' | 'syllable' | 'word'
  src: string
}
export const audioAssets: AudioAsset[] = [
  {
    id: 'word-dog',
    text: 'СОБАКА',
    kind: 'word',
    src: '/audio/words/sobaka.mp3',
  },
  {
    id: 'syllable-so',
    text: 'СО',
    kind: 'syllable',
    src: '/audio/syllables/so.mp3',
  },
]

In UI:

const audio = new Audio(asset.src)
audio.play()

Important Architectural Idea

Separate three layers:

Display text
TTS input
Generated audio asset

This will allow showing СО to the child, but sending a different technical string/SSML to the TTS if that is the only way to get the correct pronunciation.

That is, do not assume:

what is written on the screen
=
what needs to be literally passed to TTS

Quality Assurance

Each unit needs a simple review status:

type AudioReviewStatus =
  | 'generated'
  | 'approved'
  | 'rejected'

Minimum workflow:

  1. generate a batch;
  2. listen;
  3. mark bad variants;
  4. adjust ttsText / voice / parameters;
  5. re-generate only rejected ones;
  6. use the asset in the app after approved.

Check especially carefully:

  • individual consonants;
  • two-letter syllables;
  • syllables with ь/ъ (soft/hard signs);
  • cropped word parts;
  • combinations that TTS might mistake for abbreviations.

Character Integration

The voice character system can use the same pipeline.

If characters need to differ in voice/tempo/pitch, for each character you can either:

  • generate a separate set of assets;
  • or use characters only for non-critical lines, and keep educational letters/syllables in a single reference voice.

This needs to be decided separately after estimating the volume of generated assets.


First Practical Stage

Do not choose the final TTS immediately.

First, compare several candidates on a short test set of problematic strings:

Р
СО
РЫ
РЕ
ДЕ
КО
АР
СВИНЬ
ПЕЛЬ
КОВЬ
СОЛН
СОБАКА
СВИНЬЯ

Evaluate for each candidate:

  • pronunciation correctness;
  • stability;
  • presence of SSML/phoneme hints;
  • Russian voice quality;
  • batch generation capability;
  • cost;
  • licensing restrictions;
  • convenience of saving results to audio files.

After that, select the main TTS and build the production pipeline.

Definition of Done

There is a reproducible process that takes a static list of educational letters/syllables/words, generates verified audio files, saves them as application assets, and allows separately overriding the technical TTS string for edge cases.