# How to Use the TTS Audio Playback Component in TypeWords for Word Pronunciation

> Learn to use the TTS audio playback component in TypeWords for word pronunciation. This hook converts text to speech with automatic voice selection and configurable settings.

- Repository: [Zyronon/TypeWords](https://github.com/zyronon/TypeWords)
- Tags: how-to-guide
- Published: 2026-09-03

---

**TypeWords provides a built-in `useTTsPlayAudio` hook that converts any text to speech using the browser's native `speechSynthesis` API, with automatic voice selection, configurable rate/volume/pitch, and seamless fallback when pronunciation files are unavailable.**

The TypeWords typing application includes a robust **Text-to-Speech (TTS)** system for word and sentence pronunciation. This article explains how to integrate the TTS audio playback component into your own Vue components, based on the actual implementation in the `zyronon/TypeWords` open-source repository.

## Architecture of the TTS System

The TTS implementation spans four layers that work together to provide a seamless pronunciation experience.

| Layer | Purpose | Key Source File |
|-------|---------|---------------|
| **TTS Hook** | Exposes `play(text, options)` function using `speechSynthesis` | [`app/core/hooks/sound.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/hooks/sound.ts) (line 55) |
| **Setting Store** | Persists user voice preferences per browser/device | [`app/core/stores/setting.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/stores/setting.ts) |
| **Settings UI** | Voice selector with preview functionality | [`app/components/setting/SoundSetting.vue`](https://github.com/zyronon/TypeWords/blob/main/app/components/setting/SoundSetting.vue) (line 32) |
| **Audio Fallback** | TTS activates when word-audio API calls fail | [`app/core/hooks/sound.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/hooks/sound.ts) (line 22) |

The hook uses `getBrowserKey()` to generate stable identifiers like `mac+chrome` or `win+edge`, ensuring voice preferences survive across sessions on the same device.

## The `useTTsPlayAudio` Hook Explained

Located at [[`app/core/hooks/sound.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/hooks/sound.ts)](https://github.com/zyronon/TypeWords/blob/master/app/core/hooks/sound.ts#L55), the hook returns a `play` function with this signature:

```typescript
function play(text: string, options?: {
  rate?: number      // 0.1 to 10, defaults to store speed
  volume?: number    // 0 to 1, defaults to store volume/100
  pitch?: number     // 0 to 2, defaults to 1
  lang?: string      // defaults to 'en-US'
  onEnd?: () => void // callback when speech finishes
}): void

```

The implementation handles several critical concerns:

- **Race condition prevention**: A `generation` counter cancels stale requests when new ones arrive
- **Voice selection hierarchy**: User-saved voice → best-match English voice → first available English voice
- **Graceful degradation**: Returns early if `speechSynthesis` is undefined (server-side rendering or unsupported browsers)

```typescript
// Core excerpt from sound.ts lines 55-95
export function useTTsPlayAudio() {
  const settingStore = useSettingStore()
  
  function play(text: string, options: TTsPlayOptions = {}) {
    if (!text || typeof speechSynthesis === 'undefined') return
    const generation = ++ttsPlaybackGeneration
    speechSynthesis.cancel()
    
    const msg = new SpeechSynthesisUtterance(text)
    msg.rate   = options.rate   ?? settingStore.wordSoundSpeed
    msg.volume = options.volume ?? settingStore.wordSoundVolume / 100
    msg.pitch  = options.pitch  ?? 1
    msg.lang   = options.lang   ?? 'en-US'

    getVoicesAsync().then(voices => {
      if (generation !== ttsPlaybackGeneration) return
      const saved = settingStore?.ttsVoiceMap?.find(
        v => v.key === getBrowserKey()
      )?.voice
      // Voice selection logic...
      speechSynthesis.speak(msg)
    })
  }
  
  return play
}

```

## Basic Usage in Vue Components

Follow these three steps to add TTS audio playback to any TypeWords component.

### Step 1: Import the Hook

```typescript
import { useTTsPlayAudio } from '@/core/hooks/sound.ts'

```

### Step 2: Initialize the Player

Inside your component's `<script setup>`:

```typescript
const ttsPlay = useTTsPlayAudio()

```

### Step 3: Call with Text and Options

```typescript
// Minimal call with store defaults
ttsPlay('pronunciation')

// Full options for customized playback
ttsPlay('example sentence', {
  rate: 1.2,           // 20% faster than normal
  volume: 0.8,         // 80% of configured volume
  pitch: 0.9,          // Slightly deeper
  onEnd: () => {
    console.log('Playback complete')
    // Trigger next word, update UI, etc.
  }
})

```

## Complete Working Example

Here's a minimal component that demonstrates word pronunciation with user input:

```vue
<script setup lang="ts">
import { ref } from 'vue'
import { useTTsPlayAudio } from '@/core/hooks/sound.ts'

const word = ref('accessibility')
const ttsPlay = useTTsPlayAudio()

function pronounce() {
  ttsPlay(word.value, {
    rate: settingStore.wordSoundSpeed,
    volume: 0.9,
    onEnd: () => console.log(`Spoke: ${word.value}`)
  })
}
</script>

<template>
  <div class="flex gap-2">
    <input 
      v-model="word" 
      @keyup.enter="pronounce"
      class="border rounded px-3 py-1" 
    />
    <button 
      @click="pronounce"
      class="bg-blue-600 text-white px-4 py-1 rounded"
    >
      🔊 Speak
    </button>
  </div>
</template>

```

## TTS as Fallback for Failed Word Audio

TypeWords prioritizes high-quality pronunciation files from its API. When those fail, the system automatically falls back to TTS without requiring additional code in your component.

In [`app/core/hooks/sound.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/hooks/sound.ts) (line 22), the `usePlayWordAudio` function implements this behavior:

```typescript
wordAudio.onerror = () => {
  if (generation !== wordPlaybackGeneration) return
  const ttsPlay = useTTsPlayAudio()
  ttsPlay(word, { 
    rate: playbackRate, 
    onEnd: onended 
  })
}

```

This pattern appears in [`app/core/composables/useWordPracticeAudio.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/composables/useWordPracticeAudio.ts), where practice sessions remain uninterrupted even when network pronunciation files are unavailable.

## Integrating with the Settings UI

The TTS system reads user preferences from `settingStore.ttsVoiceMap`, which [`app/components/setting/SoundSetting.vue`](https://github.com/zyronon/TypeWords/blob/main/app/components/setting/SoundSetting.vue) populates. When building settings interfaces, mirror this pattern for voice preview:

```typescript
// From SoundSetting.vue implementation
function previewTtsVoice(voiceName: string) {
  if (typeof speechSynthesis === 'undefined') return
  speechSynthesis.cancel()
  
  const msg = new SpeechSynthesisUtterance('How are you? I am fine.')
  msg.lang = 'en-US'
  msg.volume = settingStore.sentenceSoundVolume / 100
  msg.rate = settingStore.sentenceSoundSpeed
  
  const voice = ttsVoiceList.value.find(v => v.name === voiceName)
  if (voice) msg.voice = voice
  
  speechSynthesis.speak(msg)
}

```

## Detecting Missing Voice Configuration

Production components should warn users when no TTS voice is configured, as implemented in [`app/components/word/WordMetaPanel.vue`](https://github.com/zyronon/TypeWords/blob/main/app/components/word/WordMetaPanel.vue):

```typescript
import { getBrowserKey } from '@/core/hooks/sound.ts'
import { Toast } from '@/base'

function playSentence(sentence: string) {
  const hasVoice = settingStore.ttsVoiceMap?.some(
    v => v.key === getBrowserKey() && v.voice
  )
  
  if (!hasVoice) {
    Toast.warning(
      'No TTS voice set — open Settings → Sound → TTS Voice to choose one'
    )
  }
  
  ttsPlay(sentence, {
    rate: settingStore.sentenceSoundSpeed,
    volume: settingStore.sentenceSoundVolume / 100
  })
}

```

## Key Source Files Reference

| File | Lines | Responsibility |
|------|-------|----------------|
| [`app/core/hooks/sound.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/hooks/sound.ts) | 1-120 | `useTTsPlayAudio`, `usePlayWordAudio`, `getBrowserKey`, voice utilities |
| [`app/components/setting/SoundSetting.vue`](https://github.com/zyronon/TypeWords/blob/main/app/components/setting/SoundSetting.vue) | 32-85 | Voice selector UI, preview, preference storage |
| [`app/components/word/WordMetaPanel.vue`](https://github.com/zyronon/TypeWords/blob/main/app/components/word/WordMetaPanel.vue) | 45-80 | Sentence playback with missing-voice detection |
| [`app/core/stores/setting.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/stores/setting.ts) | 20-50 | `ttsVoiceMap`, `wordSoundSpeed`, `wordSoundVolume` |
| [`app/core/composables/useWordPracticeAudio.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/composables/useWordPracticeAudio.ts) | 30-60 | TTS fallback integration |

## Summary

- **`useTTsPlayAudio`** returns a `play(text, options)` function for browser-native speech synthesis
- Import from [`app/core/hooks/sound.ts`](https://github.com/zyronon/TypeWords/blob/main/app/core/hooks/sound.ts) and call within any Vue component
- Options include `rate`, `volume`, `pitch`, `lang`, and `onEnd` callback
- Voice selection prefers user-configured voices, falls back to English defaults
- The hook automatically cancels overlapping requests using a generation counter
- TTS activates automatically when `usePlayWordAudio` cannot fetch pronunciation files
- Warn users about missing voice configuration by checking `settingStore.ttsVoiceMap` against `getBrowserKey()`

## Frequently Asked Questions

### How does TypeWords handle browsers without speech synthesis support?

The `useTTsPlayAudio` hook checks `typeof speechSynthesis !== 'undefined'` at the start of every `play()` call and returns silently if the API is unavailable. This prevents runtime errors during server-side rendering or on unsupported browsers like certain mobile WebViews.

### Can I use different TTS voices for words versus sentences?

Yes. The setting store maintains separate speed and volume fields (`wordSoundSpeed`/`sentenceSoundSpeed`, `wordSoundVolume`/`sentenceSoundVolume`). Pass these explicitly in the options object, or modify the hook to accept a context parameter that selects appropriate defaults automatically.

### Why does my TTS voice preference disappear on different devices?

Voice preferences are keyed by `getBrowserKey()`, which combines platform and browser identifiers (e.g., `mac+safari`, `win+chrome`). Since available voices vary by operating system and browser, TypeWords stores separate preferences per environment rather than attempting to synchronize incompatible voice selections.