Baker Engine

Churn and Spellbox were both built without an engine. The parts that kept showing up in both now have a home of their own: Baker Engine, a web game engine in TypeScript, built out of the games built on it.

Churn's chess board on its stone table in the park, with Bun the rabbit peeking over the far side and a Spellbox billboard behind
Churn's board, sitting in the first scene built on Baker Engine.

Why an engine now

Everything on this site has been hand-rolled: no Unity, no Godot, no engine. That was the point, and it still is. But two games in, we had two fixed-step loops, two profilers, two synthesized audio systems, and two sets of design tokens kept in sync by hand. Once something exists twice, it's time to pull it out.

Baker Engine is built on one rule: extract, don't design. Engines designed ahead of their games guess the wrong seams. So nothing goes in because two games might want it someday. A module joins the engine only when a working game already uses it, it contains no game content, and that game deletes its own copy once the engine's version lands. Every file in the repo is logged in a ledger that says where it came from. The one deliberate exception so far is the park in the picture above, built in the engine first and adopted by Churn the same day.

What's in it

Baker Engine runs on the open web stack: TypeScript, Three.js, Lit for interfaces, and Web Audio for sound. Those are its only runtime dependencies, and adding another one needs a written reason. It ships as TypeScript source with one entry point per layer, so a game only bundles the layers it imports.

Layer What it does
kernel The rules contract: apply(state, actor, intent) returns the next state and its events, or a refusal. The random seed lives in the state, and a save is just the list of intents.
runtime A frame clock, a fixed-step loop with interpolation, a store, saved settings, and a profiler.
render A Three.js stage with orthographic and perspective cameras, instanced layers that draw a whole family of things in one call, and picking.
audio Every sound synthesized live, from tones and noise to modal "strike" synthesis, through a typed mixer.
pixel Pixel art written as rows of characters, decoded to palette indices.
nav, physics Path graphs with A*, and flight arcs computed once when a move starts instead of simulated every frame.
art, world, scenes Toon shading, a mesher for soft shapes, crowds, billboards, and a whole park.

Three rules hold it together:

  • Pure layers are pure. The rules, math, pathfinding, and physics never touch the clock, the page, or Math.random. A test fails the build if they try. That's what lets the same rules run in a Web Worker, on a Cloudflare server, and in tests, and always give the same answer.
  • One clock. The CPU and the GPU read the same time, so animation in code and animation in shaders never drift apart.
  • Render on demand. The stage draws only while something moves. Measured idle, it draws zero frames. Extracting it also caught a bug: a frozen clock inside one animation had kept Churn's old renderer running at 72 frames a second, forever, for nothing.

What's left out is just as deliberate. There's no entity-component system, because Churn doesn't need one: state is plain data and systems are plain functions. There's no physics engine either. The usual choices cost 742 KB to 1.17 MB gzipped, and all Churn needs is pieces hopping in arcs, which can be worked out the moment a move starts. And there's no asset loader yet, because nothing we've made loads assets. The pieces, the sprites, and the sounds are all code.

2D is a camera angle

The biggest decision is how Baker Engine does 2D: in the same Three.js renderer as 3D, with the camera fixed looking straight down. That makes it one engine with two render presets, not two engines. We considered a separate Canvas 2D backend and turned it down, because keeping two backends equal is where small engines stall.

The planned pixel preset draws to a small virtual screen, 320 by 180 for example, then scales it up by whole numbers with letterboxing so every pixel stays square. Sprites are drawn from palette textures, so swapping a palette means swapping one texture. Because it's still Three.js underneath, pixel sprites can pick up real lighting and depth, the HD-2D look, at no extra cost. Before any game depends on it, a small Pokémon-style slice will prove it out.

Churn is the first game on it

