Beyond Scripted: Giving NPCs Real Personality
Exploring how large language models can create NPCs that feel genuinely alive, with their own goals, fears, and quirks.
"I used to be an adventurer like you, then I took an arrow in the knee."
We've all heard it. A thousand times. In every city. From every guard. It became a meme not because it was funny, but because it revealed how painfully robotic our NPCs have been.
But what if that guard had a name? What if he remembered you? What if, three quests later, you found him drunk in a tavern, crying about his lost dreams of adventure?
The Personality Revolution
Large language models have given us something unprecedented: the ability to create NPCs with actual personality. Not just dialogue trees with branches, but characters with:
- Memory: They remember what you did, what you said, and how you treated them
- Goals: They want things beyond giving you quests
- Relationships: They form opinions about you and each other
- Growth: They change based on their experiences
The Old Way vs. The New Way
Traditional NPC Design
const guard = {
dialogue: [
"Welcome to Whiterun",
"I used to be an adventurer like you...",
"No lollygagging",
"Wait, I know you..."
],
getResponse: function() {
return this.dialogue[Math.floor(Math.random() * this.dialogue.length)];
}
};
Personality-Driven NPCs
class Guard {
constructor(name) {
this.name = name;
this.memories = new MemorySystem();
this.personality = {
bravery: Math.random(),
humor: Math.random(),
loyalty: Math.random(),
cynicism: Math.random()
};
this.backstory = generateBackstory(this.personality);
this.currentMood = 'neutral';
this.relationships = new Map();
}
interact(player, context) {
const memory = this.memories.recall(player);
const relationship = this.relationships.get(player.id) || 0;
const mood = this.evaluateMood(context);
return generateResponse({
personality: this.personality,
memory: memory,
relationship: relationship,
mood: mood,
context: context
});
}
}
The Three Pillars of NPC Personality
1. Consistent Inconsistency
Real people aren't perfectly consistent. They have bad days, change their minds, and act out of character. Your NPCs should too:
- A normally cheerful merchant who's grumpy because their shipment is late
- A brave knight who's terrified of spiders
- A wise sage who gives terrible advice about relationships
2. Personal Stakes
Every NPC should want something that has nothing to do with the player:
- The blacksmith saving money to send his daughter to wizard school
- The innkeeper trying to win back their ex
- The guard captain dealing with a corruption scandal
These desires should influence how they interact with the player. Maybe the blacksmith offers better prices if you help with their daughter's tuition.
3. Dynamic Relationships
NPCs shouldn't exist in isolation. They should have opinions about each other:
// NPCs gossiping about the player
npc1.tell(npc2, "The hero helped me yesterday!");
npc2.adjustOpinion(player, +10);
npc2.addMemory("Heard good things about the hero from " + npc1.name);
// Next time you meet npc2:
"Oh, you're the one Martha was talking about! She said you helped her out."
Real Examples That Almost Get It Right
The Forgotten City
Every NPC has a full daily routine, relationships, and secrets. The game is built on understanding these personalities and how they interact.
Disco Elysium
Your internal voices have more personality than most games' main characters. They bicker, disagree, and develop over time.
Hades
Characters remember everything. Hundreds of contextual dialogue lines create the illusion of real relationships evolving over time.
The LLM Advantage
With large language models, we can now:
Generate Unique Dialogue in Real-Time
const response = await generateNPCDialogue({
npc: "Gruff Blacksmith",
personality: "Sarcastic but kind-hearted",
context: "Player just sold them a rusty sword",
memory: "Player previously bought their best armor",
mood: "Amused"
});
// "Another 'ancient relic' from a tomb, eh? At least you
// bought quality from me last time. Tell you what, since
// you're good for business, I'll give you 10 gold for this
// tetanus dispenser."
Create Emergent Backstories
Let NPCs generate their own histories based on their experiences:
- An NPC who becomes a merchant after you save their caravan
- A former enemy who becomes a philosopher after defeat
- A child NPC who grows up to be a knight inspired by you
Enable Real Conversations
Not dialogue trees, but actual conversations where:
- NPCs can be convinced or deceived
- They form opinions based on what you say
- They remember lies and call you out later
The Technical Challenge
Creating personality-driven NPCs isn't trivial. You need:
Memory Management
class MemorySystem {
constructor(capacity = 100) {
this.memories = [];
this.capacity = capacity;
}
add(memory) {
this.memories.push({
...memory,
timestamp: Date.now(),
importance: this.calculateImportance(memory)
});
this.prune();
}
recall(trigger) {
return this.memories
.filter(m => this.isRelevant(m, trigger))
.sort((a, b) => b.importance - a.importance)
.slice(0, 5);
}
prune() {
if (this.memories.length > this.capacity) {
// Keep important memories, forget mundane ones
this.memories.sort((a, b) => b.importance - a.importance);
this.memories = this.memories.slice(0, this.capacity);
}
}
}
Personality Persistence
NPCs need to maintain their personality across sessions while still being able to grow and change.
Performance Optimization
Generating responses for hundreds of NPCs in real-time requires clever caching and prioritization.
The Uncanny Valley of Personality
There's a danger here. NPCs that are almost human can be more disturbing than obviously robotic ones. The key is to lean into the artificiality:
- Let NPCs be aware they're in a game sometimes
- Give them impossible quirks (a merchant who only speaks in rhymes)
- Make their limitations part of their character
Best Practices for Personality Design
Start with Archetypes, Then Subvert
- The gruff blacksmith who writes poetry
- The wise elder who's terrible at giving directions
- The villain who's genuinely trying to help (badly)
Give Them Failure States
- NPCs who give up on their dreams
- Relationships that deteriorate
- Character growth that goes backward
Make Them Petty
- The merchant who charges you extra because you ignored them
- The guard who "forgets" to open the gate because you were rude
- The quest-giver who sends you to the dangerous route because you rushed them
The Future: AI Dungeon Masters
Imagine NPCs that can:
- Generate their own quests based on their needs
- Form factions and alliances dynamically
- Create emergent storylines without developer input
- Remember players across different games
We're not far from this future. The technology exists. We just need developers brave enough to use it.
Call to Action
Next time you're designing NPCs, ask yourself:
- What does this character want that has nothing to do with the player?
- How would they react if the player never showed up?
- What would make them change their mind about something?
Stop creating quest dispensers. Start creating people.
Even if those people are sometimes annoying, irrational, and wonderfully, beautifully flawed.
Because the best NPCs aren't the ones who always have the perfect quest for you. They're the ones who sometimes tell you to come back tomorrow because they're having a bad day.