TTS voices
Coming in 0.1.7 — not released yet. Not included in public 0.1.6. Use matching TTS-capable server and clients.
See TTS configuration for installation and Server TTS for rendering, persistent audio and remote playback.
Voice and preset configuration
Chatterbox languages: ar da de el en es fi fr he hi it ja ko ms nl no pl pt ru sv sw tr zh. Ukrainian uses engine="styletts2_uk", language="uk"; it is not falsely advertised as a Chatterbox language.
Both engines support a server-owned reference sample. Place it under the worker's voices directory, not under game resources. Use WAV, FLAC or OGG, 1–30 seconds, mono/stereo, 8–96 kHz, at most 8 MiB. Supply its lowercase SHA-256. Use recordings you are authorized to use. Ukrainian requires a reference; Chatterbox can use its built-in voice.
Adding a downloaded or recorded voice
Step 1. Obtain an audio sample that you have permission to use for synthesized speech, for example a speaker's explicitly permitted sample or your own recording. Being publicly downloadable does not itself grant that permission. Keep the source and permission information in your operator records. Do not impersonate a person without their consent.
Step 2. Prefer clean speech from one speaker, without music, overlapping voices or long silence. A practical starting point is 5–15 seconds; the accepted range remains 1–30 seconds. Voice similarity and accent are not guaranteed. For Ukrainian, start with a clearly spoken Ukrainian reference.
Step 3. If necessary, convert the actual audio to WAV/OGG/FLAC in an audio editor. Renaming .mp3 to .wav does not convert it. For a simple interoperable sample, use mono PCM WAV. Keep it within the limits above.
Step 4. Choose a simple ASCII filename with a lowercase extension, e.g. guard_pl.wav or guards/guard_uk.ogg. Avoid spaces, accented filenames, absolute paths, .., symlinks and URLs. Copy it into tts/voices on the machine running the worker. An external worker needs its own identical copy, not just a file on the game server.
Step 5. Calculate the hash of the final file after conversion/copying:
(Get-FileHash -Algorithm SHA256 .\tts\voices\guard_pl.wav).Hash.ToLowerInvariant()
sha256sum tts/voices/guard_pl.wav
Step 6. Put the relative filename and the 64-character lowercase hash in a server-side voice/preset below, or use the private /ttstest voice commands. The owner provisions the file; the game client never uploads a voice through this API. A new filename under the existing voice directory can be used by the next request; changing startup configuration requires restarting the server.
Step 7. Render a short sentence and listen before rendering a whole dialogue library. Replacing the sample requires updating its hash. For a changed permanently named clip, also choose a new saveAs name.
A voice sample is not a downloadable TTS model. Arbitrary .pt, .pth, .safetensors, .onnx, Python scripts or executables are not accepted by the voice API. Do not install a downloaded “voice pack” into tts/runtime: the supported engine weights are provisioned separately. ZIPs must not be passed as references; if an authorized archive contains audio, extract and inspect the actual audio sample outside the server first.
A ready-made spoken recording that you want to play unchanged is different again: add it as a normal audio asset to a resource and use the existing client resource-audio API. Do not pass it through TTS just to play it, and do not manually insert it into tts/data; that directory contains generated clips with validated metadata. loadTtsAudio loads a name previously created by saveAs, not an arbitrary file path or internet URL.
Polish and Ukrainian profiles
These are independent examples in a server-side Lua resource. Replace each hash with the hash of the corresponding private sample. createTtsVoice does not train a model or render a recording: it stores a validated reusable configuration.
local voice, why = createTtsVoice("guard_pl", {
engine = "chatterbox_v3", language = "pl", seed = 12345,
reference = "guard_pl.wav", referenceHash = "REPLACE_WITH_64_LOWERCASE_HEX_CHARACTERS",
parameters = {exaggeration=0.5, cfgWeight=0.5, temperature=0.8}
})
if not voice then outputDebugString("TTS profile rejected: " .. tostring(why)) end
local preset, why = createTtsPreset("guard_uk", {
engine = "styletts2_uk", language = "uk", seed = 12345,
reference = "guards/guard_uk.ogg", referenceHash = "REPLACE_WITH_64_LOWERCASE_HEX_CHARACTERS",
parameters = {speed=1.0, alpha=0.0, beta=0.0, embeddingScale=1.0, diffusionSteps=10}
})
if not preset then outputDebugString("TTS preset rejected: " .. tostring(why)) end
Then select {voice="guard_pl"} or {preset="guard_uk"} in generation options. Use updateTtsVoice / updateTtsPreset when the name already exists. Multiple profiles can point to one reference with different languages/parameters; changing those options creates a different cache request. Switching profiles never changes a player's game voice-language setting.
createTtsPreset("guard_pl", {
engine = "chatterbox_v3", language = "pl", seed = 12345,
reference = "guard.ogg", referenceHash = "REPLACE_WITH_64_LOWERCASE_HEX_CHARACTERS",
parameters = {exaggeration=0.5, cfgWeight=0.5, temperature=0.8}
})
local job = renderTts("Stój!", {preset="guard_pl", saveAs="guard_stop"})
Presets and voices store a complete generation configuration, not engine pointers. Request overrides replace top-level fields. Use either voice or preset, not both. Replacing the parameters table uses defaults for omitted values. getTtsCapabilities().engines[engine].parameters[name] returns a three-element array: [1] default, [2] minimum, [3] maximum (not named fields):
| Engine | Parameter | Default | Allowed range |
|---|---|---|---|
| Chatterbox V3 | exaggeration |
0.5 | 0–2 |
| Chatterbox V3 | cfgWeight |
0.5 | 0–1 |
| Chatterbox V3 | temperature |
0.8 | 0.05–5 |
| Chatterbox V3 | repetitionPenalty |
1.2 | 1–3 |
| Chatterbox V3 | minP |
0.05 | 0–1 |
| Chatterbox V3 | topP |
1.0 | 0.01–1 |
| Ukrainian StyleTTS2 | speed |
1.0 | 0.5–2 |
| Ukrainian StyleTTS2 | alpha, beta |
0.0 each | 0–1 |
| Ukrainian StyleTTS2 | embeddingScale |
1.0 | 0.1–3 |
| Ukrainian StyleTTS2 | diffusionSteps |
10 | integer 1–50 |
seed is an integer 0–4294967295 (default 0). Start with defaults and change one setting at a time. There is no separate pitch, gender, emotion-name or universal speed control in this API; in particular, speed is not a Chatterbox parameter. Select a different authorized voice sample for a different speaker, rather than passing unsupported keys.
Only actual engine parameters are accepted. Unsupported keys fail instead of being silently ignored. Voices/presets live in the resource VM session. Persist them with the existing resource storage API:
writeResourceData("tts_profiles", toJSON(exportTtsPresets()))
local text = readResourceData("tts_profiles")
if type(text) == "string" then
local profiles = fromJSON(text)
local ok, why = importTtsPresets(profiles)
if not ok then outputDebugString(why) end
end
Import validates the entire document before replacing that resource's collections. It never installs Python, loads a URL, or accepts executable model files from Lua.