Text-to-Speech Cheat Sheet

The browser-native Web Speech API reads text aloud with no backend and no cost. This page covers the core speech parameters, common SSML tags and the mobile/Safari compatibility pitfalls that make web reading sound natural and stable.

How It Works

Text-to-speech (TTS) synthesizes speakable audio from text. Two implementations: the browser-built Web Speech API (SpeechSynthesis — zero-dependency and instant, but voice quality is limited by the OS/engine), and cloud TTS services (vendor APIs with more natural voices, multi-language support and SSML tuning).

SSML (Speech Synthesis Markup Language) uses XML to describe pauses, intonation and speaker details for more natural reading. Output is usually MP3/WAV/OGG audio; sample rate and bitrate trade size against clarity, and front-end real-time reading often streams chunks to cut latency.

When to Use This Table

This table is for choosing parameters for a TTS call. It groups rate, pitch, volume, sample rate, channels, language and voice, giving each meaning and a tuning tip. Working through it page by page is faster than consulting documentation while tuning.

When not to consult it: you are building an accessibility alternative for spoken content, or dealing with vendor pricing and quotas. This table covers parameter meanings only, not vendor choice, cost, quotas or compliance.

Rule of thumb: fix the purpose before tuning. For audiobooks and long-form reading prioritise naturalness (a better voice, slower rate, more pauses); for navigation announcements prioritise clarity and pace (slightly faster, abbreviations expanded). The two goals pull the parameters in opposite directions.

Mind that the useful range is not linear. A rate of 1.0 sounds fine while 1.2 may be clearly distorted, and changing pitch affects both intonation and voice naturalness. Probe in small steps of about 0.1; large jumps usually produce worse results than incremental ones.

Boundaries and Common Pitfalls

  • Above a rate of 1.2 comprehensibility falls off a cliff

    Most engines peak in naturalness around 1.0–1.1; beyond 1.3 syllables get swallowed, consonants drop and sentence-final intonation breaks. In practice a user asking for 1.5 often stops understanding after ten seconds. When speed really matters, cut content rather than accelerating speech — trim redundant modifiers and split long sentences, which works far better than a rate of 1.5.

  • The effect of pitch and rate on duration is counter-intuitive

    Most people assume raising pitch makes speech sound faster. The opposite usually happens: as pitch rises the formants shift up and the same text tends to take slightly longer, since pitch and duration are perceptually decoupled. Raising rate also raises pitch, producing something shrill rather than simply faster. If the goal is a shorter total duration, change rate alone and do not try to compensate with pitch.

  • Numbers, abbreviations and symbols get misread or skipped

    How 2024-06-01, Dr., etc., & or $1,200 are spoken depends on the engine text normalisation and the language setting. The reliable path is to normalise the text before synthesis — convert numbers to their reading form (1200 to one thousand two hundred), replace symbols with equivalent words (& to and), expand abbreviations, and verify the output case by case wherever accuracy matters such as amounts, phone numbers and dates.

How This Relates to Neighbours

Comparing engine quality and language support is vendor selection and needs hands-on listening plus pricing, which this table does not cover.

To edit audio files (trim, transcode, join), use the in-site audio tools; this page only covers synthesis parameters.

Chaining speech recognition with synthesis is speech system design and needs scenario-specific trade-offs; this table covers TTS parameters only.

If the goal is debugging a specific pronunciation, check the language setting first — a mismatched language causes more mispronunciation than parameter problems do.

Practical Snippet

Key points: (1) normalise the text first — numbers into reading form, symbols into words, abbreviations expanded — which buys far more than any parameter tweak; (2) comprehensibility collapses above a rate of 1.2, so speed up by cutting content rather than accelerating speech; (3) raising pitch slightly increases duration, the opposite of intuition, so change rate alone when shortening; (4) long text must be synthesised in chunks or it tends to be truncated; (5) Web Speech accepts rate up to 10 but naturalness degrades sharply past 1.2.

// ① 文本正规化:数字与符号先转成可读形式,胜过调参数
function normalize(text) {
  return text
    .replace(/[¥$]s?([d,]+)/g, (_, n) => Number(n.replace(/,/g, '')) + ' 元')  // ¥1,200 → 一千二百元
    .replace(/&/g, ' 和 ')
    .replace(/(d{4})-(d{2})-(d{2})/g, '$1 年 $2 月 $3 日')
    .replace(/Dr./g, 'Doctor')
    .replace(/e.g./g, 'for example')
    .replace(/etc./g, 'et cetera')
}
console.log(normalize('2024-06-01 & Dr. Smith ¥1,200 etc.'))

