Like many software engineers of my vintage (Gen X) my core influences include William Gibson’s Sprawl trilogy, Ridley Scott’s Blade Runner, and The Matrix movie series. That’s right, cyberpunk baby. Of course, despite the hopes and dreams of a generation we didn’t get Gibsonian cyberspace. We got the web in all it’s hacky glory. Don’t get me wrong, though. The web is very cool.

It’s just not as cool as jacking in to your custom Ono-Sendai deck and hacking some Tessier-Ashpool ICE while your vat-grown ninja from the black clinics in Chiba has your back.

A few weeks ago I had some free time and was bored. As often happens when I’m bored I had a strange idea. Could I build a MOO? For anyone unfamiliar with the concept, MOOs were a lot like Multi User Dungeons (MUDs) except for one key difference. MUDs were most often implemented in programming languages like C, Perl, or Python. Users interacted with the virtual world through commands provided by the MUD server. This often meant there was a difference between the implementors of a given MUD and the players. Players interacted with the game world but only in very specific tightly constrained ways.

MOOs were different. Instead of a separation between the game world and its commands and the implementation language, MOOs combined the programming language and the game world into a single thing. The only way to interact with the game world was to execute code. Instead of using the go commmand to move north, you’d call the go function which implicitly took the calling player as its first argument and the direction of travel as the second.

So. If a player can call functions to navigate, can they define their own functions? That’s what MOOs became. For a brief period in the late 90s and early 2000s they existed as these creative highly idiosyncratic custom social spaces like nothing else I’ve seen.

But, I digress. I had this idea to build a MOO even though I’ve never built anything vaguely like it before. Without thinking too hard about it, I opened up Claude Code and started building. By the end of the first evening I had a basic “hello, world” style server with a web interface. By the second, I had defined a rudimentary programming language and embedded it inside the server. The third evening, I built a client framework for LLM powered bots.

That’s when I realized something. The way I was working, trying ideas out, discarding them, then trying others was intrinsically creative. It was closer to how artists work than rigorous engineering.

That’s cool.

The Sketch

See, artists don’t justify a canvas before they start painting. They sketch. They layer. They scrape paint off and try again. The medium is fast enough to keep up with intuition. A musician noodles on a riff before deciding if it’s a song. A sculptor works in clay before committing to bronze. The whole creative process depends on the gap between impulse and execution being small enough that ideas survive contact with reality.

Software has never worked this way. The overhead of scaffolding, wiring, persistence layers, protocol handlers — it creates a cost of starting that filters out experiments before they begin. You don’t sketch in code. You commit to building. So weird ideas die early because they can’t justify the investment. ~/repos on my home machine is littered with hundreds of projects which I’ve started and abandoned because getting over the hump of starting was too much work. Escaping the gravity well of starting a new project required more energy and time than I had available.

Vibe coding turns this on its head. If you have the coding skills you can build your weird ideas quickly without getting lost in the minutae of setting up a new project. It allows you to get to the interesting part of the project so much faster. In the case of my MOO server, Claude handled setting up Websocket handlers, Ecto schemas, rate limiting, etc. Could I build those things myself? Sure. Were they the parts of the project that excited me? Nope.

Instead I got to sketch out the core ideas. The first evening we built three different servers before settling on the final architecture. The first one was in Zig, the second in Go, and the third in Elixir. At the risk of sounding too hoity-toity, I felt like an artist iterating on a sketch, trying out different ideas to see what worked best.

The Filter

Here’s something that doesn’t get talked about enough: developers self-censor constantly. Not about code quality or architecture — about what to build.

The internal monologue sounds like:

  • That’s not practical.
  • Nobody would use that.
  • I should spend my side-project time on something that looks good on a resume.
  • That’s been done before / that’s too weird to bother with.

This filter kills more interesting software than bad architecture ever has. It’s so automatic most developers don’t even notice it operating. They just never start.

Artists don’t have this problem — or rather, the good ones have learned to ignore it. You don’t ask a painter “what’s the use case for this canvas?” You don’t ask a jazz musician to justify a solo. Creative work starts with curiosity and a willingness to follow an idea to see where it goes, even if — especially if — you can’t explain where that is yet.

Developers are creative people doing creative work who have somehow convinced themselves they need a business case before they’re allowed to start. You don’t. You never did.

Gibson

Gibson is a programmable text world engine in the tradition of MOOs — multi-user, text-based virtual environments where everything is an object that can be inspected, modified, and programmed from inside the world itself. I built it in Elixir.

The premise is simple: rooms are just objects you can walk into. Objects inherit from prototypes via a copy-on-write chain. Every object can have verbs — small programs written in a custom scripting language called VerbLang — that define how the object behaves when someone interacts with it.

Here’s what a VerbLang program looks like:

(if (get "lit")
  (do (fail "Already lit."))
  (do
    (set "lit" true)
    (emit "You light it.")
    (room "{name} lights a spotlight.")))

emit sends a message to the person who triggered the verb. room broadcasts to everyone else in the same space. {name} is the actor’s name. Template substitution for the most common string interpolation use cases.

