# How to Select a TTS Engine in VoiceStudio: A Complete Guide

> Easily select your TTS engine in VoiceStudio via Settings. This guide shows you how to choose from available engines and save your preference for seamless voice operations.

- Repository: [Palash Debnath/VoiceStudio](https://github.com/debpalash/VoiceStudio)
- Tags: how-to-guide
- Published: 2026-09-05

---

**Select a TTS engine in VoiceStudio by opening Settings → Voice, choosing from the dropdown of available engines, and letting the app save your selection to `user_env.tts_engine` for all subsequent voice operations.**

VoiceStudio simplifies text-to-speech engine management through a registry-based discovery system. The Python worker exposes available backends via a REST API, while the React frontend renders them as a user-friendly dropdown. This article explains exactly how engine selection works under the hood, with file paths and code references from the debpalash/VoiceStudio repository.

## Where TTS Engines Are Discovered

VoiceStudio maintains a central registry of TTS backends in [`services/tts_backend.py`](https://github.com/debpalash/VoiceStudio/blob/main/services/tts_backend.py). The `_REGISTRY` dictionary holds all engine classes, keyed by their unique identifiers.

Engine registration happens through a decorator pattern:

```python

# services/tts_backend.py

_REGISTRY = {}

def register_engine(id_: str):
    def decorator(cls):
        _REGISTRY[id_] = cls
        return cls
    return decorator

@register_engine("indextts")
class IndexttsBackend(OmniVoiceBackend):
    @staticmethod
    def is_available() -> Tuple[bool, Optional[str]]:
        # Verify binary, model files, GPU availability

        return (True, None)

```

Each engine implements `is_available()` to report whether it can run on the current worker. This method returns a tuple: `(available: bool, reason: Optional[str])`. The reason string explains why an engine is unavailable—missing binaries, insufficient VRAM, or missing model weights.

## How the Frontend Lists Available Engines

The **VoicePanel** component in [`frontend/src/components/settings/VoicePanel.jsx`](https://github.com/debpalash/VoiceStudio/blob/main/frontend/src/components/settings/VoicePanel.jsx) fetches the engine list when mounted. It calls `GET /api/settings/tts-engines` and transforms the response into a dropdown.

```jsx
// frontend/src/components/settings/VoicePanel.jsx
const [engines, setEngines] = useState([]);
const [engineAvailable, setEngineAvailable] = useState(true);
const [engineReason, setEngineReason] = useState(null);

useEffect(() => {
  apiJson('/api/settings/tts-engines')
    .then(data => {
      setEngineAvailable(data?.engine_available !== false);
      setEngineReason(data?.engine_reason || null);
      setEngines(data?.engines ?? []);
    });
}, []);

```

The API returns an array of engine objects with these fields:

- `id` — the registry key (e.g., `"indextts"`, `"voxcpm2"`)
- `label` — human-readable display name
- `available` — boolean from `is_available()`
- `reason` — explanation when `available` is false

The UI filters to show only available engines by default, with tooltips for unavailable ones.

## Selecting and Persisting Your TTS Engine

When you pick an engine from the dropdown, the frontend POSTs the choice to the backend:

```jsx
// frontend/src/components/settings/VoicePanel.jsx (lines 252-260)
<Select
  value={selectedEngine?.id}
  onChange={e => apiPost('/api/settings/tts-engine', {engine: e.target.value})}
  options={engines.map(e => ({value: e.id, label: e.label}))}
/>

```

The FastAPI route validates the engine ID against `_REGISTRY`, then stores it:

```python
@app.post("/api/settings/tts-engine")
def set_tts_engine(payload: dict, user: User = Depends(get_current_user)):
    engine_id = payload.get("engine")
    if engine_id not in services.tts_backend._REGISTRY:
        raise HTTPException(400, "Unknown TTS engine")
    user.settings["tts_engine"] = engine_id
    return {"ok": True}

```

This writes to the user's environment file (`.voice_studio_env`). The setting takes effect immediately—no restart required.

## How the Worker Uses Your Selected Engine

Every TTS operation calls `get_active_tts_backend()` to resolve the concrete backend instance. This function:

1. Reads `user.settings["tts_engine"]`
2. Looks up the class in `_REGISTRY`
3. Instantiates it with worker-appropriate configuration

The resolution logic is exercised in [`tests/test_worker_executor_residency.py`](https://github.com/debpalash/VoiceStudio/blob/main/tests/test_worker_executor_residency.py) around line 200. The function guarantees that voice-over, dubbing, and audiobook generation tasks all use the same user-selected engine.

## Troubleshooting Engine Availability

If your preferred engine doesn't appear in the dropdown, check these common causes:

- **Missing binaries** — The engine's executable isn't in `PATH` or the expected directory
- **Model weights missing** — Required checkpoint files aren't downloaded
- **GPU requirements** — The engine needs CUDA but the worker has no compatible GPU

The UI surfaces this information through `engine_reason`. In [`VoicePanel.jsx`](https://github.com/debpalash/VoiceStudio/blob/main/VoicePanel.jsx), unavailable engines trigger a banner:

```jsx
{engineAvailable ? (
  <Select /* ... */ />
) : (
  <span>{engineReason || t('voicePanel.engine_unavailable')}</span>
)}

```

## Adding a Custom TTS Engine

To register a new engine:

1. Create a class extending `OmniVoiceBackend` (or the appropriate base)
2. Implement `is_available()` with your detection logic
3. Apply the `@register_engine("your_id")` decorator
4. Ensure the worker module containing your class is imported at startup

The test file [`tests/test_worker_tts_parity.py`](https://github.com/debpalash/VoiceStudio/blob/main/tests/test_worker_tts_parity.py) demonstrates mock engine registration for validation.

## Summary

- **Engine discovery** happens through `services.tts_backend._REGISTRY`, populated by decorators on backend classes
- **UI population** flows from `GET /api/settings/tts-engines` to the `VoicePanel` dropdown in [`frontend/src/components/settings/VoicePanel.jsx`](https://github.com/debpalash/VoiceStudio/blob/main/frontend/src/components/settings/VoicePanel.jsx)
- **Selection persistence** stores `tts_engine` in user settings via `POST /api/settings/tts-engine`
- **Runtime resolution** uses `get_active_tts_backend()` to instantiate the chosen engine for every TTS task
- **Availability checks** run automatically; unavailable engines show explanatory tooltips instead of blocking the UI

## Frequently Asked Questions

### Why doesn't my installed engine appear in the dropdown?

The engine's `is_available()` method returned `False`. Check `engine_reason` in the UI or run the worker with debug logging to see which requirement failed—missing binary, model file, or GPU capability.

### Do I need to restart VoiceStudio after changing engines?

No. The new `tts_engine` value is read at the start of each TTS task. Active jobs continue with their original engine; new jobs use your selection immediately.

### Can I use different engines for different projects?

Not through the UI. VoiceStudio stores one global `tts_engine` per user environment. For per-project engines, you would need to modify `get_active_tts_backend()` to accept an override parameter or manage environment switching externally.

### Where is the engine selection saved?

In the user's VoiceStudio environment file, typically `.voice_studio_env` in the working directory or a platform-specific config location. The exact path depends on how the `User` model resolves `user_env` in your deployment.