KI-Stimmen bringen dynamische NPC-Dialoge in Games
Klassische Voice-Aufnahmen sind teuer und unflexibel, weshalb NPCs oft immer dieselben Sätze wiederholen. KI-generierte Stimmen sollen dynamische Dialoge ermöglichen und die Immersion stärken.

If you’ve ever played a game where the same three lines repeat for every side‑quest, you know how immersion can crack. Traditional voice‑over pipelines are expensive and inflexible: you record a line, lock it in, and any change means a new studio session.
Enter modern text‑to‑speech (TTS) and voice‑cloning. With a high‑quality neural model you can generate any line on the fly, match a character’s personality, and even let players shape how an NPC sounds. The result? Dynamic dialogue that reacts to player choices, procedural quests, and emergent storytelling—all without a massive voice‑over budget.
In this article I’ll walk through the practical steps to plug an AI voice service into a game, using ElevenLabs as the backbone. By the end you’ll have a working prototype that can synthesize NPC speech in real time, cache results, and blend seamlessly with Unity or Unreal.
When it comes to game‑ready voice, you need:
ElevenLabs checks all those boxes. Their API offers ultra‑realistic voices, supports custom voice cloning, and provides a simple REST endpoint that can be called from any language. The service also returns audio in WAV or MP3, which is easy to feed into game audio pipelines.
👉 Want to try it yourself? Sign up through this link: https://try.elevenlabs.io/kr07zfuqn1bp
Before you can generate lines, you need a voice model that belongs to your character. ElevenLabs lets you upload a short recording (≈30 seconds) and creates a clone that sounds like the speaker.
If you prefer a fully programmatic approach, you can also upload via the API (see the curl snippet later).
All you need to send is a JSON payload with the text and the voice_id. Here’s a minimal curl example:
curl -X POST "https://api.elevenlabs.io/v1/text-to-speech/<VOICE_ID>" \ -H "xi-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Halt! Who goes there?", "model_id": "eleven_monolingual_v1", "voice_settings": { "stability": 0.75, "similarity_boost": 0.85 } }' \ --output guard_halt.wav Enter fullscreen mode Exit fullscreen mode Replace <VOICE_ID> with the ID you got when you created the clone. The stability and similarity_boost parameters let you fine‑tune how consistent the voice sounds across different sentences.
Most game devs prefer a language‑native wrapper. Below is a tiny helper that sends the request and returns a bytes object ready for Unity’s AudioClip or Unreal’s USoundWave.
import requests ELEVEN_API_KEY = " YOUR_API_KEY " BASE_URL = " https://api.elevenlabs.io/v1 " def synthesize ( text: str, voice_id: str ) -> bytes: url = f " { BASE_URL } /text-to-speech/ { voice_id } " headers = { " xi-api-key ": ELEVEN_API_KEY, " Content-Type ": " application/json " } payload = { " text ": text, " model_id ": " eleven_monolingual_v1 ", " voice_settings ": { " stability ": 0.75, " similarity_boost ": 0.85 } } resp = requests. post ( url, json = payload, headers = headers ) resp. raise_for_status () return resp. content # raw wav bytes # Example usage audio_bytes = synthesize ( " Welcome to the citadel, traveler. ", " YOUR_VOICE_ID " ) with open ( " welcome.wav ", " wb " ) as f: f. write ( audio_bytes ) Enter fullscreen mode Exit fullscreen mode You can now load welcome.wav at runtime, or keep the bytes in memory for instant playback.
Even with a fast API, you don’t want to request the same line dozens of times per minute. A simple in‑memory cache works wonders:
import hashlib from functools import lru_cache @lru_cache ( maxsize = 512 ) def get_cached_audio ( text: str, voice_id: str ) -> bytes: # Hash the request to make the cache key deterministic key = hashlib. sha256 ( f " { voice_id }: { text } ". encode ()). hexdigest () # In a real project you might store the result on disk using `key` return synthesize ( text, voice_id ) Enter fullscreen mode Exit fullscreen mode In Unity you could serialize the cache to Application.persistentDataPath and load the file if it already exists. This reduces latency to near‑zero for repeated dialogue (e.g., a shopkeeper’s greeting).
using UnityEngine; using System.Collections; using System.Net.Http; using System.Text; using System.Threading.Tasks; public class NPCVoice: MonoBehaviour { private static readonly HttpClient client = new HttpClient (); private const string ApiKey = "YOUR_API_KEY"; private const string VoiceId = "YOUR_VOICE_ID"; public async Task PlayLine ( string text ) { var payload = new { text, model_id = "eleven_monolingual_v1", voice_settings = new { stability = 0.75, similarity_boost = 0.85 } }; var json = JsonUtility. ToJson ( payload ); var request = new HttpRequestMessage ( HttpMethod. Post, $"https://api.elevenlabs.io/v1/text-to-speech/ { VoiceId } " ); request.
Headers. Add ( "xi-api-key", ApiKey ); request. Content = new StringContent ( json, Encoding.
UTF8, "application/json" ); var response = await client. SendAsync ( request ); response. EnsureSuccessStatusCode (); var wavData = await response.
Content. ReadAsByteArrayAsync (); // Convert WAV bytes to AudioClip var clip = WavUtility. ToAudioClip ( wavData, 0, "NPCClip" ); var source = GetComponent < AudioSource >(); source. clip = clip; source.
Play (); } } Enter fullscreen mode Exit fullscreen mode Tip: WavUtility is an open‑source helper that parses raw WAV data into an AudioClip. You can find it on GitHub or roll your own parser.
void UElevenLabsBPLibrary:: SynthesizeSpeech ( const FString & Text, const FString & VoiceId, const FString & ApiKey, USoundWaveProcedural * OutWave ) { TSharedRef < IHttpRequest, ESPMode:: ThreadSafe > Request = FHttpModule:: Get (). CreateRequest (); Request -> SetURL ( FString:: Printf ( TEXT ( "https://api.elevenlabs.io/v1/text-to-speech/%s" ), * VoiceId )); Request -> SetHeader ( TEXT ( "xi-api-key" ), ApiKey ); Request -> SetHeader ( TEXT ( "Content-Type" ), TEXT ( "application/json" )); TSharedPtr < FJsonObject > Json = MakeShareable ( new FJsonObject ); Json -> SetStringField ( TEXT ( "text" ), Text ); Json -> SetStringField ( TEXT ( "model_id" ), TEXT ( "eleven_monolingual_v1" )); // optional voice settings TSharedPtr < FJsonObject > Settings = MakeShareable ( new FJsonObject ); Settings -> SetNumberField ( TEXT ( "stability" ), 0.75 ); Settings -> SetNumberField ( TEXT ( "similarity_boost" ), 0.85 ); Json -> SetObjectField ( TEXT ( "voice_settings" ), Settings ); FString Body; TSharedRef < TJsonWriter <>> Writer = TJsonWriterFactory <>:: Create ( & Body ); FJsonSerializer:: Serialize ( Json. ToSharedRef (), Writer ); Request -> SetContentAsString ( Body ); Request -> OnProcessRequestComplete ().
BindLambda ([ OutWave ]( FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bSuccess ) { if ( bSuccess && Resp -> GetResponseCode () == 200 ) { const TArray < uint8 >& AudioData = Resp -> GetContent (); OutWave -> QueueAudio ( AudioData. GetData (), AudioData. Num ()); } }); Request -> ProcessRequest (); } Enter fullscreen mode Exit fullscreen mode Now you can call SynthesizeSpeech from any Blueprint node, passing the NPC’s line and playing the resulting sound.
ElevenLabs supports voice settings that influence prosody. Play with these two knobs:
For an angry guard, you might lower stability to 0.4 and raise the volume in your game’s audio mix. For a calm merchant, keep stability at 0.9 and add a slight echo effect in the engine.
You can also switch models (e.g., eleven_multilingual_v2 ) if you need multilingual NPCs.
Imagine a rogue‑like where every dungeon has its own “arch‑wizard” who comments on the player’s actions. With a voice clone per wizard, you can generate unique lines like:
line = f " Ah, { player_name }, you have entered my domain for the { run_number } th time. " audio = get_cached_audio ( line, wizard_voice_id ) play_in_game ( audio ) Enter fullscreen mode Exit fullscreen mode Because the text is assembled at runtime, the NPC never repeats the same phrase verbatim, keeping the experience fresh even after dozens of playthroughs.
The barrier to high‑quality, on‑the‑fly speech is lower than ever. Grab your free trial, upload a short sample, and start experimenting with dynamic dialogue today: https://try.elevenlabs.io/kr07zfuqn1bp
Happy coding, and may your games be as talkative as the players!