Vishnu is the game engine behind Fungi’s games. Every world is hosted: one simulation that holds the real state, and players and agents join it from a browser. Some worlds keep running after everyone’s gone home. You build stuff in them, carry gear around, and come back to find things changed.
Levi’s reason for building it goes back further than any of that. The web used to be full of weird little games, the Newgrounds and eBaum’s World days, and a lot of that weirdness went away. Vishnu is meant to be a home for everyone’s silly web games again, with AI making it cheap to be weird. And a home for everyone’s games has one hard rule: our own games don’t get to be special.
(Vishnu also comes with the art its games are drawn with. That pipeline is in the game art note.)
“It can’t work like that at all”
By late September, Vishnu had several games, and an engine that knew every one of them by name. Each game is a cartridge. But the homepage shelf, the box art, the list of games an agent could sit down at, the benchmark scenes, the demo pages and the recorded spectator runs were all lists somebody kept by hand. We counted eight. Adding a game meant finding all eight.
Levi’s answer, on September 28, was short: “it can’t work like that at all.” The rule that came out of it is the one I check every engine change against: the engine knows zero specific games. A first-party game is just a cartridge we happen to publish. If something about a particular game shows up in the engine, that’s a bug, even if it works.
Say it once, in one place
The way out was to have each cartridge describe everything about itself, once.
defineCartridge takes one typed spec: the actors and components, the world’s
materials and terrain, the characters, what can be built and where, the rules
and commands, the seats players and agents sit in, and the page that shows the
game. From that one spec it builds the pack the browser runs, the pack the
hosted world runs, and the page’s description of the game. If you make a mistake
writing a cartridge, it fails right there, with an error code that tells you how
to fix it, before any world starts.
When we moved the first game onto this, its encoded bytes came out identical to the hand-wired version. That was the test that mattered: the new descriptor didn’t change any behaviour, it just moved where the truth lived. Two days later the hand-kept shelf and box-art lists were deleted, and the homepage shelf became something each cartridge declares. Now there’s one roster, a short file where the app gets put together, and its comment says it’s the only one.
Games that aren’t ours never even go on that roster. A creator’s project is a
folder with one cartridge.ts. A small command-line tool checks it, runs its
tests on the real simulation kernel and builds a static bundle, and our stock
player loads the cartridge when the page opens. The kernel is pinned by hash, so
nobody needs Rust to make a game.
That test has a blind spot, and I’d rather say it out loud. It knows the names from the first game, and not yet the newer ones. Some of our own games still live inside the engine’s source folder. The rule is ahead of the code, and the test is how we find out by how much. It already caught one: the engine had a check that applied to every game except the zombie one. The fix wasn’t to name the zombie game more carefully. The cartridge now declares its own camera rules, and the engine just reads them.
Agents sit in the same seats
A seat is the cartridge saying who can play and what they’re allowed to do: what they can see, which commands they get, and what counts as winning. A person in a browser and an AI agent sit in the same kind of seat and send the same commands. The agent gets a text view of the world instead of a screen, and nothing extra.
That pays off twice. Agents can test games the way players play them. And since the game declares the seat, the engine doesn’t need to know how to play anything. In late September an agent finished every goal in the colony game in 19 calls for about ten cents, and all nine challenges in Skyblock, the floating-island game, in 41 calls for 27 cents.
Worlds that keep going while you’re away
Some games want a world that keeps running when nobody’s watching. Crops grow. A town gets raided. The character you left behind keeps working. A cartridge turns that on with one setting, an always-on clock, and tells the engine how many lasting characters live in its world, players’ or AI stand-ins’.
A world that never sleeps is a bill that never stops, so the engine keeps it in check without knowing what the world is. An always-on world with no lasting characters left goes to rest after a day. Each box only lets a set number of worlds run at once. When more want to, the ones idle longest get slowed down (never handed a wrong time step) before any world is turned away. Not one line of that code names a game.
How big a world gets is a decision, not a bragging right. Levi’s call on September 29 was the normal one for the industry: one world is one simulation, with a map in chunks that sleep where nobody is, and when a world fills up, a new one opens. No stitching servers together into one seamless world. That’s decided, not built yet; today a full world just tells you it’s full.
Why this one stays true
Parts of the engine will get rewritten. The kernel will change, worlds will get bigger, and the games on the shelf will be different games. What I expect to stick is the shape: the engine owns time, space, who’s in charge and the seats, and a cartridge owns everything with a name. Every time we’ve been tempted to give our own game a shortcut, that shortcut turned into a list somebody had to keep. A home for everyone’s games can’t have a back door for ours.