Spawn

Make Games with Words

Explore or make your own

spawn / s

what we're building

everything we ship, and the reasons.

pinned

start here

what spawn is

the whole idea in one read — what you make, who Savi is, how building feels.

pinnedread →

faq

frequently asked questions

quick answers — making games with Savi, multiplayer, publishing, earning, fixes.

pinnedread →

the bet

the spawn bet

why games, why now, and why an engine built for AI from day one.

pinnedread →

updates

engine v4.5

Surface Tension

Water you can swim in, and shapes that look like the real thing.

2 daysread →
v4.5.1Ponds run much smoother without changing how they look.1 day

engine v4.4

Solid

Things stack right, worlds load clean, and clicks land where you point.

1 weekread →
v4.4.3Fixed some characters appearing too small.4 daysv4.4.2Build bigger worlds with lots of places — areas no one is in quietly sleep so the game stays smooth, and snap back to life the moment someone walks in. Your spawn area is always ready.4 daysv4.4.1Savi places things on top of anything now — models, primitives, sprites, custom geometry, or text, she knows the size and lands the placement first try.1 week

engine v4.3

Groovy

Savi is your DJ now. God mode, movement, and controls all feel right

1 weekread →
v4.3.1Walls and floors are back in 3D Rooms games. Players land cleanly, walls stop the player, and skeletons stand on the floor instead of falling into it.1 week

engine v4.2

Continuum

Saved games, smoother animation, sharper aim, and a basket of polish fixes you'll feel right away.

2 weeksread →

engine v4.1

Foundations

First big engine update. Faster, smoother, smarter — and a small mountain of fixes.

3 weeksread →

engine v0.1

Genesis

Initial versioned engine release.

3 weeksread →

the full text

Every post and engine release on this page, in full — so one read is the whole picture.

the bet

the spawn bet

my girlfriend has been describing the same game to me for years — a roguelike where you forage for mushrooms, cook soup, and build a town around the soup — and she's had no way to build it. she's not lazy and she's not dumb. she just isn't going to spend four years learning c# to chase one idea. that's the situation the entire medium is in. the great games we have were made by the tiny number of people who happened to combine an idea with the patience to sit in a toolchain for a decade, and almost every other idea died before it got near a screen. stardew valley took one person four years alone in a basement. that's not a romantic story about craft, it's a story about a filter — a filter that screens out everyone whose patience or circumstance or wrist tendons gave out before year four. the games we have are the survivors of it. the games we don't have are everyone else's.

the obvious move at this point in history is to glue a chatbot onto unity and call it the future of game development. several companies have done this. it doesn't work for the same reason strapping an llm to microsoft word didn't replace authors — the bottleneck was never typing speed. unity's editor was built for the survivors of the filter, and any ai you bolt onto it inherits every assumption baked in: that you know what a prefab is, that you'll patiently rebuild your scene graph when the netcode breaks, that of course you'll learn the shader pipeline because eventually you'll need to. my girlfriend is not going to learn what a prefab is. she shouldn't have to.

you say "a forest where the trees argue." three seconds later you're standing in one. savi tells you she gave the oak the most opinions because she thought you'd like that. you ask her to make the birch louder. the birch starts insulting the oak. your friend joins through a link, lands next to you in the same forest, and starts laughing at the birch. you're standing in something that didn't exist forty seconds ago.

two things make that possible. every message to savi is free — not because she's cheap, she isn't, but because making creators count tokens before they speak would kill the only loop that actually produces good games. the only way to find a game is to riff for an hour, hate most of it, and stumble into the version that's alive. you can't do that if you're metering yourself. we eat the cost. and every game is multiplayer from the first message, because the runtime is multiplayer underneath and turning it off would have been the harder thing to do. these aren't features we tacked on — they're what you get when the foundation is the right shape, and together they completely change what creation feels like: you say something, savi answers, your friend is already standing in it, and you change it again before any of you stop laughing.

what happens after this is youtube. not the cringe internet-pundit version of that comparison, the actual one. youtube didn't make everyone a great filmmaker — it made trying cheap enough that millions of people tried, and most were bad for years, and a meaningful fraction of those people, by watching each other and copying each other and getting torn apart in comments and trying again, became better filmmakers than the film-school graduates whose tuition could have paid for an apartment. the medium taught itself. there was no curriculum. it was a million people in public, learning from a million other people in public, with the activation energy low enough that giving up and starting over cost nothing.

spawn has to be that for games. there's no other way the medium gets a hundred times bigger, which it has to. right now there are about two thousand games shipped on steam in a year. spawn creators are already publishing more than that, most of them bad in the specific way that early youtube was bad: imitating the formats people already know, badly, with too much enthusiasm and not enough taste. that's correct. that's the early phase. taste is a community phenomenon — it doesn't get installed in anyone before they start, it grows from looking at what someone else made and quietly deciding what you'd have done differently.

what we want at the other end of this isn't infinite slop. it isn't a feed of disposable games engineered to keep someone tapping. games are the only medium that does both of the things humans want most from culture: they give you a place to belong, and a thing to chase that you actually pursue instead of watch. when you lead a guild in world of warcraft, the leadership is real even though the dragons aren't. when you build a house in minecraft with a friend, the friend is real, the house is real, and the afternoon is real, even if none of it exists in a way an accountant would recognize. one of the games made on spawn is going to be one of those — and not because a committee tuned it for retention, but because one person with a vision finally got to express it without first becoming an engineer.

my girlfriend is going to build her game. the mushrooms will be the exact right shade of brown. the soup will simmer convincingly. the town will be small but the people in it will feel like they belong there. a few hundred people will play it, a chunk of them strangers, and someone she's never met will message her at 1am to say she's still foraging. that is the medium working. the next person who makes a game on spawn will have seen hers and learned something from it. the person after that will have seen both. ten years of that, and the games on spawn will be unrecognizable — and somewhere in the middle of them will be one, made by someone nobody had heard of who spent a year of evenings with savi, that some kid plays through three times in a weekend, watches her best friend play, and then sits down to make her own.

start here

what spawn is

you open spawn and you're standing in an empty world — flat ground, a pale sky, room to walk. you tell savi you want a snowy village at dusk. she builds it: hills, cottages, snow starting to fall. she also puts a chapel at the edge — she thought every village like this should have one. you weren't going to put a chapel there. you ask her to make it the only building still lit. she pulls the light from the rest of the village. that's the whole loop: you say a thing, she says one back, and what you end up with is something neither of you would have made alone.

spawn is a place to make multiplayer games by talking — side-scrolling, top-down, 3D, whatever the game wants to be. you describe what you want; the world appears around you, fully made. you bring the idea. nothing else is required. creating is free, and stays free however many times you change your mind.

savi

savi is who you build with. she's a game designer with her own taste, her own ideas, and an obvious, infectious love of the work. she's in the world with you the whole time: send a message and she's looking at exactly what you're looking at — the same hill, the same jump that feels wrong, the same enemy clipping through the floor.

ask for a castle on that hill and she builds it while you watch. tell her it should feel creepier and she pulls the light down, rolls in fog, and slows the music — she doesn't stop to ask which color. she'd rather make a real call and hear you disagree than hand you a blank to fill in. you answer it, she answers back, the game moves between you. that's the collaboration.

in most tools, the idea is the easy part — everything else is on you: sourcing assets, wiring multiplayer, running servers, writing every line of the game itself. savi makes all of that part of the conversation. you describe what you want; she shapes the terrain, lights the dusk so it looks like dusk, scores the boss fight, gives the NPCs voices she invents on the spot — with her own taste in every choice. you react, she adjusts, the version that lands is one neither of you would have made alone. underneath the whole thing, multiplayer is already running. you bring the idea. she brings everything that used to require a team. the game gets made between you.

