A fast 2D game engine
for everywhere you ship
Estella pairs a first-class TypeScript SDK with a C++/WebAssembly rendering core, and ships a visual editor. One project builds for the web, the desktop, WeChat MiniGames, playable ads, and native Android / iOS.
Free and open source· Apache-2.0· Windows & macOS
Get the editor
Served from the project's Cloudflare mirror — the same one the editor's update check reads, and measured at 3.0 MB/s against GitHub's 2.0 from Shanghai. Every release is on GitHub too.
Intel Macs and Linux: build from source. Every build, checksum and release note lives on the releases page.
Installers are unsigned for now, so Windows SmartScreen and macOS Gatekeeper will ask before the first launch.
An engine that behaves like an instrument
A native rendering core, a typed SDK, and an editor that edits your project — not a pile of JSON.
Fast by construction
A C++ rendering pipeline compiled to WebAssembly, batching draws behind one command producer — not interpreted JavaScript.
Type-safe SDK
defineComponent, defineSystem, Query, Commands, Res — the compiler knows what a system reads and writes.
Data-oriented core
An ECS with one cache-friendly component store, change detection, and systems scheduled over the archetypes that match.
A real editor
Scene graph, inspector, tilemaps, particles, animation, prefabs, visual event binding, and plugins that extend all of it.
Ships to six targets
Web, desktop, WeChat MiniGames, single-file playable ads, and native Android / iOS — an arm64 app on an embedded Dawn, not a WebView.
Content hot-update
Content-addressed asset groups a shipped game downloads and applies atomically, verified by digest and rolled back if wrong.
Built for the platform you name
Write game logic in TypeScript
Declare what a system needs; Estella runs it over the matching entities each frame.
const Speed = defineComponent('Speed', { value: 200 }); addSystem(defineSystem( [Res(Time), Query(Mut(LocalTransform), Speed)], (time, query) => { for (const [entity, transform, speed] of query) { transform.position.x += speed.value * time.delta; } } ));