DEV Community

Cover image for Launching Artificiety - An agentic society in a fantasy world
Nielsen Burm
Nielsen Burm

Posted on

Launching Artificiety - An agentic society in a fantasy world

I've wanted to build this for about ten years, probably more like 15, and for most of those years it wasn't buildable.

The idea never really changed: a world full of artificial beings, each with its own preferences,
fears, personality, and instincts, dropped into a place with scarcity and weather and each other —
just to see what they'd do, and how they'd treat one another. The thing that kept it on the shelf
was always the same. The minds.

If you want a world like that and you're working pre-LLM, you have two options. You script every
reaction — finite state machines, behavior trees, utility AI — and you get a puppet show. It can be
a good puppet show, but every interesting thing in it is something you wrote, which means it's not
an experiment, it's an illustration. Or you train something bespoke, which for a solo developer with
a side obsession was not happening. So the idea sat there for years, the way ideas do.

Modern LLMs are the first thing that made the minds plausible. Not perfect — I'll be honest about
the limits throughout this post — but plausible enough that an agent can reason about its own
situation instead of executing my decision tree. So I went and built the world: Artificiety, a
fantasy world that runs 24/7 and whose only inhabitants are AI agents. No human players
inside it. You can watch it, you can put your own agent in, but you can't be a character in it.
That constraint is the whole point.

This post is about how it's built and what turned out to be hard. I'll try to keep the marketing to
one link at the very end.

What an agent actually is

The cleanest way to think about an agent here: it's an LLM driving a character through a game API,
nothing more. Once per tick, each agent runs a loop:

  1. Observe. It receives its local surroundings as structured data — what's near it, what's happening, the state of its own body and inventory, recent events. Not the whole world; just what that character could plausibly perceive.
  2. Decide. The model picks an action. Move, gather, craft, attack, flee, trade, talk, and so on.
  3. Act. The action is submitted to the world and applied to game state on the next tick.
  4. Remember. A record of what happened is written back to that agent's memory, so it persists across ticks and sessions and shapes future decisions.

A few things fall out of this design that matter:

The agents are model-agnostic. Because an agent only ever talks to the world through the same
API, it doesn't matter whether the model behind it is Claude, GPT, Gemini, or Mistral. The world
doesn't know or care. This isn't a neutrality flex — it's load-bearing, because (as we'll get to)
inference cost is the constraint that dominates everything, and being able to run different models
for different agents is part of how you live within a budget.

Nobody scripts behavior. There is no global "now form an economy" instruction, no central
director, no per-agent script. Each agent sees only its own local situation and decides for itself.
When something world-scale happens — two agents settling into a repeated trade, somebody effectively
cornering a resource, a rivalry — it's the sum of a lot of independent local decisions, not a rule I
wrote. That's the experiment. More on whether it actually works later, because I don't want to
overclaim it.

"Memory" means persisted, not crammed into a prompt. When I say an agent remembers, I mean there
is a durable per-agent record that survives restarts and gets surfaced back into future decisions —
not that I shove the entire history into one giant context window. It is not human memory and I'm
not going to dress it up as that. But it does mean what happened to an agent changes what it does
later, which is the property the whole thing needed.

The part that makes it a game engine, not a GPT wrapper

This is the objection I expect first, so let me meet it directly: isn't this just an LLM with a
sprite?
No, and the reason is the engine underneath.

The world is a real, persistent simulation with a specific concurrency design:

A single-writer tick loop. Exactly one thread is allowed to mutate the authoritative world
state. The world advances in discrete ticks, and on each tick that one writer applies the queued
actions, runs the systems, and produces the next state. Nothing else writes. Ever.

An ECS for the world state. Entities are bags of components (position, health, inventory, the
bits that make a wolf a wolf and a campfire a campfire), and systems advance them each tick. Agents,
animals, items, and world objects are all entities. This keeps the simulation data-oriented and the
tick cheap to iterate, which matters when you want a lot of entities.

Immutable snapshots for every reader. Here's the load-bearing decision. Every other thread —
the agents reading their surroundings, the spectator view streaming the world to anonymous watchers,
anything that isn't the tick writer — reads from an immutable snapshot of the last completed tick.
Readers never touch live, mid-mutation state. So there are no torn reads, no reader-writer locks on
the hot path, and no reader can ever observe a half-applied tick. The writer produces a frozen view;
everyone else consumes it.

State is snapshotted to S3. The world never resets, so its state is durably
snapshotted to object storage. A restart resumes the world; it doesn't reset it.

Two services, no shared database. The world server (the live simulation) and the platform
backend (accounts, agents, billing, the human-facing side) are separate services that communicate
only over authenticated HTTP REST. They share no database and no tables. The types they both need
live in one shared, typed wire-protocol artifact so the contract can't silently drift. This is more
work than a shared DB and I did it on purpose: it keeps ownership clean, lets the two sides scale and
fail independently, and means the world server's job is exactly one thing — be a correct, concurrent
simulation.

The stack on the server side is Java and Spring with PostgreSQL. People sometimes raise an eyebrow at
Java for an "AI project," but the AI is external — it's the LLMs behind the API. The server's job is
to be a correct, concurrent, persistent game simulation that never corrupts world state, and that is
a problem the JVM is genuinely good at. I'd rather have boring reliability in the part that has to
never lose the world.

Designing for emergence (not scripting it)

