Documentation
Everything you need to bring living characters into your game - from first install to production deployment.
Getting Started
Go from zero to talking NPCs in five steps. VoxLore models run entirely on the player's machine - no cloud, no latency, no ongoing costs.
Create your world
Define the setting, lore, factions, and rules that all characters share. This becomes the foundation for consistent, world-aware dialogue.
Define your characters
Write character definitions with personality traits, backstory, speech patterns, and knowledge. Use our JSON format or the visual Character Creator.
Generate your model
Run the VoxLore CLI to fine-tune a compact LLM on your world and characters. The output is a single .gguf file that ships with your game.
Install the SDK
Drop the SDK into your engine of choice - Godot, Unity, Unreal, or Phaser. It handles model loading, inference, and response parsing.
Your NPCs are alive
Players talk to characters who respond in-character, with structured output your engine can use to drive animations, expressions, and gameplay.
# Install the VoxLore CLI
npm install -g @voxlore/cli
# Or with pip
pip install voxlore# Generate your world model
voxlore generate --world ./my-world/ --output ./models/my_world.gguf# Install the SDK for your engine
npm install @voxlore/sdk # Phaser / Web
# Unity and Godot: download from voxlore.dev/sdk
# Unreal: available via the MarketplaceWorld Building
A world is the shared context that all your characters live inside. It defines the setting, rules, factions, and lore so that every NPC speaks consistently about the same reality.
Your world directory contains a world.json manifest and individual character files. When you run voxlore generate, the pipeline reads the entire directory and produces a single model fine-tuned on everything inside.
{
"name": "Skybound",
"description": "A world of floating islands connected by wind currents, where sky-pirates and merchant guilds vie for control of the trade routes.",
"era": "Industrial Fantasy",
"rules": [
"Gravity works normally on islands but is weak between them",
"Airships are powered by captured storm energy",
"Magic does not exist - all power comes from technology and weather manipulation"
],
"factions": [
{ "name": "The Free Captains", "role": "Independent sky-pirate crews" },
{ "name": "Meridian Trade Guild", "role": "Controls legitimate commerce" },
{ "name": "The Stormweavers", "role": "Engineers who harvest lightning" }
],
"characters": ["captain_zara.json", "merchant_orin.json", "engineer_val.json"]
}Tip
The rules array is powerful. Use it to prevent characters from referencing things that don't exist in your world. If there's no magic, say so - the model will respect that boundary.
Character Creation
Each character is defined in a JSON file with personality traits, backstory, dialogue examples, and metadata the model uses to stay in character. The more detail you provide, the more consistent and nuanced the character becomes.
The personality object uses the OCEAN model (Big Five) - each trait is a float from 0.0 to 1.0. The model uses these values to modulate tone, word choice, and emotional range.
{
"name": "Captain Zara",
"system_prompt": "You are Captain Zara, a fearless sky-pirate captain who commands the airship Stormchaser. You speak with bold confidence, use nautical metaphors, and have a soft spot for underdogs. You never back down from a challenge and always keep your word.",
"personality": {
"openness": 0.8,
"conscientiousness": 0.4,
"extraversion": 0.9,
"agreeableness": 0.6,
"neuroticism": 0.2
},
"response_examples": [
{
"player": "I need help crossing the Shattered Reach.",
"npc": "The Shattered Reach? Ha! Most captains won't even look at that stretch of sky. Lucky for you, I'm not most captains. Stormchaser's cut through worse."
},
{
"player": "Why should I trust you?",
"npc": "Trust is earned on the wind, friend. I could give you a list of references, but they're all either too drunk or too dead to vouch. My ship's still flying - that speaks for itself."
}
],
"animations": {
"emotions": ["neutral", "happy", "angry", "sad", "surprised", "amused", "determined", "suspicious"],
"faces": ["smirk", "wide grin", "furrowed brow", "raised eyebrow", "narrowed eyes", "soft smile"],
"gestures": ["hand on hip", "arms crossed", "pointing forward", "drawing cutlass", "adjusting hat", "leaning on railing"]
},
"inventory": [
{ "item": "Stormchaser Cutlass", "description": "A well-worn blade with a lightning motif etched into the guard" },
{ "item": "Sky Chart", "description": "Detailed map of wind currents across the Shattered Reach" },
{ "item": "Captain's Compass", "description": "Always points toward the nearest storm" }
],
"locations": [
{ "name": "The Stormchaser", "description": "Zara's airship - her home, her pride, and the fastest vessel in the fleet" },
{ "name": "Port Nimbus", "description": "A floating trade hub where Zara resupplies and picks up jobs" },
{ "name": "The Shattered Reach", "description": "A dangerous stretch of fractured sky islands only the bravest pilots navigate" }
],
"quests": [
{ "id": "storm_run", "title": "The Storm Run", "description": "Help Zara navigate the Shattered Reach to deliver critical supplies" },
{ "id": "old_debt", "title": "An Old Debt", "description": "A rival captain from Zara's past has come to collect" }
],
"knowledge": [
"Knows the wind patterns of every major sky route",
"Has a rivalry with Captain Vex of the Iron Talon",
"Lost her first ship, the Breeze Runner, ten years ago",
"Secretly funds an orphanage in Port Nimbus"
]
}system_promptThe core identity the model always follows. Write it as a direct instruction.
personalityOCEAN traits (0.0-1.0). High extraversion = bold speech; low agreeableness = blunt responses.
response_examplesFew-shot examples that teach the model the character's voice and speech patterns.
animationsLists of valid emotions, facial expressions, and gestures the model can reference.
inventoryItems the character has. The model can reference these naturally in dialogue.
knowledgeFacts the character knows. Keeps responses consistent and prevents hallucination.
World Events
World events let you inject things that happen in your game into every NPC's awareness with a single API call. A bridge collapse, a dragon sighting, a festival starting - push the event once and every character reacts through the lens of their own personality.
Events are filtered by each NPC's OCEAN personality traits. A character with high conscientiousness worries about logistics and safety. A character with low agreeableness sees opportunity in chaos. The same event produces completely different reactions across your cast - no extra scripting required.
# Push a world event - every NPC gets the news
voxlore.push_world_event("The old bridge collapsed during the storm")
# Events are filtered by each NPC's personality
# A warrior worries about supply routes
# A healer worries about injured travelers
# A thief sees an opportunity in the chaosPersonality-Filtered
Events pass through OCEAN trait filters. High neuroticism NPCs panic about a storm while low neuroticism NPCs shrug it off. Zero scripting per-NPC.
Player-NPC and NPC-to-NPC
Events flow into both conversation types. Players hear about events when they talk to NPCs, and NPCs discuss events with each other in the background.
Recency Weighted
Recent events dominate conversation naturally. Yesterday's dragon sighting gets more airtime than last week's market dispute.
Single API Call
Push once, reach every NPC. No need to manually inject events into each character's context - the SDK handles distribution and filtering.
Tip
Combine world events with NPC-to-NPC conversations for maximum immersion. Push an event like "Bandits raided the eastern farmsteads" and listen to NPCs debate what it means while the player walks through town.
Animation Constraints
Every game has a different animation vocabulary. An indie 2D game might have 5 emotion sprites. A AAA title might have 50 blend shape parameters. The animation constraint system lets you define exactly what flags are available per character, and the model will only output flags from your list.
This means no post-processing, no fuzzy matching, no "the model said melancholic_resignation but I only have sad". The output is machine-readable from the start.
Define your animation vocabulary
{
"animations": {
"emotions": ["neutral", "happy", "angry", "sad", "surprised"],
"gestures": ["wave", "point", "shrug", "bow", "crossed_arms"],
"faces": ["smile", "frown", "smirk", "raised_eyebrow", "wide_eyes"],
"body_poses": ["relaxed", "alert", "defensive", "confident"]
}
}Model outputs only your flags
{
"dialogue": "You again? Last time you cost me three gold.",
"emotion": "angry",
"face": "frown",
"body": "defensive",
"gesture": "crossed_arms"
}emotionsValid emotion labels. The model picks one per response. Map these to animation states, audio tone parameters, or mood indicators.
gesturesValid gesture labels. Trigger one-shot animation clips or particle effects. The model selects contextually appropriate gestures.
facesValid facial expression labels. Map to blend shapes, sprite frames, or portrait swaps. Each label maps to one visual state.
body_posesValid body pose labels. Drive your IK system, animation state machine, or character stance. The model picks based on dialogue context.
Why this matters
Without constraints, LLMs generate creative but unparseable descriptions like "a wistful half-smile tinged with regret". With constraints, you get sad - exactly what your animation system needs. Different characters can have different animation sets, so a boss with 20 emotions gets richer output than a background NPC with 3.
Game State Awareness
NPCs can reference the player's current game context - inventory items, active quests, location, time of day, and reputation. This creates natural, contextual dialogue where characters notice what the player is carrying, remember past interactions, and react to the environment.
Push game state updates whenever the player's context changes. The SDK injects this data into the prompt so NPCs can reference it naturally without breaking character.
# Set the player's current game context
voxlore.set_game_state("captain_aldric", {
"inventory": ["broken_sword", "health_potion", "old_map"],
"active_quest": "find_missing_merchant",
"location": "market_district",
"time_of_day": "night",
"reputation": 0.7
})
# Now when the player talks to Aldric:
# "I see you carry a broken sword. The smith on
# Irongate Lane could fix that - if he's still alive.
# The market's not safe at night. Watch your back."Inventory
"I see you carry a broken sword. The smith on Irongate Lane could fix that."
Active Quests
"Looking for the missing merchant? Last seen heading east past the old mill."
Location & Time
"The market's not safe at night. Watch your back, traveler."
Reputation
"I've heard good things about you. Perhaps we can do business after all."
Context Pipeline
Under the hood, VoxLore assembles a structured system prompt from five layers. Studios configure layers 1 through 4. The model handles the rest. Zero ML expertise required.
This is the complete architecture of what gets sent to the model for every conversation turn. Understanding this helps you get the most out of each layer.
[CHARACTER] - Who the NPC is
system_prompt Personality, backstory, voice
personality (OCEAN) Trait values that shape tone and word choice
knowledge Facts the NPC knows (and does not know)
response_examples Few-shot examples of the character's voice
[WORLD EVENTS] - What is happening in the world
active_events Filtered by personality relevance
event_recency Recent events weighted higher
[GAME STATE] - The player's current context
inventory Items the player carries
active_quests What the player is working on
location / time Where and when the conversation happens
reputation How the NPC feels about the player
[ANIMATION CONSTRAINTS] - Available animation flags
emotions Valid emotion labels for this character
gestures Valid gesture labels
faces Valid facial expression labels
body_poses Valid body pose labels
[FORMAT] - JSON output specification
Enforced schema dialogue, emotion, face, body, gesture
Constrained decoding Model can only output valid flagsCHARACTER
The NPC's identity - personality traits, backstory, speech patterns, knowledge boundaries. This is the foundation that never changes during a conversation.
WORLD EVENTS
Active events filtered by personality relevance and recency. A warrior hears about the battle before the festival; a bard hears about the festival first.
GAME STATE
The player's current context - inventory, quests, location, time, reputation. Updated dynamically as the player progresses through the game.
ANIMATION CONSTRAINTS
The character's available animation flags. Constrains the model to only output valid emotion, gesture, face, and body labels for this specific character.
FORMAT
The JSON output schema enforced via constrained decoding. The model can only produce valid JSON with the correct fields. You never get malformed output.
No ML expertise required
You configure characters, events, and game state through JSON and simple API calls. The SDK assembles the prompt, manages context windows, and handles constrained decoding. You focus on game design - VoxLore handles the LLM engineering.
Structured Output
Every response from the model is a typed JSON object - not raw text. This means your game engine can directly parse the response and use each field to drive different systems simultaneously.
No regex parsing, no string splitting, no guesswork. The model is constrained to always produce this exact schema.
{
"dialogue": "Ha! Do I look like a coward to you?",
"emotion": "amused",
"face": "wide grin, eyes sparkling with defiance",
"body": "hands on hips, chest out, stance wide",
"gesture": "drawing cutlass slowly from belt"
}dialogueThe spoken text to display in your dialogue UI. This is what the player reads.
emotionAn emotion label from the character's allowed emotions list. Use this to set the animation state or mood indicator.
faceA natural-language description of the character's facial expression. Map this to blend shapes, sprite frames, or your portrait system.
bodyA description of the character's body posture and stance. Drive your IK system or select from a library of body poses.
gestureA specific action or movement the character performs while speaking. Trigger animation clips or particle effects.
Why structured output matters
Traditional NPC dialogue systems give you a string. With structured output, a single model call gives you everything needed to animate the character, set the mood, and trigger gameplay events - all in one inference pass.
Engine Integration
The VoxLore SDK handles model loading, inference, and response parsing. Pick your engine below to see a complete integration example.
# voxlore_npc.gd
extends Node
var voxlore: VoxLoreClient
func _ready():
# Load your world's model (ships with your game)
voxlore = VoxLoreClient.new()
voxlore.load_model("res://models/my_world.gguf")
func chat(npc_name: String, player_message: String):
# Send player input, get structured response
var response = await voxlore.chat(npc_name, player_message)
# Parse the structured JSON output
var dialogue = response["dialogue"]
var emotion = response["emotion"]
var face = response["face"]
var body = response["body"]
var gesture = response["gesture"]
# Drive your game systems
$DialogueUI.show_text(dialogue)
$AnimationTree.set_emotion(emotion)
$FaceController.set_expression(face)
$BodyController.set_pose(body)
$GesturePlayer.play(gesture)How it works
- 1.Load the model - Call
loadModel()once at startup with the path to your .gguf file. The SDK initializes the inference runtime. - 2.Send a chat message - Pass the NPC name and the player's text to
chat(). The SDK looks up the character, builds the prompt, and runs inference. - 3.Use the response - The return value is a parsed object with
dialogue,emotion,face,body, andgesture. Route each to the appropriate game system.
API Reference
The SDK exposes a minimal, consistent API across all engines. These are the core methods you will use.
loadModel(path: string)returns voidInitialize the inference runtime with a .gguf model file. Call once at startup. The path should be relative to your project's asset directory.
chat(npcName: string, message: string)returns VoxLoreResponseSend a player message to a named NPC and receive a structured response. The SDK handles prompt construction, context management, and JSON parsing internally.
setContext(npcName: string, context: object)returns voidInject runtime context into an NPC's conversation - quest state, inventory changes, world events. The model uses this to adapt its responses.
pushWorldEvent(event: string)returns voidBroadcast a world event to all NPCs. The event is filtered through each NPC's OCEAN personality traits, so each character reacts differently. Events persist until cleared.
setGameState(npcName: string, state: GameState)returns voidUpdate the player's game context for a specific NPC. Includes inventory, active quests, location, time of day, and reputation. NPCs reference this naturally in dialogue.
startNPCConversation(npc1: string, npc2: string)returns voidStart an autonomous conversation between two NPCs. They exchange dialogue on a timer using the LLM, each maintaining their distinct voice. Player requests always take inference priority.
stopNPCConversation(npc1: string, npc2: string)returns voidStop an ongoing NPC-to-NPC conversation. Use when the player walks away or a scene transition occurs.
setNPCChatInterval(npc1: string, npc2: string, seconds: number)returns voidSet the interval between autonomous NPC-to-NPC dialogue exchanges. Lower values create more frequent chatter. Default is 30 seconds.
resetConversation(npcName: string)returns voidClear the conversation history for a specific NPC. Use this when the player starts a new scene or the game state changes significantly.
getConversationHistory(npcName: string)returns Message[]Retrieve the full conversation history for an NPC. Useful for saving game state or displaying a conversation log to the player.
VoxLoreResponse schema
interface VoxLoreResponse {
dialogue: string; // The spoken text
emotion: string; // Emotion label (from character's emotion list)
face: string; // Facial expression description
body: string; // Body posture description
gesture: string; // Action or movement description
}