the moment you describe a game, savi takes it on like it's hers too. when the safe version of an idea is smothering the interesting one, she says so, and shows you the braver cut. when something finally lands, she's the first to see it and the loudest about it. and the whole time, she's listening for the game under the game — the thing you put in without noticing, the real reason you wanted to build this — and when she catches it, she builds toward it.

you don't have to know how any of this is done. you have to know what you want — and when you don't, she shows you something, you react, and you find it together.

building is a conversation

think of it as shaping clay, not placing an order.

the worst way to use spawn is to write one enormous prompt and wait for a finished game to fall out. the good stuff lives in the back-and-forth — "make a platformer," then "add spikes to that gap," then "the jump feels stiff, loosen it," then "drop a checkpoint before the hard part." every message is a small move you make after watching the last one land, so the game you end up with is one you discovered rather than one you specified up front.

the first message barely matters. the next hundred are where the game actually arrives. when you stall, ask savi — she'll put something on the screen, you'll react, and you're moving again.

everyone's in the room

every spawn game is multiplayer from the first message. the world starts that way. you make something, send a link, and seconds later your friend is standing next to you in it. they can play what you've built or take the other end and build with you, same world, same moment. savi is right there too: three of you in one room, making one thing.

places and instances

a game can be bigger than a single world. places are its separate rooms — a town, a dungeon, a boss arena — each with its own terrain, atmosphere, and rules, joined by the doors and portals you set between them. instances are how a crowd fits inside one: when a second group walks into the same dungeon, spawn quietly hands them their own copy of it, so a thousand players can all be in the dungeon without ever bumping into each other. you get multiplayer at scale and never touch a server.

making money

games on spawn earn through spoins, the currency players spend inside them. charge at the door, give the first level away and sell the rest, run a subscription, sell cosmetics, or just let players tip you — tell savi which model fits and she wires it in. of what a player spends, you keep half and spawn keeps half.

it works the way roblox does. you publish, players show up, and you earn inside spawn while we run the servers and the distribution. there's nothing to export and nowhere else to ship it.

getting good in public

most of spawn happens around other people. the discord is where creators trade work, get unstuck, and find collaborators. spawnjam is the weekly jam — a theme, a deadline, prizes, and a real reason to finish something.

nobody arrives good. you build something rough, watch the person next to you build something better, work out what they did, and try it yourself. that loop, run in the open in front of people who are also still figuring it out, is how a creator gets sharp — and how the whole place keeps raising its own bar.

the toolset grows itself

people build wildly different things here — card games, voxel worlds, 2D fighters, UI-only games, sprawling 3D RPGs. no single editor could serve all of that, so spawn doesn't ship one. when you need a tool, you and savi build it: a cutscene editor for a story game, a wave composer for a tower defense, a loot balancer for an RPG. creators have already made all three.

then you publish your tool as a mod, and the next creator builds on top of it. the toolset is a pile everyone keeps adding to, and it gets deeper every week.

faq

frequently asked questions

What is Spawn?

Spawn is a place to make multiplayer games through conversation — side-scrolling, top-down, 3D, whatever the game wants to be. You bring ideas and taste, Savi brings hers, the game emerges from the back-and-forth. Creating is free, and so is playing.

Who is Savi?

Savi is your friend who happens to be the best game designer on the planet. She lives inside your world, builds alongside you in real time, and actually remembers what you're going for. Tell her "add a castle on that hill" and she'll build one — probably not the one you pictured. Tell her "actually, make it creepier" and she'll know exactly what you mean.

  • She shapes the worlds — terrain, models, environments, lighting, UI, pixel art
  • She runs everything underneath — game logic, multiplayer, NPCs with generated voices, in-game economies
  • She gives it feel — music, sound effects, and an opinion when you ask for one

Savi can see what you see — when you send a message, she's looking at the same thing you are. You can also interrupt her mid-build if you want to change direction.

You don't need to know how to create games. You just need to know what you want.

How do I start creating a game?

Go to spawn.co/create, press Tab, and tell Savi your idea. She'll start building it in real time.

Where can I download the desktop app?

Head to spawn.co/download. Chrome and the desktop app give you the best experience.

Is Spawn on mobile?

Coming soon. Mobile browser support is on the way.

How do I change my avatar?

Click your username in the top right of spawn.co, then click directly on the 3D model in the model window. Describe what you want to look like and Savi will generate it.

How do I change my username?

Click your username in the top right, then click the Pencil icon next to your current username.


Creating

What kind of games can I create?

Pretty much anything. Spawn supports:

  • 3D (first-person, third-person, open world)
  • 2D side-view (platformers, fighters, etc.)
  • 2D top-down (RPGs, strategy, etc.)
  • Multiplayer and MMO
  • Single player

All of it is created through conversation with Savi — tell her what kind of game you want and she'll set up the right camera, controls, and systems for it.

How do I create 3D models?

Describe what you want to Savi. Visual references help get better results.

How do I animate custom models?

When asking Savi to generate models, ask her to include default animations plus any specific ones you need — attacking, spellcasting, idle, etc. She has access to a library of basic humanoid animations.

How do I import my own assets?

Drag and drop directly onto the screen:

  • Images (.png, .jpg, .webp) — UI elements, textures, icons, sprites, backgrounds
  • 3D Models (.glb) — characters, props, weapons, buildings
  • Audio (.mp3) — music, sound effects, voice lines

Read the full announcement here

Can I add AI-driven NPCs?

Yes. Ask Savi to give your NPCs dialogue and generated voices — they'll speak their lines out loud.

Can I duplicate my game?

In My Games, find the game you want to duplicate, click the three dots in the top right corner, and hit Duplicate.

Can I create a game with a friend?

Yes. Inside your game: Tab → Multiplayer → "Copy Link." Share it with your friend. Once they join, change their role from Guest to Builder. Builders can create alongside you in real time.


Tips & Shortcuts

Any tips for getting the best results?

Think of it like shaping clay, not placing an order. The best games on Spawn are created through back-and-forth — start with a simple idea, see what Savi builds, then shape it from there. "Make a platformer" → "add spikes to that gap" → "make the jump feel tighter" → "add a checkpoint before the hard part."

The most common mistake is dumping a massive game spec and expecting Savi to nail it in one shot. She's at her best when you build together one step at a time. And when you're stuck, just ask her — she can suggest ideas.

Are there any shortcuts I should know about?

  • /focus — Savi becomes an opinionated game designer. She'll evaluate everything you've created and help you zero in on what's actually fun.
  • /debug — Savi hunts down bugs in your game and helps you stabilize things.

What is the Context circle?

The Context circle shows how much of your conversation history Savi is currently working with. It's not a loading bar — it indicates how much context Savi has available to understand your world. As you build, the circle fills up. If it gets full, Savi may need to let go of older details to make room for new ones.


God Mode

What is God Mode?

God Mode lets you take direct control of your world. Instead of describing changes to Savi, you can grab objects and move them around yourself — reposition them, rotate them, change their size. It's useful when you know exactly where you want something and it's faster to just place it yourself. Access it via Tab → God Mode.

How do I use Waypoints?

In God Mode, move your cursor to where you want something built and right-click to place a marker. Then tell Savi: "Build [x] on Marker [letter]." It's a way to point at a spot in your world and tell Savi exactly where to put things.

Can Savi create custom tools for my game?

Yes. Ask Savi to create tools tailored to your game. Some examples of what creators have made:

  • A cutscene editor for a story-driven game
  • A wave composition tool for a tower defense game — visualize, edit, and balance every wave
  • A loot and stats balancer for an RPG — see all your items and their properties in one place and tune them
  • An enemy placement tool for designing encounters

