EverRealm
Free-to-play browser game, first-person, open-world RPG in the spirit of The Elder Scrolls: Arena and Daggerfall:
About This Project
Hi all,
EverRealm is a free-to-play, first-person, open-world RPG in the spirit of The Elder Scrolls: Arena and Daggerfall: procedural world, skills that improve through use, guilds, crime, dungeons. In design I'm aiming closer to Morrowind: fewer empty miles, more handmade-feeling places. It runs entirely in the browser with no install.
What's in it
An island province of 50 towns and villages, with seamless chunk streaming, roads, biomes (lowland, snowy highland, sandy scrub), lakes and a coastline you can swim off
Gutter Reach, a capital city of 1,600+ buildings, hundreds of them enterable, with rooftop plank walkways for thieves
13 classes, 8 races, 8 attributes and 24 skills that improve through use, Daggerfall style
Fighters, Mages and Thieves Guilds with their own questlines, rivalry between factions, bounties and guards
A multi-chapter main quest plus procedural quests: kill, fetch, deliver, escort, assassinate (some can be resolved without violence), explore and clear a dungeon
Generated dungeons, goblin camps, ruin mazes, towers, waystations and shrines
Lockpicking and pin-tumbler minigames, pickpocketing, house burglary (sometimes someone is home), climbing and swimming with a breath meter
Spells, soul-trap enchanting, alchemy (potions and weapon poisons), disease and poison
Day and night, weather, seasons, and a calendar with town fairs and sale days
A tavern card game, books you can read, master trainers hidden out in the wild
NPC conversations generated by an AI language model (LLM), with grounding in the game (more on that below)

The tech
Stack: three.js for rendering, cannon-es for physics, simplex-noise for terrain, three.quarks for particles, Vite for the build, Vitest for tests. There's no engine editor. Everything is placed in code. A small Node backend handles the LLM calls so the API key never reaches the client.
Chunk streaming without whole-world passes
The overworld streams terrain, trees, grass and points of interest in chunks around the player. The 50 regular towns use a layout algorithm that needs the whole town at once: a spanning tree of streets with extra loop edges, plus elevation smoothing that looks at neighbouring cells. That's fine for a village. It doesn't work for a city where only a few chunks are in memory at any moment.
So Gutter Reach uses a different layout. Every city block is a pure function of a fixed seed and the block's own integer coordinates. There are no whole-grid passes and no neighbour lookups, so any chunk can be generated on its own, in any order, at any time, and it always comes out the same. Buildings come from a template set and are instanced. The rooftop walkways are generated per block in the same way.
What made the city run acceptably
Most of the work after the first city build went into finding real frame-time problems rather than guessing. A few that might be useful to others:
Unwarmed shaders and textures. A recurring multi-second freeze turned out to be the GPU compiling shaders and uploading textures the first time a material was drawn. Warming them up before the player could see them (character models first) fixed most of the hitching that had been blamed on streaming.
Grass regeneration. A stutter every few seconds while walking through grass came from rewriting a whole patch buffer and re-uploading it to the GPU in a single frame. Spreading that work over several frames fixed it, and so did cutting the number of grass tufts.
Physics only where it matters. Shopkeepers, bankers and guild staff don't get physics bodies until you approach them. NPCs and creatures far away or off-screen are culled from both rendering and simulation.
Auto quality. There's a Low/Medium/High/Ultra preset chosen from a hardware check on first launch, which quietly steps down if the frame rate stays low. Crowd density is tied to the same setting.
Download size
For a browser game, download size is load time. A pass with gltf-transform and sharp (WebP recompression, removing duplicate textures shared across dungeon pieces, converting legacy DDS building textures to JPG) cut the model folder from 311 MB to 176 MB. The loading screen now shows real progress instead of appearing to freeze.

LLM-driven NPCs, kept on a short leash
I didn't want the language model running the game, so answers to factual questions never reach it. Each player message first goes through a chain of small, pure intent handlers: "where am I", "nearest town", "where's the bank", "what's my bounty", quest status, rumours. They answer from actual game state, with compass bearings and distances. Only open-ended conversation goes to the LLM, along with the NPC's persona (role, town, temperament) and relevant context. If the backend isn't reachable, NPCs fall back to written stock lines, so the game never depends on the network to be playable.
It's the same split I've seen work elsewhere: the world and quests are deterministic, and the LLM adds personality.
Assets
Buildings are a mix of purchased FBX packs and hand-placed landmarks. Most newer characters and creatures are AI-generated 3D models (Tripo3D) converted with gltf-transform. Older ones come from Mixamo-rigged FBX models.
Testing
There are about 40 Vitest files (512 tests) covering the systems that can be tested on their own: quest engine, stats and skills, crime, the dungeon/town/POI generators, save games, the card game, the graphics-quality heuristics. It's been worth it on a project this size. Several "it feels off" reports turned out to be real arithmetic bugs. One was race trait bonuses that had never applied to anything, from the day the feature was added.
Where it's going
Current focus is RPG depth over visual polish: more quest branching, more reasons to explore off the road, and making the city feel lived in.
I'd love feedback, especially from anyone who has done large-scale procedural streaming in WebGL, or who has opinions on LLM NPCs in games.
Discussion