How AI NPCs Actually Work: Behaviour Trees, LLMs and Memory

How AI NPCs decide what to do — the classic decision layers (state machines, behaviour trees, GOAP), where an LLM genuinely fits, and what an NPC can and cannot remember.

Creative MilaOctober 12, 2026
How AI NPCs Actually Work: Behaviour Trees, LLMs and Memory

How AI NPCs Actually Work: Behaviour Trees, LLMs and Memory

An AI NPC is not a chatbot with legs. It is a stack of layers, and only the top one is a language model. If you want to know how AI NPCs work, start at the bottom: perception and navigation, then a decision layer, then animation, and only then — for some characters — an LLM sitting on the dialogue and intent layer. The LLM changes what an NPC says. It does not change where the NPC walks. Confusing those two jobs is the most common architectural mistake I see in browser projects, and it produces characters that talk fluently while walking into walls.

This post breaks the stack into its parts, then covers memory (what survives a session and what does not), cost, and the three failure modes that will eat your weekend: oscillating state machines, runaway plans, and hallucinated intent.

The four layers of an NPC, bottom to top

Layer 1: perception and navigation

Perception is a query against the world, run on a fixed tick — in our runtime, 10 Hz is typical for perception, while navigation resolves at the physics rate. A character needs, at minimum: its own transform, a list of nearby entities with distance and line of sight, and a reachable-position test. Line of sight is a raycast; reachability is a navmesh query.

Navigation is the part people underestimate. A NavMesh bake over a terrain with a 0.4 m cell height and a 0.2 m agent radius gives you walkable polygons. Pathfinding runs A* over those polygons, then a funnel algorithm smooths the corridor into waypoints. The character follows waypoints with steering: seek, arrive, separation from other agents. If you skip separation, three NPCs converge on the same point and jitter.

None of this involves an LLM, and none of it should.

Layer 2: the decision layer — state machines, behaviour trees, GOAP, utility

The decision layer answers one question per tick: what should this character do now?

Finite state machines are the simplest and still the most common. Patrol → Investigate → Chase → Attack → Flee. Each state has an entry action, an update, and exit conditions. FSMs are cheap and readable. They scale badly: twelve states with pairwise transitions is a mess of arrows.

Behaviour trees are the industry default for a reason. Composite nodes (sequence, selector), decorators (inverter, cooldown, RepeatUntilFail), and leaves (conditions, actions). A tree ticks from the root, returns success, failure, or running, and — critically — can be halted mid-subtree. That halt is what makes them safe: you can abort a Chase branch the moment the target dies, without waiting for it to finish.

GOAP (goal-oriented action planning) generates a plan from action preconditions and effects. It is powerful when the world has many interacting resources, and it is where runaway plans come from, discussed below.

Utility systems score candidate actions each tick — attack: 0.7, flee: 0.4, idle: 0.1 — weighted by distance, health, target threat. Utility handles fuzzy tradeoffs that trees express awkwardly.

Decision layer

Best for

Main cost

Classic failure

Finite state machine

3–6 distinct modes

Transition explosion

Oscillation between two states

Behaviour tree

Hierarchical, interruptible tasks

Tree authoring discipline

Stale running nodes

GOAP

Many interacting resources

Planner CPU per goal

Runaway plan length

Utility

Continuous tradeoffs

Weight tuning by hand

Score cliffs / flip-flopping

You can mix them. A behaviour tree whose selector is driven by utility scores is common and fine.

Layer 3: animation

The animation layer takes the decision layer's output and makes it readable. Three techniques, usually combined: a locomotion blend tree (idle / walk / run blended by speed), layered additive clips (aim upper body while legs run), and root motion or capsule-driven displacement. The animation layer does not decide anything — but if it lies, players read the NPC as broken. A character whose attack state fires while the animation is still in idle looks like a bug even though the logic is correct. Expose the animation event back to the decision layer: don't deal damage on state entry, deal it when the swing clip reaches its contact frame.

Layer 4: the language model

This is where the LLM goes, if it goes anywhere. The LLM receives a short context — who the NPC is, recent dialogue, the current world facts the NPC is allowed to know — and returns text, or a structured intent drawn from a small enum: {intent: "warn", target: "player"}. The decision layer decides whether that intent is legal, and if legal, maps it onto the existing tree. The LLM proposes; the tree disposes.

An LLM never sets a transform directly. It never picks a waypoint. It never gets to invent a state your tree doesn't have.

Rule we hold to: the LLM may only return intents from a fixed enum the decision layer already understands. Free-form intents are how you get a character trying to "summon reinforcements" in a world with no reinforcement system.

Decision, navigation, animation, dialogue — four layers, one of them optional

Why the LLM is the smallest and most oversold part

For most shipped NPCs, the LLM is a few hundred milliseconds of latency and a text response on top of a stack that already worked. Its contribution is real but narrow: it makes dialogue non-repetitive and lets a character answer a question the designer didn't pre-author. It does not improve pathfinding, combat, or spatial reasoning. Ask an LLM where to walk and it will confidently describe a route that clips through a wall, because it has no navmesh.