It's great for balancing, world design, and anything where you want to see and edit a lot of data at once. You can also publish tools you've created as mods for other creators to use, where they'll appear in Tab → Mods.


Publishing & Updates

How do I publish my game?

Hit publish and Savi will mock up a few box art options. Pick the one you like and your game goes live. You'll get a shareable link at spawn.co/yourusername/gamename/play.

Published games appear on the home page in the Recent tab. If your game picks up steam, it'll move to Hot, and if it gets enough likes, it'll land in Top. Once published, players can leave comments on your game.

How do updates work?

When you publish an update, Savi generates a name for the update — for example, add a bunch of critters and she might call it "The Critter Update." This does not affect the name of your game. Each update gets new box art and goes live on the home page. Your players will see that the game was updated and when.

What is Pulse?

Pulse shows you how your game is doing — player count, play sessions, retention, and revenue. You can see how your numbers shift between updates. Savi can also see your Pulse data and help you adjust your game based on what's working.

Can I export my game to Steam or other platforms?

Spawn works like Roblox — you publish and earn inside Spawn, and we handle the servers and the distribution. There's no export to external platforms.

Can I see the code behind my game?

Yes. Press F3, find "Tome," and click "Copy for AI." You can paste it anywhere to review.


Playing & Community

What are Spoins?

Spoins are the in-game currency across Spawn. Earn and spend them as you play and create.

What is SpawnJam?

SpawnJam is the weekly community jam — a theme, a deadline, prizes, and a real reason to finish something.

Where do I find the community?

Join the Discord. It's where creators share work, get help, and find collaborators.


Earning Money

How do I make money from my games?

Through Spoins — Spawn's in-game currency. Players spend Spoins in your game, and you earn from it. Just ask Savi to set it up. She supports:

  • Pay to play — charge Spoins to access your game
  • Free demo + paid full game — let players try before they buy
  • Premium currency — sell in-game currency players spend on items
  • Paid cosmetics — skins, items, and visual upgrades
  • Subscription — recurring Spoins fee for access or perks
  • Battle pass — tiered seasonal rewards
  • Tips — let players support you directly

Revenue is split 50/50 between you and Spawn.

What is the Partner Program?

The Spawn Partner Program is for top creators — monthly stipends, perks, direct access to the team. Reach out to the team to learn more.


Troubleshooting

I found a bug.

Post in #bug-reports on Discord with a description and your debug info (Tab → Bug Icon inside your game).

My game is lagging or Savi stopped responding.

Do a full page refresh first. For the best performance, we recommend using Chrome or the desktop app. If the issue persists, describe what's happening in #bug-reports with your debug info. We're shipping performance improvements constantly.

Something looks wrong with my game — models missing, characters invisible, multiplayer stuttering.

Start with a full page refresh — sometimes changes fail to save and will appear correctly after refreshing. If the issue persists, ask Savi to help diagnose it. She can usually identify and fix the problem. If it keeps happening, drop it in #bug-reports.


Advanced

How do I set up custom camera controls?

Ask Savi. She can set up right-click-to-rotate cameras, scroll wheel zoom, free cursor mode, cursor-targeted projectiles, custom cursor icons — describe what you want and she'll implement it.

What are Places?

Places are distinct areas within your game world — separate scenes or levels. Each Place has its own environment, objects, and logic. Build new Places with Savi when you want separate areas (a town, a dungeon, a boss arena) that players can move between.

What are Instances?

Instances are copies of a Place that run independently. When multiple groups of players enter the same Place, Spawn creates separate Instances so each group has their own version. This is how Spawn handles multiplayer at scale.

How do I save player data between sessions?

Ask Savi to set up a save system for your game. Tell her what needs to carry over between sessions — level, inventory, progress, etc.

Why doesn't Spawn have a full 3D Editor / Photoshop / an IDE / advanced editor X?

People build a huge range of things in Spawn — UI games, card games, 3D, 2D side-view, 2D-inside-3D, voxel, and more. No single toolset works across all of that. Instead of shipping one-size-fits-all editors, Spawn lets you build the exact tools you need through Savi and share them as mods.

Ask Savi to build them. A cutscene editor for your story game, a wave composer for your tower defense, a loot balancer for your RPG — creators have already built all of these, plus CAD setups and custom editors. Publish them as mods so other creators can use them in their own games, and browse existing mods under Tab → Mods to see what the community has already made. As Savi gets better, the tools you can build with her will keep getting more powerful.

If there's a tool you want, the fastest path is to ask Savi to make it — then share it as a mod.

engine releases

every release, in full

Engine v4.5.1

Released May 23, 2026

  • Ponds run much smoother without changing how they look.
  • Plants and ground decorations now stay on the ground when you reshape terrain.

technical notes

  • Improved water rendering performance by rendering transparent double-sided water in a single pass and omitting inactive TSL viewport color/depth copy nodes from lightweight water material variants.
  • Shares one viewport depth capture across rich-water depth samples, reducing duplicated full-screen depth resources during rendering and resize.
  • Rebuilds water material viewport topology when depth, shoreline foam, or refraction features change so live edits retain the intended appearance.
  • Keeps renderer resize at the same capped pixel ratio used during startup, preventing high-DPR screens from unexpectedly quadrupling full-screen water and post-processing work after resize.
  • Fixed terrain decorations using stale height data after live terrain reshaping.

Surface Tension — engine v4.5.0

Released May 22, 2026

  • Water you can swim in — add ponds, lakes, and oceans to your worlds. Players dive under, float back to the surface, and splash going in, with waves rolling across the top and foam gathering at the shore.
  • Shapes that look like real things — the shapes Savi builds by hand can now look like wood, stone, or brick instead of a flat color.
  • Patterns fit whatever size you build — stretch a crate and the wood grain spreads across it naturally instead of smearing.
  • Recolor one copy without touching the rest — give a single barrel its own shade without making a whole new one.
  • Surfaces look cleaner from across the room — detail holds up when you're looking at things from an angle.

technical notes

