A TypeScript-first 2D engine for games and interactive apps. Explicit scene graph, WebGPU/WebGL2 rendering, native physics, spatial audio, and a strict type system — measured and verified, not just claimed.
Pre-1.0. The public API is under active design — minor versions may include breaking changes. Pin exact versions in downstream projects.
1.0.0marks the first stable API contract.
| Package | Description |
|---|---|
@codexo/exojs |
Core runtime — scene graph, rendering, audio, UI, serialization |
@codexo/exojs-physics |
Native 2D physics — shapes, constraints, TGS-Soft solver |
@codexo/exojs-particles |
GPU-driven particles (WebGPU compute) with CPU fallback |
@codexo/exojs-tilemap |
Format-independent tilemap runtime and object layers |
@codexo/exojs-tiled |
Tiled JSON adapter and scene-graph conversion |
@codexo/exojs-tilemap-physics |
Static physics colliders from tilemap collision geometry |
@codexo/exojs-aseprite |
Aseprite adapter — sprite sheets, tags, frame direction/repeat, slices |
@codexo/exojs-ldtk |
LDtk adapter — multi-world levels, entities, structured field values |
@codexo/exojs-lighting |
Forward, normal-mapped 2D point lighting inside the sprite batch |
@codexo/exojs-pathfinding |
A* with jump-point search over weighted grids and waypoint graphs |
@codexo/exojs-audio-fx |
Audio effects — biquad filters, analyser, beat detection, worklets |
@codexo/exojs-react |
React bindings — run an ExoJS Application inside React components |
Rendering
- WebGPU-first with automatic WebGL2 fallback; force either backend with one option
- Drawables:
Sprite,AnimatedSprite,NineSliceSprite,RepeatingSprite,Graphics,Text(SDF),BitmapText,Video - Rendering composition:
RenderTexture,RenderPipeline, filter chains, visual masks, cache-as-bitmap - Immediate-mode rendering: one-off
drawGeometryand instancedRenderBatchcollapsing to a single draw call - Linear and radial gradients, pixel snapping, view/camera helpers (
follow, shake, zoom, bounds clamp) - Custom sprite materials (
SpriteMaterial) with your own GLSL/WGSL fragment stage, plus built-inDropShadowFilter,DisplacementFilter, colour matrix and blur filters @codexo/exojs-lighting: normal-mapped point lights shaded in the sprite fragment stage — no extra pass, no extra draw call- Render stats with GPU memory accounting (
gpuMemoryBytes, texture/buffer upload bytes)
Text
- SDF text with Unicode-safe layout (
Intl.Segmentergraphemes, bidi runs, soft wrapping) and browser-native shaping for complex scripts - Multi-stop gradients with an angle,
oblique/small-capsvariants, underline and strikethrough,maxLineswith a configurable ellipsis,textTransform,tabSize BitmapTextfor prebuilt atlases; both text kinds batch with sprites
Scene & UI
Application,Scene,SceneDirector— one active scene withchange/restore/preload/unloadnavigation,pause/resume, and fade, slide, or cross-fade transitions (or your ownSceneTransition)scene.ui— screen-fixed widget layer withLabel,Panel,Button,ProgressBar,Stack,ScrollContainer,Tooltip, and anchoring- Keyboard focus through
app.interaction—focus,blur,focusNext/focusPreviousTab traversal, and scoped focus traps for modals - Exact picking: rotated nodes hit-test as oriented boxes, or set a local
hitArea(Circle,Ellipse,Polygon,Rectangle) for round buttons and irregular shapes app.jobs— a frame-budgetedJobSchedulerthat runs generator jobs a slice per frame, so world generation or batch pathfinding never blocks a frame and never needs anasyncupdate- Write your own
SceneTransitionagainst the same conformance harness the built-in fade, slide and cross-fade use
Physics (@codexo/exojs-physics)
- Circles, boxes, capsules, and convex polygons; static, kinematic, and dynamic bodies
- SAP broadphase, manifold narrow-phase, warm-started TGS-Soft solver (sub-stepped, stable to 20+ box stacks)
- Contact graph, collision events, spatial queries; allocation-free per step (V8-sampler verified)
- Sleeping islands, contact modifiers, joints; measured against matter.js, planck.js and Rapier in
packages/exojs-bench - Scene-graph binding and a
/debugdraw subpath
Pathfinding (@codexo/exojs-pathfinding)
- One search core — A* with jump-point pruning — over weighted grids (top-down worlds) and waypoint graphs (platformers, abstract graphs)
- Deterministic and allocation-free per query; plain logic with no scene node, renderer or registration step
Audio
- Voice capability matrix across
Sound,AudioStream, andAudioGenerator - Spatial panning, audio sprites, frequency and waveform analysis,
BiquadEffectfilters @codexo/exojs-audio-fx:AudioAnalyser,BeatDetector, worklets, and DSP helpers
Assets & Storage
- Typed
Loaderwith a declarative asset catalog (Assets.from,Asset.type,defineAsset) and a de-duplicatingLoadingQueue - Binary asset containers (
loader.loadContainer) for bundled distribution - Key-value persistence:
WebStorageStore(localStorage/sessionStorage),IndexedDbKeyValueStore(structured-clone, binary-safe),MemoryStore(tests/ephemeral)
Serialization
Scene.serialize/deserializecaptures scene structure — nodes, drawables, UI, tilemapSerializationRegistryandPrefabfor templates; pairs with anyKeyValueStorefor save-slot persistence
Architecture
noUncheckedIndexedAccess+exactOptionalPropertyTypesacross the full codebase — zeroas any, zerots-ignore- Ordered
SystemRegistry(app.systems,scene.systems) with deterministic tick bands - Deterministic disposal via
Destroyable/DisposalScope; all managers are app-owned, not process singletons
Scaffold a new project with one command:
npm create exo-app@latest my-gameOr pick a template explicitly:
npm create exo-app@latest my-game -- --template minimal
npm create exo-app@latest my-game -- --template game-starter
npm create exo-app@latest my-game -- --template audio-reactiveThen:
cd my-game
npm install
npm run devnpm install @codexo/exojsExoJS ships as ESM — use import syntax with a modern bundler or runtime. A prebuilt IIFE bundle (dist/exo.iife.js, global Exo) is included for CDN and script-tag usage.
Optional packages install independently — add only what your project needs:
npm install @codexo/exojs-physics
npm install @codexo/exojs-particles
npm install @codexo/exojs-tilemap @codexo/exojs-tiled @codexo/exojs-tilemap-physics
npm install @codexo/exojs-aseprite @codexo/exojs-ldtk
npm install @codexo/exojs-lighting @codexo/exojs-pathfinding
npm install @codexo/exojs-audio-fx
npm install @codexo/exojs-reactimport { Application, Color, Graphics, type RenderingContext, Scene, type Seconds } from '@codexo/exojs';
class HelloScene extends Scene {
private readonly box = new Graphics();
override init(): void {
this.box.fillColor = Color.white;
this.box.drawRectangle(-32, -32, 64, 64);
this.box.setPosition(this.app.width / 2, this.app.height / 2);
this.addChild(this.box);
}
override update(delta: Seconds): void {
this.box.rotate(delta * 45);
}
override draw(context: RenderingContext): void {
context.render(this.root);
}
}
const app = new Application({
scenes: { HelloScene },
canvas: { width: 800, height: 600, mount: document.body },
clearColor: Color.black,
});
await app.start(HelloScene);See the Guide for physics, audio, UI, and more, or browse the live examples.
Directional work toward the 1.0.0 API freeze. Priorities may shift — nothing here is a release commitment.
- Backend selection by class (
backends: [WebGl2Backend]) and compile-time defines for single-backend builds - Typed shader uniform blocks shared between GLSL and WGSL
@codexo/exojs-cli—exo serve,exo create,exo doctor, asset packing- Rich text with style spans and inline icons
- Worker-backed jobs on the same
Jobhandle asapp.jobs - Post-processing passes (bloom, tone mapping, grading) on
RenderPipeline - Platform adapters for Worker and headless runtimes
- Final pre-1.0 API audit and stabilization pass
ExoJS is measured against Pixi, Phaser and Excalibur for rendering, and against matter.js, planck and Rapier for physics, on one reference machine per release. On the current reference run:
- Level with Pixi on plain sprite scaling, and 2x or better ahead of it on WebGPU filter chains.
- About 7x behind Pixi on masked clipping — the widest rendering loss on that machine.
- In physics, ahead of the pure-JS peers on a 1000-body box stack (roughly 3x over matter.js, 17x over planck) and behind Rapier's Rust/WASM solver on five of six scenes.
The full matrix, the mechanism behind each row, the rows that were left out and the steps to reproduce all of it are on the benchmarks page.
Application auto-selects the best available backend. Force one when needed:
new Application({ backend: { type: 'webgpu' } });
new Application({ backend: { type: 'webgl2' } });
new Application({ backend: { type: 'auto' } }); // defaultPrerequisites: Node 24 (.nvmrc; devEngines in package.json refuses any other major) and
pnpm (packageManager pins the version; with Corepack enabled, or any installed pnpm 10+, it
switches itself).
pnpm bootstrap:dev # dependencies, git hooks, every build, the bench competitors, a Chromium
pnpm doctor # what is missing, and the command that fixes itpnpm bootstrap alone is what CI runs: dependencies and the build tooling, nothing else. It
installs with scripts disabled, so whether a clone ends up with git hooks depends on whether pnpm
ran an install of its own first - and it builds nothing. pnpm doctor reports the actual state
either way.
pnpm typecheck
pnpm lint
pnpm test
pnpm build:all # core plus every extension package
pnpm verify:package
pnpm clean:artifacts # what a local test, benchmark or release run left behindPackage-internal imports use Node package.json#imports subpath imports: ./X for the same directory, #dir/X for any other path in the same package, and the public bare specifier (@codexo/exojs) across packages. See CONTRIBUTING.md for the full import policy, per-package commands, and the shared @codexo/exojs-config tooling. Building the library requires TypeScript 6.
This repository uses pnpm workspaces (site/ is a workspace package). Use root-level commands as the source of truth — avoid running pnpm install inside site/ directly:
pnpm bootstrap
pnpm site:build
pnpm site:build:api- Repository: https://github.com/Exoridus/ExoJS
- Issues: https://github.com/Exoridus/ExoJS/issues
- Changelog: CHANGELOG.md