The layers below it are doing the actual work, and they are deterministic, cheap, and debuggable. A behaviour tree with 30 nodes costs microseconds per tick. Rendering one dialogue line through a language model costs orders of magnitude more time and money for a contribution that is entirely verbal. If your NPC feels dead, the problem is almost never that the dialogue isn't generative enough — it's that the perception tick is too slow, the tree has a stale node, or the animation layer isn't selling the state change.

Memory: what can actually persist across a session

Memory is where the abstraction leaks, because people expect continuity the architecture doesn't provide for free. Be explicit about the tiers.

Frame memory (a single tick). Perception results. Discarded immediately.

Working memory (seconds to minutes). The blackboard. In a behaviour tree, a shared key-value store: target, lastKnownPosition, alertLevel. This is what lets Investigate remember where the player was last seen. It clears on state exit unless you deliberately write it to a longer-lived store.

Session memory (until reload). Persisted in the running instance. Dialogue history, relationship flags (hasMetPlayer, owesFavour), faction state. A language model can be given the last N turns; practically, keep N small — a rolling window of the last several exchanges, plus a summarized block of earlier ones. Feeding raw history grows context linearly and degrades output quality.

Cross-session memory (across reloads). Requires durable storage — a database or a saved world state. This is the tier most people assume exists and it usually doesn't. If the world is re-instantiated from scratch, everything in memory is gone unless you wrote it out.

Tier

Lifetime

Typical contents

Survives reload?

Frame

One tick

Perception query results

No

Working

Seconds–minutes

Blackboard: target, last seen

No

Session

Until reload

Dialogue history, relationship flags

No

Cross-session

Persistent

Saved faction/world state

Only if explicitly stored

The failure that bites: assuming the LLM "remembers" the conversation. It doesn't. You resend the context every call. If you stop sending, the character forgets mid-sentence and contradicts itself.

A single reference sprite: what a character is, before it remembers anything

Cost: where the budget goes

Every LLM call has a latency (round-trip, typically hundreds of milliseconds) and a token cost proportional to context length. On a decision tick of 10 Hz, you cannot call an LLM in the loop — you'd need 10 calls per second per NPC. So you don't. You call it only on discrete events: a player speaks to the NPC, or the NPC needs to emit a line. Between those events, the decision layer runs solo. Budget for the LLM as an occasional event, not a continuous process. Batch NPC dialogue so you're not making ten simultaneous calls when a room of characters reacts to one thing. Cache responses for canned interactions so repeated questions don't re-hit the model.

The decision layer, by contrast, is essentially free. A tree tick is arithmetic and comparisons.

The three failure modes

Oscillating state machines

Patrol sees the player at 12 m, transitions to Alert; Alert sees nothing (the raycast is blocked by a rock the player stepped behind), transitions back to Patrol; Patrol sees the player again. The character vibrates between modes, twitching its animation. The fix is hysteresis: require the player to be within 10 m to enter Alert and beyond 15 m to leave it, and require the alert condition to hold for a minimum dwell time (0.3–0.5 s) before transitioning. Never let a state's exit threshold equal its entry threshold.

Runaway plans

In GOAP, if an action's precondition set is inconsistent with its effects — say, GatherWood requires hasAxe but its effects don't grant hasAxe while another action GetAxe should — the planner can loop, expanding nodes without reaching the goal. The symptom is CPU spike and a character standing still "thinking". Cap plan depth and node expansion count, and validate that every action's effects move the world toward at least one goal. Fail loudly rather than looping forever.

Hallucinated intent

The LLM returns an intent outside your enum, or one that is syntactically valid but contextually impossible. Without a validation gate, the NPC attempts an action the tree has no node for, and you get either an exception or silent no-op that reads as the character ignoring the player. Validate every LLM output against the enum and a per-state allowlist before it reaches the tree. On invalid output, fall back to a default line.

Hard-won detail: log every rejected LLM intent with its raw text. When a character "acts weird," the rejection log tells you in seconds whether the tree or the model is at fault.

Putting a described character into a world

Here is the practical payoff. If you are building in the browser, you already have a rendering loop, a physics step, and an input system running every frame. The decision layer rides on top of that loop at almost no additional cost — no new runtime, no server round-trip. The heavy lifting (transforms, collision, animation blending) is being done anyway.

So a character described in plain language can become a placeable component: you write who they are and what they do, and the system gives them a capsule, a navmesh agent, an animation set, and a decision layer configured to one of a few built-in behaviour modes — a patroller that walks waypoints and reacts to sight, a stationary talker that only handles dialogue, a follower that maintains distance from a target, a hostile that pursues within an aggro radius. Each mode is a pre-built tree; the description selects and parameterises it. The LLM, if enabled, sits on top for dialogue only.

This is how AI NPCs work in practice, and it's why the decision layer is the interesting one: it's the part that makes a character feel alive, it's cheap, and in a browser it's largely already running under your feet. The LLM is the last coat of paint, not the frame.

If you want to see the stack assembled — perception, a tree, animation, and an optional dialogue model — Agent Sprite Forge, The Cyclops' Island, and Mini World in the 3D game builder are the places to look. Start with a described character, assign a behaviour mode, and watch the layers tick.


A hero palette board — the art direction a generated cast should obey

Keep going


Build the world it walks in

A character, a tile or a model is a material with nowhere to stand. Every world on the 3D game builder was made from one written description and published as a page you can play — including Mini World and The Cyclops' Island.

Open the 3D Game Builder →