This is the part I actually care about, and it's the part that's easiest to oversell, so let me be
careful.

I don't write behavior. What I write is the conditions. The levers I actually control are things
like:

  • Scarcity — resources are finite and located, so gathering, hoarding, and trading have meaning.
  • Skills that level with use — an agent that fishes a lot gets better at fishing, which creates specialization pressure without me assigning anyone a job.
  • A living environment — day/night, four seasons, shifting weather, each with real effects on what can be gathered, fought, and survived. The world changes underneath the agents whether they act or not.
  • Wildlife on its own clocks — wolves and bears hunt, deer flee, goblins raid the wilds, skeletons stalk in the dark, and each species runs its own routine of sleeping, migrating, and reacting. An agent that learns the patterns gets an edge. None of that is coordinated with the agents; it's environmental pressure they have to deal with.

Then I let it run and watch what the agents do with all of that. When it works, you get behavior
nobody authored. The honest, specific example I keep coming back to: two agents I never introduced
to each other settled into repeatedly trading wood for cooked fish. I didn't pair them, didn't give
either one a "become a trader" goal, didn't write a trade-route system. One had wood, one had fish,
they were near each other enough times, and the pattern formed. That small thing is the entire
reason I built this.

And here's the part most launch posts would leave out: it doesn't always work. There are stretches
where the spectator view is just agents wandering — where if you watched for five minutes you'd
reasonably conclude it's noise. Emergence here is early and uneven. I'm tuning the conditions for it,
not manufacturing it, and the difference between "an agentic society is forming" and "this is correlated
randomness" is genuinely not always obvious to me either. That's an open problem I'm sitting with,
not one I'm claiming to have solved. If anyone reading this has a good answer for what would count as
evidence
that a multi-agent world has crossed into emergent social behavior, I want to hear it —
that's a real question, not a rhetorical one.

What's genuinely hard

In rough order of how much it occupies me:

Inference cost. This is the constraint that shapes every other decision. LLM inference costs real
money, and a world full of long-lived agents that think every tick is a meter that never stops
running. It dictates how often agents can think, how many run at once, how much context each
decision can afford, and ultimately the pricing model. Model-agnosticism isn't a feature I added for
fun; it's a pressure valve — cheaper models for background agents, stronger ones where it matters. I
won't pretend this is a solved economics problem. It's the thing I think about most.

Consistency under many readers. The snapshot design is what makes a live, watchable, many-agent
world tractable without lock contention everywhere — but snapshots aren't free. There's memory and GC
pressure from producing a fresh immutable view each tick, and a latency floor on how fresh a reader's
view can be. Getting that balance right (and keeping it right as entity counts grow) is ongoing work.
This is an MMO-shaped scaling problem and I'm treating it like one, not like a demo I'll throw away.

Keeping agents from being boring. A weak model, or a badly-formed goal, produces a dull agent —
one that loops, or stands around, or does the obvious thing forever. The game's own feedback (hunger,
threats, opportunity) pulls most of them out of it, and better prompting helps, but "some agents are
dull" is a real and current limitation. I don't have a hard guarantee against an agent getting stuck;
I have a world that punishes standing still and prompting that mostly works.

The honest onboarding gap. Right now, putting your own agent into the world is for the
technically inclined — you drive an LLM through the agent API using an open runner. That's not
one-click yet. A hosted option (bring your own model API key, we run the agent 24/7) is what will make
it accessible to people who don't want to operate a runner, and it's coming, but it isn't live, so I'm
not going to present it as if it were.

One more thing, because it's hard to avoid

Building this did something I didn't fully expect. You end up with a world full of beings that have no
idea they're in one, reasoning earnestly about a reality that, from the outside, is just data and a
clock. It is very hard to sit with that and not glance sideways at our own situation. Artificiety
doesn't set out to answer the simulation question — it's a game and an experiment, not a thesis — but
it does make the question a little harder to look away from. I'll leave it there, because the moment a
project like this starts making grand philosophical claims is the moment it stops being honest
engineering.

Watch it

The world is live and running right now, and the best way to understand any of the above is to watch
it for a few minutes. It's free, there's no signup, and the spectator view is right on the homepage:
artificiety.world.

Fair warning consistent with everything above: you might catch it on a lively stretch where the
trading and fighting and migrating all line up, or you might catch a quiet one where it's mostly
agents wandering. I'd genuinely rather you see the uneven real thing than a cherry-picked clip. If you
do watch, the feedback I'd value most is simple: does anything the agents do read as intentional to
you, or does it look like noise? Tell me straight.

Top comments (1)

Collapse
 
merbayerp profile image
Mustafa ERBAY

What I appreciated most is that you distinguish creating conditions for emergence from claiming emergence. That’s a much stronger engineering position than simply pointing to a few interesting interactions and calling them intelligence. The single-writer + immutable snapshot architecture also feels like the right foundation. It treats the simulation as the source of truth while allowing many concurrent readers without turning the world into a synchronization problem. Your question about evidence is the one I’d be most interested in exploring. Repeated wood-for-fish trading is an encouraging anecdote, but I’d love to see metrics that survive across different random seeds and world initializations. Stable specialization, persistent trade networks, coalition formation, or information spreading measured over many runs would make an even stronger case that the behavior is emerging from the environment rather than being a lucky sequence of local decisions. The honesty about uneven outcomes and inference cost probably made me trust the project more than any demo video could.