A point-and-click adventure engine built on Phaser 3.
Caper provides the reusable systems for a Humongous-style adventure game: walking, one-click interaction, declarative props, inventory, NPC/cast behavior, weather, cutscenes, transitions, UI helpers, and engine-owned state primitives.
Caper is published to JSR and depends on Phaser 3 as a runtime dependency.
Deno:
deno add jsr:@caper/engineNode / npm (Vite, webpack, etc.):
npx jsr add @caper/engine
npm install phaserEverything is imported from the package entry point:
import { AdventureScene, createAdventureGame } from "@caper/engine";
const game = createAdventureGame({
scenes: [MyScene],
// ...game config
});See the public API for the full list of exports.
src/ is organized into capability slices. The folders are internal — everything is re-exported from the package root
(@caper/engine) — but each slice is "everything for one capability," including the boot-time registry the game fills.
Slice (src/) |
What it owns | Modules |
|---|---|---|
core/ |
cross-cutting primitives | conditions, random, perspective |
scene/ |
the shell a game extends + boots | AdventureScene, EngineScene, createAdventureGame, SubsceneStack, transitions |
movement/ |
locomotion & navigation | WalkController, pathfinding, Fidget, IdleCharacter, behaviors/ |
interaction/ |
the one-click verb loop + props | HotspotManager, PropEngine |
inventory/ |
carried items | InventoryLayer, itemDef, ContentRegistry |
cast/ |
NPCs + the ensemble director | NPC, CastDirector, CastRegistry |
characters/ |
playable characters, switching, outfits | CharacterRegistry, CharacterSwitcher, Wearables, portraits |
cutscene/ |
scripted sequences + on-screen speech | Cutscene, CutsceneRunner, cutsceneActor, DialogueBubble, SuccessMessage |
environment/ |
the ambient world | WeatherLayer, NightLayer, CritterHelper |
state/ |
persistent reactive store | Store |
assets/ |
load conventions + engine asset registry | assetLoading, EngineAssets |
ui/ |
shared chrome + dev tools | UIHelper, FullscreenButton, DebugOverlay, SceneEditor |
Start with docs/index.md.
Useful entry points:
To work on a game and the engine side by side without publishing, map the package in your game's deno.json:
{
"imports": {
"@caper/engine": "../Caper/mod.js"
}
}deno task ci # lint + fmt:check + check + dts:check + test
deno task test
deno task dts # regenerate the bundled mod.d.ts from JSDocThe engine is authored in JavaScript with JSDoc. To ship real types (and satisfy JSR fast-check without
--allow-slow-types), deno task dts compiles the JSDoc to declarations with tsc, rolls them into a single
self-contained mod.d.ts, and mod.js points at it via // @ts-self-types. deno task dts:check (run in CI)
regenerates it and fails if the committed mod.d.ts is stale, so types can never drift from the source.
deno task dts && deno publishRequires the @caper scope on JSR. Pushing a v* tag runs this via .github/workflows/publish.yml.
MIT