Getting started
Set up#
Ferret is a pnpm workspace (packages/*, examples/*, tools/*). Babylon.js, Havok, recast-navigation and hls.js come in as development dependencies; esbuild bundles everything, so there is no other build step.
pnpm install
pnpm typecheck # tsc --noEmit over every package
pnpm test # vitest: unit tests of every package
pnpm build # tools/build.mjs: dist/web, dist/beroun, dist/lib
pnpm serve # tools/serve.mjs [port] [dir]: a static server for dist/web
pnpm test:e2e runs the Playwright tests against the built samples. pnpm docs builds this site into dist/docs.
What a build produces#
| Output | What it is |
|---|---|
dist/web/<sample>/ | each sample as a web page: open index.html through pnpm serve |
dist/beroun/<sample>/ | the same sample as an OS Beroun app: manifest.json, icon.svg, the page and the bundle |
dist/lib/ferret.js | the engine as one script that exposes Ferret and BABYLON globals |
dist/lib/ferret/ | the release: engine, Babylon, loaders and the WASM files (Havok, recast), for apps that load the engine from OS Beroun's shared library |
The samples show what each part is for: greybox (a character in a grey-box level), npc (NavMesh walkers, doors, levers), showcase (media, UI, transformations) and physics (the realistic-physics capabilities).
A first game#
A game is a canvas, createGame, a level and a start call. This is the shape of examples/greybox:
import { createGame } from '@ferret/game';
import { PlayerController, spawnCharacter } from '@ferret/character';
const game = createGame({
canvas: document.getElementById('c') as HTMLCanvasElement,
storageKey: 'my-game', // one key per game in a browser
input: { actions, bindings }, // see "Input and UI"
});
const { world, platform, scene } = game;
// ...build the level's meshes into `scene`; static meshes with checkCollisions are solid
const { entity } = spawnCharacter(world, 'hero', new BABYLON.Vector3(0, 1, 0));
const player = entity.add(PlayerController, { camera: { view: 'first' } });
game.display.trackCamera(player.rig.camera); // the field of view follows the screen's aspect
world.events.on('character:jump', () => platform.log('jump'));
game.start(); // render loop + fixed-step simulation
createGame gives you the platform, the Babylon engine and scene, the World, the Display, the InputService and the PhysicsService. Anything else is a service you add to the world.
Where to go next#
- The world: entities, components, services, events and the fixed step.
- Levels and packages: how a level is described and loaded.
- Physics overview: how to get realism where a game wants it.
- Commands: every debug command a tool or an AI agent can call.
Build this documentation#
pnpm docs # dist/docs
pnpm serve 8080 dist/docs # then open http://localhost:8080
The guides are the Markdown files in docs/; the API pages are read from the TypeScript sources. See Writing the docs.