Web minimal bundle
The Web minimal bundle is the smallest possible Forge bundle: one screen, one loop, four unlockable tools, every system wired through a GameManager spine. Pure DOM, no canvas, no game engine. About 600 lines of TypeScript across 9 source files plus 5 composed system components.
Use this when you want to learn the harness. The whole thing reads in 10 minutes and demonstrates every pattern the bigger bundles use, without the visual novel or 2D platformer noise.
What ships
Section titled “What ships”- 9 TypeScript files: GameManager spine, scenes (boot / tutorial / complete), tools registry, goals registry, page chrome.
- Single-screen loop: hammer the anvil 100 times. Tools unlock at 25 / 50 / 75 strikes. Saves on every strike.
- 4 unlockable tools (hammer, bellows, tongs, chisel) with per-tool cooldowns and combo behavior.
- One background plate (
bg_tutorial_forge.png) from the asset bank and 4 tool icons.
Systems wired in
Section titled “Systems wired in”The bundle ships with 5 systems registered:
- EventBus carries strike, unlock, and goal-reached events.
- StateMachine drives boot / tutorial / complete.
- SaveSystem persists progress to localStorage (debounced 2 seconds).
- SettingsManager renders audio / video / accessibility menus.
- ThemeManager ships three themes (default dark, light, high contrast) via CSS variables.
No AudioManager (a single click is too small a loop to justify a mixer). No InputManager (click plus keyboard activator is overkill for one button). No CharacterController (no avatar in the world). No ProjectConfig (no constants worth extracting at this scale).
Controls
Section titled “Controls”| Input | Action |
|---|---|
| Left click on the anvil | Hammer strike (+1, builds heat) |
Space / Enter (with anvil focused) | Hammer strike |
| Click bellows (unlocks at 25) | +30 heat. Hot strikes count double |
| Click tongs (unlocks at 50) | 5s combo grip. Rapid strikes chain a streak bonus |
| Click chisel (unlocks at 75) | Instant +5 strikes |
R (after completion) | Restart the tutorial |
Mechanics
Section titled “Mechanics”| System | Drives |
|---|---|
| Hammer (always) | +1 strike per anvil click. Builds heat by 4 |
| Heat | Decays 6/sec. At >= 50, anvil glows + strikes count double |
| Bellows (>= 25) | +30 heat per click. 1.5s cooldown |
| Tongs (>= 50) | 5s grip. Consecutive strikes within 600ms add a streak bonus (+0.5/streak, capped at +5). 8s cooldown |
| Chisel (>= 75) | Instant +5 strikes (does not stack heat / combo). 10s cooldown |
| Save | Every strike autosaves to localStorage (debounced 2s) |
| Goals | Fire goal_reached once each as hammerCount crosses target |
Extending
Section titled “Extending”Three exercises, each takes 5 to 15 minutes. The first one teaches the pattern; the rest are obvious from the source comments.
Add a tool: drop a 256x256 PNG into public/icons/icon_<name>.png. Add an entry to src/data/tools.ts:
{ id: 'rivet', label: 'Rivet driver', icon: '/icons/icon_rivet.png', kind: 'chisel', // reuse an existing kind for free behavior unlockAt: 60, cooldownMs: 6000, hint: 'Drives a rivet. Instant +3 strikes.' }Tools render in the bottom palette automatically. To wire new behavior, add a ToolKind variant in tools.ts and a matching branch in handleToolClick() in tutorial-scene.ts.
Add a goal: append to src/data/goals.ts in ascending target order:
{ id: 'masterpiece', target: 200, message: 'A masterpiece is forged.', banner: 'Masterpiece complete' }The highest target ends the run and transitions to the complete scene. Intermediate goals fire goal_reached and show the banner if banner is set.
Add a theme: append to THEMES in src/systems/theme-manager.ts. Switch via devtools: __forge.theme.setTheme('your-id'). The page reskins via CSS variables on <body>.
Why this bundle exists
Section titled “Why this bundle exists”The Web minimal bundle is the reference implementation when building any new bundle that fits on one screen. If you’re going to read one bundle from top to bottom to learn how Forge composes systems, this is the one. The bigger bundles add a lot of game-specific logic. This one is just the spine.