Churn has depended on Baker Engine since October 19. The first thing it took was a backdrop: a toon-shaded park built entirely in code, with people walking the paths, ducks, a banner plane, billboards running house ads for Spellbox and Baker Games, and Bun, the studio's Dutch rabbit. The chess board now sits on a stone table under a great tree, viewed from a seat at the table through a 36° perspective camera. The isometric and top-down views are still one setting away.

The park loads only when it's needed. It adds 2.6 kB to Churn's first load, and the rest arrives as a 16.9 kB chunk. Pieces landing after a move now follow arcs from the physics layer and turn to face where they're headed using the nav layer.

That was the easy direction: new engine code written for Churn. The next milestone is the harder one. Churn deletes its own copies of what went into the engine (the rules kernel, the stage and its five instanced layers, the audio system) and imports them back, with all 431 of its tests passing the whole way. After that come the multiplayer pieces: the authority that runs a game, the rooms, and the server.

Spellbox keeps its own engine

Spellbox is the other half of where Baker Engine came from. Its fixed-step loop, its profiler, and its pixel-art format were among the first pieces extracted. Spellbox itself isn't moving over, though.

Spellbox is a 2D game all the way down. Its world is drawn on a plain HTML canvas, a hand-written entity-component system runs at 60 Hz with interpolated rendering, and every menu is a web component layered on top. That hand-rolled engine is the point of the project, not something to grow out of, and it's staying. Baker Engine bets on 2D inside a 3D renderer. Moving Spellbox onto it would mean rewriting the thing that makes it Spellbox.

The two can still meet below the renderer. The parts of Baker Engine that never draw anything (the loop and profiler Spellbox donated, the synthesized audio and its mixer, the pure rules kernel) are plain TypeScript that any game could pick up. Ideas travel the other way too. Spellbox's chunked tile caching and its dithered screen transitions are the reference designs for the engine's tilemaps and transitions.

Open source

Baker Engine is open source under the MIT license, and the code is on GitHub at WSB98/Baker-Engine. Use it, fork it, take it apart. Everything it builds on, from Three.js and Lit to our own synthesis library, cuelume, is permissively licensed too, and the repo's provenance ledger credits every source.

Going public also fixes a practical problem. Churn currently links to the engine as a folder sitting next to it on one machine, so a fresh clone of Churn can't build. Tagged releases on GitHub replace that link with a pinned version any machine can install.

A new home for these posts

This is also the first post published on the devlog's new system.

Until now, every post was a hand-built HTML page, and each one had to be listed by hand in four places: the devlog index, the homepage, the related links on other posts, and the sitemap. Five posts in, that's already how things drift.

Now each post is a markdown file, and one JSON file lists them all: title, date, summary, cover, tags, author, and the path to the markdown. At build time, that list becomes everything else: each post's page, the index, the latest posts on the homepage, the related links, and the sitemap. Pages still arrive in your browser as complete HTML. Nothing renders in JavaScript, and there's nothing new to download.

It's built to stay small and safe:

  • Build time only. Markdown becomes HTML once, when the site is built. The live site serves the same static files it always has, so readers run no new code and there's nothing new to attack.
  • No raw HTML in posts. The markdown renderer has HTML switched off and only follows web and relative links, so a post can't smuggle in a script. Figures, tables, video clips, and color swatches come from a short list of conventions the renderer knows how to draw.
  • It fails loudly. If a post's file is missing, its path points outside the posts folder, or its fields are wrong, the build stops instead of shipping half a page.

Kinds and tags (devlog, announcement, patch notes, and each game) let you filter the index, and it paginates once there are more posts than fit on a page. Every post also names its author. That's the first step toward something bigger: an articles section where friends of the studio can write too. That might need a real publishing system one day. For now, a folder of markdown and one JSON file is the smallest thing that works, and it's shaped so the next step won't mean starting over.

What's next

  • Churn finishes its move: its own copies deleted, the engine imported.
  • The pixel preset, proven on a small Pokémon-style slice.
  • A first tagged release, so games can pin a version instead of a folder.