// ② Web Speech API 调参:小步试探,不要一次跳到 1.5
function speak(text, { rate = 1.0, pitch = 1.0, volume = 1.0 } = {}) {
  if (!('speechSynthesis' in window)) throw new Error('不支持 Web Speech')
  const u = new SpeechSynthesisUtterance(normalize(text))
  u.lang = 'zh-CN'
  u.rate = Math.min(Math.max(rate, 0.1), 10)     // 数值合法但 >1.3 自然度差
  u.pitch = Math.min(Math.max(pitch, 0), 2)
  u.volume = Math.min(Math.max(volume, 0), 1)
  speechSynthesis.speak(u)
  return u
}
speak('测试语速', { rate: 1.1 })   // ✅ 先小幅试
speak('测试语速', { rate: 1.5 })   // ⚠️ 往往变成尖锐急促

// ③ 长文要分句合成,否则长文本会被截断或卡住
function speakLong(text, chunk = 200) {
  const s = normalize(text)
  for (let i = 0; i < s.length; i += chunk) {
    speak(s.slice(i, i + chunk), { rate: 1.0 })
  }
}

// ④ 先 stop 再 speak,避免队列堆积
speechSynthesis.cancel()
speak('重来一次')

// ⑤ 服务端 TTS(更可控,但要自己处理 SSML 与采样率)
// POST /tts  { text, voice, rate, pitch, format: 'mp3', sampleRate: 24000 }
// 24kHz 是清晰度与体积的平衡点;16kHz 会明显发闷

Practical Notes

  • Rate and pitch are separate axes, not substitutes for volumeRaising the rate also compresses pauses and sounds rushed, while lowering pitch alone sounds dull. Readability is driven mostly by rate and sentence segmentation; pitch carries emotion. Tune and verify them independently.
  • SSML support varies by engineOn built-in browser voices most SSML tags are ignored, while cloud services honour them. If the page relies on <break> or <prosody>, ship a fallback that reads clean text, or the markup itself gets spoken aloud.
  • Chunk long text, and only at sentence boundariesSubmitting very long input hits engine length limits or produces stalls. Split after full stops and question marks, never inside a number-plus-unit or an English word, because those breaks cause mispronunciation.

Data Sources

  • W3C SSML 1.1 (Speech Synthesis Markup Language)
  • W3C Web Speech API

Codes, names and definitions above come from public specifications; we claim no originality over the specification data itself. The arrangement, Chinese annotations and practical notes on this page are written by us.

Core Speech Parameters

ParameterMeaningTuning Tip
rateSpeech rate; 1 is normal, roughly 0.1–10Chinese reads well at 0.9–1.1; English can go to 1.2
pitchVoice pitch; 1 is default, roughly 0–2A ±0.1 nudge distinguishes speakers; avoid extremes
volumeVolume; 1 is default, 0 is mutedUsually leave at default and defer to system volume
voicePick a specific speaker by language and genderList with speechSynthesis.getVoices() and filter by lang
langDeclares the text language, drives voice choiceFor mixed zh/en, switch lang per sentence for naturalness

Frequently Asked Questions

Does the browser have built-in TTS, or do I need a backend?

Modern browsers ship SpeechSynthesis from the Web Speech API, so you can read text entirely client-side with no backend call and no extra cost. The catch: available voices depend on the OS-installed voices, so timbre varies across devices.

Is SSML fully supported on the web?

Support is inconsistent. Desktop Chrome reasonably supports common tags like <break> and <prosody>, but mobile and Safari are spotty and silently ignore some tags. In production, feature-detect and fall back to plain-text reading where SSML is unsupported.

Why does getVoices() sometimes return empty?

The voice list loads asynchronously and may not be ready right at page open. Listen for the voiceschanged event, or only build the UI after a non-empty list arrives, otherwise you will not be able to pick a voice.

What if long-text reading gets cut off?

Some browsers cap a single utterance by length or duration and truncate the rest. Split by paragraph, chain segments on onend, and insert a <break> at boundaries so pauses feel natural.