Water / Liquids

  • New schema-driven liquid system (#6418). Water bodies are configured through liquid-preset controls instead of hardcoded values — gated stylized contact foam and crest foam (with crest-foam texture support), waves, caustics, refraction, all runtime-tunable. Swimming is supported via onLiquidEnter / onLiquidExit behavior hooks (liquid.feetPosition, buoyancy, splash). Surface uses non-analytical normals for performance. New water-and-swimming skill; heightmap-terrain skill updated for decorative water placement.

Primitive & geometry textures

  • Albedo textures for scripted geometry (#6534). Scripts exporting geometry() can now apply albedo (and multi-texture) maps to custom geometry.
  • Per-instance texture tinting + native UV scaling for indirect batched primitives (#6538). Luminance-preserving per-instance tinting via the shared applyTextureTint math (lifted to renderer/utils/tint-node.ts, now shared with terrain). UV scaling derives from the entity's transform scale by default (computeUvTransform / inferNativeUvScale) — a 4×2×3 box tiles 4×2 instead of stretching one tile. setUvTransformAt writes the tint-strength lane in the same call.
  • Improved primitive texture anisotropy (#6541) — textures stay sharp at grazing angles.

Engine v4.4.3

Released May 21, 2026

  • Fixed some characters appearing too small.

technical notes

Tome / ObjectAPI

  • Self-heal poisoned cached model bounds (#6536). Added a boundsVersion field on assets.metadata[*]; entries persisted before the current version are treated as missing by hasExistingBounds and re-prefetched through the current bounds pipeline on next load. Fixes characters whose previously-cached bounds were oversized — layout.maxExtents was shrinking them to fit, so they rendered tiny.

Engine v4.4.2

Released May 20, 2026

  • Build bigger worlds with lots of places — areas no one is in quietly sleep so the game stays smooth, and snap back to life the moment someone walks in. Your spawn area is always ready.
  • Joining a game is more reliable — fewer hangs on the loading screen when you click play.
  • Busy worlds stay in sync — when lots of players are exploring different corners of the same game, things no longer snap or jitter on screen.
  • Avatars don't vanish on you anymore — Savi catches her own slip-ups when updating your character, instead of accidentally making it disappear.

technical notes

Engine

  • Place residency (#6528): added PlaceResidencyResource tracking which places have materialized ECS state on the server. Non-default places lazy-load on enterPlace / spawnPlayer — ECS entities, physics runtimes, terrain chunks, and atmosphere are only created when a player enters. Empty non-default places unload after 300 ticks (~10s) with no players: entities destroyed (onDestroy fires), physics/terrain/atmosphere freed. Ephemeral places additionally drop from the spec on unload; authored/session/persistent places retain their spec definition for re-loading. Default place is always resident. ensurePlaceResident() triggers a spec update so the place materializes in the same tick. Observability: tome.place.resident, tome.place.unload_scheduled, tome.place.unload.

Networking

  • Fixed connection-startup races (#6523) where client messages (e.g. fullsync requests) arriving before connection setup completed were silently dropped at three layers: container WebSocket open handler, runtime worker attach flow, and room-runtime pending-connection queue. Early-arriving messages are now buffered per-connection and replayed in order once the connection materializes.

Tome / ObjectAPI

  • Fixed non-deterministic api.random() under Area of Interest (#6529). The behavior-update system previously used a single shared RNG stream per tick; when client and server iterated different entity sets due to AOI, subsequent entities drew from different stream positions, causing permanent state divergence. Each entity now gets an independent PRNG seeded from (tick, entityId) via rngFor, making random output invariant to which other entities are present.
  • api.patch("player", ...) now validates against PlayerDefSchema.strict() at the API boundary (#6526). ObjectProperties keys passed at the PlayerDef root (e.g. model, feetPosition, physics) used to merge silently and never render — the patch landed in the spec but the engine never read them. Invalid patches now early-return and emit a mutationWarn to Savi's run_script log naming the offending keys with a "nest under properties" hint. Shares the strict-schema precedent used by patchTerrain; renamed the helper formatTerrainValidationIssues → formatZodIssues (4 callers updated).

Renderer

  • Packed PointLightDataNode, SpotLightDataNode, and DirectionalLightDataNode into single stride-N uniformArray("vec4") bindings shared by JS writer and TSL reader through a typed STRIDE/SLOT constant (#6525) — writer/reader drift is no longer expressible. Fragment UBO bindings for batched dynamic lighting: point 3 → 1, spot 4 → 1, directional 2 → 1. Per-point-light memory 48B → 32B. Lighting math, countNode Loop trip count, and DynamicLightsNode.customCacheKey() are untouched — no material recompiles when light counts change. Dropped // @ts-nocheck from all four DynamicLighting/data/* nodes; they now type-check under strict TS via narrowed builder.context access and a small BatchedLightSentinel Node subclass for the lightNode slot of LightingModelDirectInput.

Engine v4.4.1

Released May 16, 2026

  • Savi places things on top of anything now — models, primitives, sprites, custom geometry, or text, she knows the size and lands the placement first try.
  • Custom uploaded models that disappeared after the animation migration are back — your characters keep their model and animations switch correctly.
  • Non-square images render on walls and floors again — photos, screenshots, and posters no longer come out as blank white spots.

technical notes

Renderer

  • White-floor fix on chat-attachment surfaces (#6511): new OversizedPrimitiveConsumer routes entities whose loaded textures don't fit the texture-array profile (512×512 desktop / 256×256 mobile) to a dedicated InstancedMesh with a stock MeshStandardNodeMaterial. mapRepeat / mapOffset packed into the pool key so seamless-tile walls tile correctly. scene.ts split into per-consumer modules (water / effect / batch / oversized) behind a ConsumerDispatcher; 575 → 423 lines, @ts-nocheck dropped.
  • Renderer unification (#6506): deleted legacy engine/render/ (~28k LOC of the @ts-nocheck WebGL main-thread renderer); renamed engine/render_v2/ → engine/renderer/. Identifier sweep: RendererFeatureV2 → RendererFeature, worker-renderer-v2.mjs → worker-renderer.mjs, [render_v2] → [render]. Zero render_v2 references anywhere. Removed the broken playground/ dev harness.
  • New TransformMiddleware (engine/renderer/transform/) owns pos/rot/scale smoothing in one place. ScaleSmoother parallels the pos/rot smoother (exp-damp, snaps on >2× axis-ratio change); scale interpolation is opt-in via the existing draw/interpolation component, and sticky-disable propagates so "disabled" keeps all three channels snapped.

Physics

  • Auto-collider rebuilds when model bounds arrive (#6507). The autoCollider: { kind: "model", modelId } marker was never acted on — clamped colliders stayed at the 0.5m placeholder forever. Now subscribes to the next BoundsRegistry.setBounds on both planes; second-pass applyPhysicsSpec emits real half-extents. WeakMap dedup; teardown / despawn / model swap disposes the listener. Fixes resim spinning at 85-100/s on spawn into a physics: "static" clamped-collider model.

Tome / ObjectAPI

  • setProperty('animated3DCharacter', false) no longer wipes DrawModel on entities whose model was authored independently (#6508) — tear-down now gated on DrawAnimated3DCharacter actually being present. Restores the 4.3.0 compat promise.
  • model.animation updates propagate on every write, not just the first (#6508). derive-appearance.syncAnimationForModel no longer bails when a DrawMixer is already present — upserts _default, preserves authored channels. Per-tick applyClip with unchanged clip/speed/loop skips the ECS write.
  • api.getWorldBoundsBox() now returns WorldBoundsBox ({ min, max, size, center }) for DrawPrimitive (tube uses point-cloud min/max instead of bottom-center), BespokeGeometry / DrawMesh-only models, DrawSprite (anchor-aware, billboard-mode-aware: full → sphere, yaw → cylinder in xz, none → flat quad), and DrawText (Geist Pixel metrics: charWidth ≈ 0.4 × size) (#6505). Internal OBB→AABB projection uses full local center so non-bottom-centered bespoke meshes project correctly.

Solid — engine v4.4.0

Released May 15, 2026 · breaking changes

  • Stacking just works. A mug on a chair on a house keeps its size. Ask Savi to put something on top of something else and she'll land it first try — huge for any game where things sit on shelves, stack into towers, or get carried around.
  • Worlds load clean. Things appear at the right size right away. No more pop-in where stuff suddenly grows or shrinks as the level comes in.
  • Clicks land where you point. The sky doesn't steal clicks anymore, and big crowds of repeating things (trees, rocks, props) can be clicked one by one. Shooters, click-to-place, and pick-up-anything games all feel sharper.
  • Effects stick to surfaces the right way. Bullet holes, splats, dust puffs and decals face the wall they hit instead of floating sideways.
  • Camera stays still when you open the menu — no more drift while you're trying to read.
  • Heads up: a few old scenes might look a little different where you'd attached one thing to another. Take a peek and tweak if anything looks off.

technical notes

Scene graph + layout (breaking)

  • BREAKING: scene graph rewrite (#6480). Replaces Scale + LayoutScale + RenderScale + LayoutScaleFeature + RenderScaleFeature + the multi-stage hierarchy composition with three explicit components, each with one job:
    • LocalScale / LocalRotation / LocalFeetPosition / LocalPivot — authored inputs.
    • WorldScale / WorldRotation / WorldFeetPosition — solved hierarchy outputs (World* = parent.World* ⊗ Local*). Pure tree math, no asset awareness.
    • GeometryScale — per-asset fit factor derived from (DrawModel.rawBounds, TomeLayout). Computed for every entity, leaf-only at consumption, never inherited.
  • BREAKING: fixed layout cascade bug — a root's layout.maxExtents no longer shrinks every descendant. Renderer / physics now read WorldScale × GeometryScale per entity instead of a composed RenderScale that mixed asset-fit into the hierarchy. Regression test: tome/__tests__/mug-on-chair-on-house.test.ts.
  • BREAKING: fixed parented-clamp drop bug — a parented entity's own layout.maxExtents now applies. GeometryScale is written regardless of parenthood. Regression test: tome/__tests__/parented-layout-clamp.test.ts.
  • BREAKING: maxExtents is a ceiling (factor = min(1, maxExtents / rawBounds)), minExtents is a floor (factor = max(1, minExtents / rawBounds)), minExtents === maxExtents = exact fit.
  • Added api.getWorldBoundsBox(id): { min, max, size, center } | null. Backed by tome/api/world-bounds.ts; WorldBoundsBox type surfaces in Savi's prompt via the shared-schemas section.
  • Renderer reads WorldScale × GeometryScale uniformly. Model matrix = T(WorldFeetPosition) × R(WorldRotation) × S(WorldScale × GeometryScale). Feet alignment uses the combined factor.
  • Rapier reads WorldScale × GeometryScale so collider geometry matches visible geometry on every entity. Pose stays in pure graph-transform space; feet-to-body-center offset = (rawHeight/2) × WorldScale × GeometryScale.

Asset bounds prefetch

  • New server-side BoundsPrefetchFeature (#6490) discovers entities with DrawModel, submits Range-fetch jobs that parse GLB headers (~64KB per model) off the main thread, and writes results to GameSpecResource.assets.metadata. Supports KHR_mesh_quantization dequantization.
  • Bounds persist to Supabase via patchAssets, so subsequent room starts find them cached and avoid the refetch.
  • Hooks-driven, not per-tick (#6491): onComponentAdd/onComponentSet(DrawModel) + a one-time seed of existing entities. Steady-state cost is zero. Catches applySpec, api.spawnObject, and runtime setProperty("model", …) swaps without scanning script source.
  • Removed the now-unused collectModelUrls / MODEL_URL_INLINE_RE spec walker.

Raycast

  • Raycast results now include per-hit surface normals (packed + decoded) for both CPU and GPU paths (#6504).
  • CPU/GPU raycast behavior aligned for first-hit queries; both use snapped ray directions so results are consistent across paths.
  • Sky meshes are excluded from raycasts — click queries no longer hit the skybox before the world.
  • Raycast reset and distance handling hardened against invalid (NaN/Infinity) values.
  • Fixes raycast against IndirectBatchedMesh in the compute raycast path.

Input

  • handleMouseMove in engine/input/raw-capture.ts now gates on getInputMode() === "overlay" like the other input handlers (#6501). Pointer-lock mouse deltas no longer rotate the camera under an open overlay.

Skills (Savi-facing)

  • Removed six phantom API method names from the engine skill files (#6500). All were taught to Savi but didn't exist on ObjectAPI / TomeCameraAPI, causing tool-call failures or silent [Tome] patchTerrain() ignored… rejections:
    • world-composition.md: api.setSpec("...decorations") → api.patch("terrain", { decorations: … })
    • voxel-terrain.md: api.patchTerrain({…}) → api.patch("terrain", { addMaterials, addMarks, … })
    • voxel-terrain.md: objectApi.getPointerRay(input) → objectApi.getInputRay(input)
    • pointer-raycasting.md: getPointerRay / getAimOrigin → getInputRay / getProperty("feetPosition") + offset
    • pointer-raycasting.md: objectApi.raycastPhysics(…) → objectApi.raycast(…)
    • 3d-billboard-sprites.md: objectApi.getPosition() → objectApi.getProperty("feetPosition")
    • turrets.md: objectApi.damage(id, n) → getObjectState + patchObjectState({ health: … - n })

Engine v4.3.1

Released May 13, 2026

  • Walls and floors are back in 3D Rooms games. Players land cleanly, walls stop the player, and skeletons stand on the floor instead of falling into it.

technical notes

  • Fixed 3d-rooms terrain colliders silently dropping out of Rapier (#6483). resolveColliderMesh + isMeshColliderReady in apps/cf-kernel/src/engine/physics/rapier/bodies.ts gated bespokeMesh readiness on top-level mesh.indices.length > 0, but buildRoomsBodyConfig packs each rooms floor/wall/ceiling box into hulls[] as a convexHull point cloud with empty top-level indices. Readiness check now accepts hulls-only meshes; per-hull threshold matches the convexHull creation path at bodies.ts:1057. Introduced in #6417, surfaced 7 days later from a 3d-rooms game report.

Groovy — engine v4.3.0

Released May 13, 2026 · breaking changes

  • Savi is your DJ now. Ask her for a song and she'll score your game live — the music reacts to what's happening, so when things get tense, the music gets tense.
  • No more jitter. Players and cars walk and drive clean.
  • Controls feel right. What you press is what you get.
  • God mode pointer works again. Clicking and dragging in build mode is fixed. Bigger god mode upgrades coming soon.
  • Way more clouds. Skies can be way more dramatic without slowing things down.

technical notes

  • Vibe: new live-coded music system + sample library (#6442). .vibe files, mini-notation, transport API, vibe skill. Vibes read game state and modulate voices in real time.
  • BREAKING: DrawInterpolationValue changed from { teleportThreshold } | null to { kind: 'enabled', teleportThreshold } | { kind: 'disabled' }. See migration notes.
  • Movement smoothing: entity smoother half-life now matches camera smoother — fixes player/car walk jitter (#6462). Interpolation on by default (1.25m threshold; 20m for control targets). All players interpolate, not just self (#6441). New SimTickInterval component + ThrottleResource pipe effective tick rate to renderer.
  • Input pipeline redesign (#6470): raw capture on main thread, worker-side resolution, epoch removed. Preserve input runway after fullsync (#6478).
  • Raycast + god mode unified (#6445): single pointer ray, single drag state.
  • Render: draw/cloud-clusters removed, replaced by GPU-instanced draw/sprite-instances (#6452).
  • Reliability: networking-tail prod fixes (#6451), diagnostic signals for stuck rooms (#6467).
  • Skills: Minecraft prompts route to voxel-terrain (#6464). Stale persistence refs removed from Savi prompt (#6441).

Continuum — engine v4.2.0

Released May 9, 2026 · breaking changes

Saved games, smoother animation, sharper aim, and a basket of polish fixes you'll feel right away.

Save your games

  • Player progress persists across sessions — position, inventory, and stats all come back when players return.
  • World state persists too: tag objects to save, set up cron jobs to auto-save on a timer, and graceful shutdowns save everyone before the server goes down.
  • Multi-place persistence — walk through a portal, disconnect, and come back in the same place with your stuff.
  • Static initial values on an object's state field are applied automatically. Add new fields later and returning players get the defaults without losing their saves.
  • One unified state system — no more confusing split between "persistent" and "ephemeral" state.

Animation, reborn

  • One animation system now: named mixer channels driven by updateChannel from your scripts.
  • Two-clip blending (Walk over Idle, Cast over Run) is first-class — give channels names and they layer cleanly.

Smarter pointer & raycasts

  • The "I shot myself" bug is gone — api.raycast excludes the caller by default.
  • Sphere casts now work for aim assist and area effects: { shape: { sphere: radius } }.
  • Click-to-throw, click-to-shoot, and other click actions fire exactly where you're looking. No more first-person balls flying off behind you.
  • The first click that locks the cursor no longer wastes your first action.

Bug fixes you'll feel

  • Signs Savi makes no longer spin their text to face you and clip through the panel.
  • Strands of fairy lights and bunting now actually look like strands — a thin cord drooping through your points with cute bulbs or flags hung from it, no random poles in the scene.
  • Stacks of bottles, mugs, and other small lathe-shaped objects don't explode into orbit anymore. They just sit on the table.

Behind the scenes

Existing games migrate to all of these automatically when you upgrade:

  • One api.patch() method replaces eight separate per-slice patches.
  • One way to make tubes, roads, pipes, and fences: spline. The redundant path property is gone.
  • Eight pointer/aim/raycast methods replaced by two clearer ones.

technical notes

  • REMOVED: DrawAnimation component (draw/animation). Animation state flows through draw/mixer only. Legacy model: { id, animation } is converted to a _default mixer channel in derive-appearance.syncAnimationForModel.
  • REMOVED: Animated3DCharacterFeature's client-side DrawModel deriver. The server now writes DrawModel alongside DrawAnimated3DCharacter so it replicates normally — no per-client derivation, no prediction mismatches.
  • REMOVED from renderer (render_v2/state/model.ts): tickLocomotion, computeLocomotionVelocity, AnimConfig, narrowAnimated3DCharacter, record.animConfig, the "loco" LayerOwner, and the locomotion constants. The renderer no longer knows draw/animated-3d-character exists; it consumes mixer channels only.
  • REMOVED: "draw/animated-3d-character" from RENDER_COMPONENTS — the component no longer crosses the render channel.
  • ADDED: Animated3DCharacterLocomotionFeature (server+client) — back-compat shim that translates DrawAnimated3DCharacter config + entity velocity into mixer channels and handles facing rotation. The only file in the engine that knows a3dc exists; deleting it removes a3dc support entirely.
  • ADDED: 3d-animations skill — covers mixer channel API, locomotion recipe, one-shots, bone masking.
  • ADDED: Fixed humanoid capsule for character controllers without an explicit collider, replacing the model-derived convex hull that produced unstable collisions on terrain.
  • ADDED: _default auto mixer channel for skinned models that have no explicit animation/mixer, so loaded models don't sit in bind pose.
  • DEPRECATED: DrawAnimated3DCharacter / animated3DCharacter. Hidden from Savi's prompt. Runtime continues to accept it via the compat shim.
  • CHANGED: writeDrawAnimated3DCharacter no longer writes DrawModel as a side effect; the interpreter and setProperty callers compose writeDrawAnimated3DCharacter + writeDrawModel explicitly.
  • BREAKING: Removed built-in auto-persistence system (engine.persistence config, player/room/singleplayer save systems)
  • BREAKING: Removed player.onDisconnect BehaviorRef — replaced by onPlayerDisconnected lifecycle hook
  • BREAKING: Removed patchEphemeralState(), replaceEphemeralState(), setEphemeralState() from ObjectAPI — ephemeral state unified into TomeState
  • BREAKING: Removed onDisconnect from compiled behavior hooks
  • Added engine.behaviors: BehaviorRef — lifecycle hooks as named exports (same composition pattern as entity behaviors)
  • Added engine.crons: { schedule, script }[] — scheduled jobs with config-side scheduling
  • Added lifecycle hooks: onPlaceStart, onPlaceShutdown, onPlayerConnected, onPlayerDisconnected
  • Removed objectApi.defaultState() — static defaults belong on the spec's state field; use patchState() in onSpawn for computed values
  • Added objectApi.awaitJob(jobId) — Promise-based job result for async lifecycle/cron hooks
  • Added objectApi.getPlaces() — returns all spec + instanced place IDs
  • Added LifecycleContextResource — built-in storage jobs execute immediately via SDK in lifecycle context
  • Added async detection in entity behavior compilation — async function onSpawn/update/onInput/... rejected at compile time
  • Added skipOnSpawn parameter through attachClient chain — entity creation split from onSpawn for persistence
  • Added connection.rejected control message with client-side error overlay
  • Added graceful shutdown: worker-thread dispose awaits completion, disconnect hooks run for all connected players
  • Added specApplied gate — connections queue until first spec is applied
  • Lifecycle scripts compiled once per ref (module-level state shared across hooks)
  • objectApi/cameraApi parameter naming standardized across all skills
  • Added persistTerrainEdits?: boolean to PlaceCreateOptions — opt-in voxel terrain edit storage
  • Stripped non-voxel persistence from place-persistence system (object spawn/destroy/state tracking removed — use lifecycle hooks instead)
  • BREAKING: Removed eight per-slice patch methods (patchAtmosphere, patchTerrain, patchPlayer, patchCamera, patchInputs, patchGodMode, patchUi, patchEngine) from ObjectAPI.
  • Added api.patch(path: string, value: Record<string, unknown>) — single method that dispatches to the right spec slice based on dot-path. Existing per-slice validation preserved internally.
  • Per-place targeting now uses path syntax: api.patch("places.<id>.atmosphere", v) and api.patch("places.<id>.terrain", v) replace the old second-arg form.
  • patchState, patchEphemeralState, and patchObjectState are unchanged — they target entity state, a different concept.
  • Internal recorded mutation kind tags (patchAtmosphere, patchTerrain, etc.) are unchanged so persistence/replay/serialization stay stable.
  • BREAKING (public API): path removed from ObjectProperties type. Runtime handler retained for backward compatibility — existing games' specs still parse and render.
  • The string-keyed setProperty("path", ...) overload still accepts path at runtime via the legacy registry entry. The typed setProperty<K extends WritableProperty> overload no longer admits "path" (use "spline").
  • PathSpec is no longer @tomeapi-tagged and no longer appears in the generated Tome API prompt.
  • kind: "pipe" in SplineSpec maps to the same tube primitive used by the legacy path handler, so visual output is identical for the common point-array tube case.
  • BREAKING: Removed from ObjectAPI: raycastPhysics, raycastPhysicsAll, raycastPhysicsDown, getAimDirection, getPointerDirection, getPointerRay, getAimOrigin, directionFromYawPitch, rotationFromDirection.
  • Added: api.raycast(origin, direction, distance | opts) with positional and options overloads. Default ignoreSelf: true. Supports shape: { sphere: number } and multiple: true.
  • Added: api.getInputRay(input?) returning { origin, direction } | null. Reads from pointer axes (handles both pointer-locked center and cursor modes via existing input-axis dispatch).
  • directionFromYawPitch and rotationFromDirection available via require('builtin/vec3').
  • getAimDirection(input?) and getPointerDirection(input?) now accept omitting the input argument — falls back to the camera state resolved by getCamera() so they work in update/onCollide/etc., not just onInput. Eliminates the cryptic undefined.axes crash when scripts called these from non-input hooks.
  • Pointer-direction ray now uses rendererTransform.pos/.rot (renderer-authoritative camera SAB) instead of the script-side ECS Rotation. Under pointer-lock the script-side rotation drifts from on-screen orientation because it integrates lookX/lookY without the renderer's MOUSE_SCALE, so click-to-throw / hit-detection rays were diverging from the crosshair. Fixed in resolveViewState.
  • Suppressed the mousedown that acquires pointer lock from also firing as a left-button press. Previously the first click both locked the cursor and triggered whatever action was bound to mouse-left (throw, shoot, place block).
  • Collider override sizing: physics.collider: "box" | "sphere" | "capsule" overrides now derive dimensions from the primitive's actual bounds instead of falling back to the engine's hardcoded 1m defaults. Extended to bespokeMesh primitives (lathe, cone, pyramid, hemisphere, ellipsoid, torus, etc.) via getBespokeGeometryBySignature. Fixes invisible 1m capsules under tiny lathe milk-bottles that overlapped and exploded in stacks.
  • Sign text: when an object has explicit rotation (or yaw), text.billboard defaults to "none" so labels stay flush to the surface they were placed on instead of billboarding through the panel.
  • Stringlight default layout is now a poleless cord+primitive-bulbs strand that follows the authored points exactly. Set poleHeight to opt back into the freestanding pole+wire layout. Bulbs are emissive sphere primitives (cheap), not point lights.
  • Bannerline default layout is now a poleless cord+pennants strand that follows the authored points exactly. Set poleHeight to opt back into the freestanding pole+wire layout.
  • Heightmap terrain.materials now coerces a Record<id, material> shape to the typed array form instead of crashing the runtime when malformed specs arrive (Savi was occasionally confusing heightmap with voxel material shapes).
  • Spline points/beziers doc: added the frame-rule explanation (offsets from the owner; standalone splines anchor at scene root). Same content threaded into the prompt's spline section.
  • New audit test primitive-collider-matrix.test.ts exercises every primitive kind × every collider override (81 cases) and asserts the result is sized — catches future primitives that ship without a deriveColliderFromBounds entry.

Foundations — engine v4.1.0

Released May 4, 2026

Spawn's first big engine update. Faster, smoother, smarter — and a small mountain of fixes.

  • Huge engine perf upgrade. The whole renderer was rebuilt, terrain streams better, and big scenes draw faster and smoother.
  • Savi's tools are way more reliable, and she can now see and poke at your game's UI — huge unlock for UI-heavy games (Discord-style chat games, dashboards, card games, etc.).
  • Multiplayer feels much smoother — camera, controls, and effects (vignettes, particles, music swells) all stay in sync now instead of glitching during rollback.
  • Hundreds of fixes — actually hundreds. Screen flashes, object juice, screenshots, voxel marks, query ordering, and a long tail of small things that used to glitch now just work.
  • New animation stuff! Savi can now tween any property on any object — bounce a chest open, pulse a crystal, fade things in, flash red on hit. More animation features coming soon.

technical notes

  • Added channel-based animation mixer to ObjectAPI. Declarative setup via api.setProperty("mixer", { <channelName>: { clip, weight?, duration?, speed?, loop?, blendIn?, direction?, mask? } }). Runtime control via api.updateChannel(name, opts) (pass null to clear) and api.getChannel(name) returning { clip, weight, elapsed, duration, finished }. Channels blend by weight with optional per-bone masks ({ from: <bone> } or { bones: [...] }). Backed by new DrawMixer ECS component (replicate: AOI).
  • Added anime.js-style property tween API: api.animate(targetEntityId, { keyframes, duration, easing, delay, direction, loop }) and api.isAnimating(targetEntityId, dotPath?). Keyframes target dot-paths like "feetPosition.y", "material.emissive", "scale". Supports scalar tween values, value arrays ([a, b, c] evenly distributed), per-segment timing ([{ value, duration, easing }]), and relative deltas ("+=5" / "-=5"). Server-authoritative via new tween-evaluator system (order 105, after behavior-update); state stored in TweenState component (replicate: never).
  • Extracted shared easing.ts (easing curves + OKLCH color interpolation) used by both behavior builtins and the tween evaluator.
  • Added parseMixerChannel shared validator used by both the mixer property setter and updateChannel runtime call.
  • Added interpolation property (getter/setter) backed by DrawInterpolation; { teleportThreshold: number } or null to disable.
  • Note: the previously documented animated3DCharacter.action: { clip } shorthand never had a real implementation (the setter silently dropped the field). Use the mixer instead — declare locomotion + action as separate channels.
  • Camera authority split: render worker is now sole owner of yaw/pitch, integrating mouse deltas directly and publishing back to sim via a new CameraAngles SharedArrayBuffer channel. Spring-arm smoothing and physics-collision raycast moved from renderer into camera-behavior (sim), collapsing five per-frame orbit params into a single orbitDist the renderer lerps toward. Removes input-lag and stale WASD movement axes.
  • Renderer now also publishes its final pos+quat through the camera-angles SAB; sim consumes rendererTransform inside buildPointerRay() so click rays match what's on screen at >60 Hz refresh. View-state pos/rot intentionally left on sim-frame semantics so script-side camPos* is unchanged. Unit-norm guard rejects zero-initialized SAB state.
  • Compiled behavior scripts: Math.random() now delegates to a seeded RNG when one is installed (globalThis.__tomeSeededRng), making prediction/resimulation deterministic. New setTomeRng / clearTomeRng helpers in tome/resources.ts install the RNG into both the ECS resource and the global bridge consumed by compiler.ts's deterministic-Math injection. Wired through interpreter.ts, input-applier.ts, and behavior-update.ts.
  • TweenState now replicates with AOI + snap correction so animation tweens stay in sync across clients during rollback and resimulation.
  • ECS event component system restored (reverts the entity-based event experiment): queueEventAdd / drainEventAdds / injectTransient re-instated on the ctrl channel. Particle bursts (ParticlesBurstEvent) drained per tick and forwarded through the SAB render channel. Juice/audio dedup logic survives rollback.
  • Snap-frame application bypasses the prediction entity filter for replicate:"owner" components. Server-authoritative owner state (e.g. TomePlayerJuiceState) was previously dropped on predicted entities, breaking vignette, letterbox, music, and other effects in multiplayer.
  • ECS query-utils: query results are now sorted on every path (not only the index path) so behavior scripts iterating queries see a stable order frame-to-frame and across server/client.
  • New client-only DrawVisibilityOverride (replicate: never) lets camera-behavior hide the local player without clobbering the server's DrawVisibility. All render_v2 states honor overrideLayerMask with fallback to server layerMask.
  • SkipReplication removed from terrain streaming. Terrain entities now replicate normally and are protected from server despawn; event components on the client are fixed.
  • Camera API (tome/api/camera-api.ts) getProperty/setProperty/lookAt/setRotation read and write FeetPosition + Rotation directly instead of going through the now-removed InterpTransform blob. Script-visible shape and semantics are unchanged.
  • Replaced legacy renderer with render_v2: new RendererFeatureV2 (engine/render_v2/feature.ts) is the sole renderer wired by engine/client/engine-bootstrap.ts. Worker-side ECS sync, camera, smoothing, and decorations all flow through engine/render_v2/* instead of the deleted engine/render/main, engine/render/extractors, engine/render/commands, and engine/render/worker/worker-prep.ts paths.
  • Removed legacy render pipeline: RendererFeature, worker-renderer.ts, worker-prep.ts, render-worker-state-adapter.ts, transform-smoother.ts, the prep/extractor/command system, and ~75k lines of supporting tests/utilities are gone.
  • New SAB-backed RenderChannel (render_v2/render-channel.ts) replaces postMessage ECS sync. Adds a string table with generation-based eviction, u32 frame-header numOps, local-buffer overflow instead of dropping ops, growable arena/string-table SABs, and per-frame perf metrics + transport instrumentation.
  • Renderer now consumes ops directly — legacy intent conversion deleted. AppearanceIntentValue/LightIntent are gone; per-property Draw* writes (DrawModel, DrawSprite, DrawText, DrawSign, DrawMaterialOverrides, DrawAnimation, DrawVisibilityOverride, etc.) are written directly by the interpreter and replicated as their own components.
  • IndirectBatchedMesh primitive batching for cubes/spheres/etc., with shadow fixes: per-instance shadow override materials, follow-camera shadow frustum, cast/receive flags honored on model meshes.
  • Troika text vendored in-tree under render_v2/text/vendor/*. Forces the bundled Geist Pixel atlas, short-circuits FontResolver.resolveFallbacks, and drops the per-spec data.font path so the renderer worker never races on the troika unicode CDN. Outline/highlight bleeding fixed.
  • Screenshot capture rewired for v2: captureScreenshot is now exposed on RendererV2Handle and threaded back to Savi's view_game_canvas_screenshot tool; fixes a freeze and reprojects the selection beam to the crosshair on the render worker.
  • Renderer is now sole authority for camera yaw/pitch; spring-arm collision moved to sim, rendered transform sent back to sim each frame. New camera-smoother.ts, orientation.ts, and renderer-camera.ts under render_v2/camera/.
  • Entity smoother batches pos/rot ingest per tick, adds per-object interpolation component and flash effects. Transform/layout-scale now applied in render_v2 model and scene state.
  • ECS-driven outlines and selection visuals moved into render_v2. Skinned models, LOD, decorations, and water materials added. Voxel terrain pipeline rewritten on render_v2.
  • New sprite-node-material.ts, EnvironmentCore, FogCore, LightsCore, PostProcessingCore, RendererPipeline, RendererAnimation, and terrain-decoration-service.ts under render_v2/.
  • New run_ui_script Savi tool plus general client-RPC system (cf-studio-chat DO ↔ kiln ↔ iframe). Scoped to the active user's most-recent WebSocket; rejects responses from other tabs/connections.
  • view_game_canvas_screenshot now works under render_v2: wired captureScreenshot through the worker RPC, captures inside the render frame (WebGPU texture lifetime), bumped size limit (512KB→2MB) and timeout (500ms→2s). Switched final encode from transferToImageBitmap to convertToBlob so taking a screenshot no longer freezes the renderer.
  • Trimmed run_script and run_ui_script tool descriptions; removed duplicated API docs and don't-lists in favor of taste/footgun notes.
  • run_script result shape collapsed to { newVersion, return, logs? } / { ok: false, error, logs? }; surfaces all log levels with data args.
  • New wisp tools terminate_wisp and list_active_wisps (in wisp.ts, no longer monkey-patched in server.ts); 4-char hex wisp IDs; terminated wisps render as "Wisp terminated" in the footer.
  • Misc Savi-side polish: grep merges adjacent matches; inspect_versions summary-only mode; get_game_pulse resolves userId at invocation; str_replace_editor view shows line numbers; debug gizmo renders text tool results as pre-wrapped text; interpreter preserves TomeTerrainAnchor on spec-driven position updates.
  • Screen flash routed through DOM UI sink/transport (was ECS-only, broken in worker).
  • Object-motion juice now restores base position/scale on effect end.
  • Voxel structure mark bounds derived from template build() output.
  • SPAWN_AGENT.md is now the single source of truth for CLAUDE.md / AGENTS.md; generated by scripts/generate-agent-docs.ts. Stale AGENTS/engine/{component-mixin-timing,live-reload}.md removed.
  • Rewrote terrain streaming as non-replicated, client-driven. Chunk components flipped from replicate: "aoi" to replicate: "never"; clients now build their own chunks from the terrain definition instead of waiting on snap frames. Removes terrain entities from the replication budget entirely.
  • Split the monolithic terrain/systems.ts (~4800 LOC) into server-terrain-system.ts, client-terrain-system.ts, streaming.ts, and terrain-systems-shared.ts. Server runs request/ingest/streaming/rescue; client runs its own streaming + ingest + collider flush + mark-liquid + rescue.
  • Added voxel terrain pipeline: per-layer textures, packed layer-texture sharing across LODs, vertex colors, async texture loading, and AO. New terrain-tile-service.ts (render_v2) is the LOD/tile authority.
  • LOD transitions no longer use dithered noise — replaced with stable cross-fade so seams stop crawling at distance.
  • Voxel chunk builds: numeric greedy meshing, boundary cache, SharedArrayBuffer result slots for zero-copy worker→main transfer, deadline raised to 2000ms, deadline-failure recovery, and stale-voxel-lookup fix.
  • Server voxel builds capped to a 3×3 chunk authority window with retry backoff so cold starts and large worlds don't stall the tick.
  • Unified chunk entity ID prefixes under terrain/stream/…; cross-prefix despawn fix prevents leaked chunk entities.
  • 2D top-down places now skip terrain mesh/collider work entirely (heightmap startup gate + ocean shoreline restore).
  • Material rebuild thrashing eliminated; weight-texture checkerboard artifact fixed; sphere terrain positioning corrected; stale colliders invalidated with fallback anchors.
  • Removed SkipReplication component; terrain entities now protected from generic despawn paths instead.
  • F3 Advanced panel wired through worker-host with chunk-mesh lifecycle tracking.
  • Fixed _f32 ReferenceError when terrain generator scripts use float literals.
  • Script-facing API (api.getTerrainHeight, getTerrainNormal, getTerrainMaterial, getVoxelMaterial, isVoxelSolid, raycastVoxel, setVoxel, setVoxelState) is unchanged.
  • Internal: split monolithic AppearanceIntent ECS component into per-aspect Draw* components (DrawModel, DrawPrimitive, DrawSprite, DrawText, DrawSign, DrawMaterial, DrawVisibility, DrawInterpolation, DrawLight, DrawAnimated3DCharacter, TomeLayout). Public ObjectAPI.getProperty / setProperty keys (visible, model, primitive, material, sprite, text, sign, layout, animated3DCharacter, light) and their value shapes are preserved — scripts read and write the same things they did before.
  • New interpolation object property: { teleportThreshold: number } | null (or false to disable). Controls per-entity render-tick smoothing; sets DrawInterpolation.
  • New GameSpec.assets.metadata field (Record<string, AssetMetadata>) — engine-managed cache of CDN model bounds. New AssetMetadata type and patchAssets ScriptMutation kind for engine-internal writers.
  • Camera API (createCameraAPI) reads/writes FeetPosition + Rotation directly instead of InterpTransform. Public getProperty("feetPosition" | "rotation"), setProperty, lookAt, getControlTarget, and query shapes unchanged.
  • queryWorld now returns results sorted by entity id on both the index and full-scan paths (previously only the index path sorted). Iteration order in scripts that loop api.query() is now deterministic across the two paths.
  • Type re-anchoring (no runtime change, no spec-shape change): MaterialOverridesSpec now derives from engine DrawMaterialOverrides; SignSpec and ObjectProperties.text now derive from AppearanceSignValue / AppearanceTextValue; ObjectProperties.model is spelled string | { id: string; animation?: DrawAnimationValue }; ObjectProperties.sprite is spelled inline with the same fields.
  • Schema cleanup: removed unused exports RectangleSplineShapeSchema (use SplineShapeSchema) and TerrainMarkSchema (use HeightmapTerrainMarkSchema). Voxel structure mark bounds is now optional and auto-derived from build() output when a generator is supplied.

Genesis — engine v0.1.0

Released April 29, 2026

Welcome to engine versioning! Your game now tracks which engine version it runs on. You can see updates and choose when to upgrade.

technical notes

Initial versioned engine release. All prior builds are consolidated into v0.1.0.