Monorepo for the murow game engine — a lightweight TypeScript framework for server-authoritative multiplayer games with WebGPU rendering.
- murow — Core game engine (ECS, networking, protocol, game loop)
- murow/webgpu — WebGPU 2D/3D renderer (bundled with murow)
- murow/netcode — Opinionated multiplayer layer: snapshot sync, prediction with rollback, interest management (bundled with murow)
npm install murowThe WebGPU renderer and netcode layer are included by default:
import { GameLoop, PrefabBucket, World, defineComponent } from 'murow';
import { WebGPU2DRenderer, WebGPU3DRenderer, d } from 'murow/webgpu';
import { GameServer, GameClient, defineIntents } from 'murow/netcode';3D glTF Models with Animation
import { GameLoop, PrefabBucket } from 'murow';
import { WebGPU3DRenderer } from 'murow/webgpu';
// Declare every spawnable thing up-front. Typed ids, parallel load.
const prefabs = new PrefabBucket('3d')
.add({
type: 'gltf',
id: 'hero',
src: '/character.glb',
animations: ['Idle', 'Run'],
metadata: { scale: 0.01 },
});
await prefabs.load();
// Renderer self-sizes from the bucket — no magic numbers.
const renderer = new WebGPU3DRenderer(canvas, { prefabs, maxInstances: 100 });
await renderer.init();
const hero = prefabs.get('hero'); // typed as GltfPrefab
const instance = renderer.addInstance({
model: hero,
position: [0, 0, 0],
scale: hero.metadata.scale,
});
instance.play?.(hero.animations.Idle, { loop: true }); // typed clip name
renderer.camera.setPosition(3, 1, 3);
renderer.camera.setTarget(0, 0, 0);
const loop = new GameLoop({ tickRate: 20, type: 'client' });
loop.events.on('render', ({ alpha }) => renderer.render(alpha));
loop.start();GPU Compute + Zero-Copy Rendering
import { WebGPU2DRenderer, d, std } from 'murow/webgpu';
const renderer = new WebGPU2DRenderer(canvas);
await renderer.init();
const Particle = d.struct({
posX: d.f32, posY: d.f32,
velX: d.f32, velY: d.f32,
life: d.f32
});
// Physics runs on GPU
const compute = renderer
.createCompute('physics', { workgroupSize: 256 })
.buffers({
particles: { storage: d.arrayOf(Particle, 10000), readwrite: true },
config: { uniform: d.struct({ deltaTime: d.f32, gravity: d.f32 }) }
})
.shader(({ particles, config }, { globalId }) => {
const p = particles[globalId.x];
p.velY = p.velY + config.gravity * config.deltaTime;
p.posY = p.posY + p.velY * config.deltaTime;
})
.build();
// Render directly from compute buffer (zero-copy)
const render = renderer
.createGeometry('particles', { maxInstances: 10000, geometry: 'quad' })
.instanceLayout({ dynamic: { posX: d.f32, posY: d.f32, velX: d.f32, velY: d.f32, life: d.f32 } })
.fromCompute(compute, 'particles')
.build();
compute.dispatch(10000);
render.render(); // GPU → GPU, no CPU overheadFull example: benchmarks/renderer/programs/gpu-particles.ts
- Murow Core Package — ECS, networking, protocol, game loop
- WebGPU Renderer Package — 2D/3D rendering, compute shaders, TypeGPU
- Netcode Package — Snapshot sync, prediction, interpolation, plugins
# Install dependencies
bun install
# Build all packages
bun run build
# Run tests
bun run test
# Publish (runs tests + builds)
bun run pubMIT