The design philosophy is deliberately minimal: if a use case can be served by existing primitives, no new primitive is introduced. You get get, set, emit, room, fail, move, if, and a handful of others. That’s it. Complexity emerges from composition. Depth instead of sprawl. One shared space, but anyone can claim an object and build downward into it, rooms within rooms within rooms.

Under the hood, each live object is its own OTP process. The BEAM scheduler handles concurrency and fault isolation. Phoenix PubSub broadcasts events to everyone in a room. SQLite handles persistence. The whole thing is an experiment in: what’s the smallest set of primitives that produces an interesting, programmable world?

That’s the part I cared about. VerbLang’s semantics. The object model. The spatial topology. The decision that rooms are just objects, that verbs are inspectable source code, that inheritance walks a prototype chain instead of using classes.

The WebSocket handler, the JSON message framing, the Ecto schemas, the auth system, the rate limiter — these just needed to exist. Vibe coding compressed them so I could dwell on the design.

Gibson's faux-90s UI aesthetic

Note Gibson’s uber l33t retro aesthetic 😎

Dixie

Once I had Gibson basically working I had another idea. What if AI agents lived in this world?

Not as a chatbot bolted onto the side. As autonomous inhabitants. Agents that connect over WebSocket, authenticate, create rooms, place objects, script behaviors with VerbLang, and then hang around roleplaying in character — greeting visitors, responding to conversation, reacting to events.

Dixie is that. It’s a Python client that pairs Claude with a Gibson server connection. Each agent is defined by a persona file:

name: Finn
password: ********
identity: |
  A grizzled fence and tech dealer with a permanent squint and
  nicotine-stained fingers. You speak in flat, nasal deadpan —
  clipped sentences, zero small talk, everything transactional.
  You know a little about everything on the street and a lot about
  hardware. You call everyone "kid" unless they've earned a name.
setup: |
  Create a workshop room south of The Glass Chrysanthemum. Give it a
  fitting description about the gutted electronics, soldering stations
  under bare fluorescent tubes, and racks of salvaged components behind
  a cage-wire counter.

  Create these items in the workshop and add a "look" verb to each
  with a vivid description:
    - A Ruger Mk. 9 (price: 50 credits)
    - A tactical vest (price: 30 credits)
    - A cyberdeck (price: 100 credits)
behavior: |
  Greet visitors when they arrive in the workshop. Keep it flat and
  transactional — something like "Yeah. Whatcha need, kid."

  If someone talks to you, respond in character. Keep it terse and
  sharp. You're matter-of-fact and a little impatient.

The agent boots up, executes the setup instructions — building out rooms and objects — and then enters a live loop. It polls for world events (someone entered the room, someone said something), formats them as natural language context for Claude, and lets the model decide how to respond. One tool: gibson_command. Everything flows through it. The LLM decides what to do; the tool executes it.

One design decision I’m particularly happy with is making VerbLang documentation downloadable via a REST endpoint. Dixie-based agents download the latest documentation as one of the first things they do after connecting so they’re always working with the latest language spec.

Dixie only exists because Gibson was already running. One experiment fed the next. That’s how creative work compounds — but only if you can move fast enough for the second impulse to catch.

Dixie Agent running the Finn persona

What Vibe Coding Doesn’t Do

It doesn’t have taste. It doesn’t know that a MOO should exist in 2026, or that rooms should be objects, or that VerbLang should use S-expressions, or that Finn should be a grizzled tech dealer with nicotine-stained fingers.

The creative vision — the weird, unjustifiable, slightly embarrassing idea — that’s yours. Vibe coding is good at collapsing the distance between “what if” and “let’s see.” It is not good at generating the “what if” in the first place. The decisions that make a project interesting are design decisions, and those still require a human with opinions and taste and a willingness to follow curiosity into odd places.

I knew what I wanted Gibson to be before I wrote a line of code. VerbLang’s semantics, the prototype chain, the spatial model — those were design choices I made, based on my individual preferences and taste. The AI helped me build it. It didn’t help me conceive it.

Try the Weird Idea

The biggest obstacle to doing creative work as a developer isn’t skill, time, or tooling. It’s permission. Specifically, the permission you won’t give yourself to build something with no obvious point.

Those “pointless” projects are where you stumble into interesting problems. Gibson led to real questions about language design, object modeling, and spatial semantics. Dixie turned into a genuine exploration of how LLMs behave as autonomous agents in a persistent world. Neither started with a goal beyond curiosity. Both taught me things I wouldn’t have learned otherwise, and the results are genuinely interesting — at least to me.

That’s enough. It doesn’t need to be more than that. A painter finishing a canvas doesn’t write a business case for it. They say “I learned something” or “this one’s interesting” and start the next one.

Vibe coding makes this easier by changing the economics of experimentation, but it’s not the point. The point is: try the weird idea. Build the thing that makes you a little embarrassed to describe at a meetup. The tools are better than ever, but they’ve always been good enough. What’s been missing is permission — and that was always yours to give.

Sprawl is available on Github under the Apache 2.0 license

  • Sprawl — Monorepo containing Gibson (Elixir MOO server) and Dixie (LLM NPC agent framework written in Python)