Zero build, zero dependencies
ES5-ish IIFEs loaded in script order. Nothing is imported, nothing is bundled, nothing is transpiled. Open the folder over any static host and it runs.
Vanilla JS · zero build · zero dependencies
Aethelgard Engine is the engine behind Aethelgard: Chronicles of Fate, a deep single-player fantasy RPG that runs entirely in the browser. There is no bundler, no framework and no package to install — just HTML, CSS and ES5-ish JavaScript, loaded in script order, fully offline.
Free & fully offline — nothing to install. The Android .apk is sideload-only (allow installs from unknown sources).
What's inside
Every design choice here is aimed at the same goal: a game engine small enough to read end to end, and open enough that a mod can reshape it without touching the core.
ES5-ish IIFEs loaded in script order. Nothing is imported, nothing is bundled, nothing is transpiled. Open the folder over any static host and it runs.
Everything under js/data/ is pure data with no side effects. That is exactly what makes mods simple: a mod supplies the same shapes and they merge.
The whole game paints through Game.sprite(key, fallback). An art overhaul is a graphics pack that registers images for sprite keys — anything it doesn't cover keeps its emoji.
No audio files ship at all. js/music.js improvises every mood live from a scale through the Web Audio API, sitting under the sound effects.
Drop a .js file in mods/ and the engine finds it at boot. Mods declare their impact, are compatibility-checked, and can be switched off with safe mode.
No virtual DOM. Each view returns an HTML string that render() assigns to #view-container — so overriding a view is one function. Cheap number updates are split from panel rebuilds.
How it works
Every file is an IIFE that hangs its public functions off a single global, window.Game (aliased G). Load order is the order of the <script> tags in index.html. That is the whole dependency system — and it is why a mod can add a chapter, a class or a language without the core changing.
// the module contract — every logic file looks like this
/* js/graphics.js — sprite packs and image rendering */ "use strict"; (function () { const G = window.Game; /* the whole game draws its art through this one call */ G.sprite = function (key, fallback) { const s = G.SPRITES[key]; return s && s.src ? img(s) : fallback; // emoji if no pack }; G.registerGraphicsPack = function (pack) { G.GRAPHICS_PACKS[pack.id] = pack; // a mod calls this }; })();
A graphics pack is just a mod that registers sprites for the engine's sprite keys — enemy.<id>, class.<id>, item.<id>, loc.<id> and so on.
No install step. Any static host works — a tiny local server is enough to get the service worker and PWA install.
# extract/clone the whole folder first git clone https://github.com/drsamonline/aethelgard-rpg-engine.git cd Aethelgard-Engine python3 -m http.server 8080 # → http://localhost:8080
Plain Node and Python — there is nothing to install. CI runs the syntax check, JSON validation and the three suites on every push.
node --check js/core.js node test6.js && node test7.js && node test8.js node tools/verify.js # end-to-end node tools/sims.js 1 12 # 12 simulations node js/validate.js # content validator python3 tools/static-audit.py .
Add content to js/data/, or drop a mod into mods/ and change nothing in the core.
window.AETHELGARD_MODS =
window.AETHELGARD_MODS || [];
window.AETHELGARD_MODS.push({
id: "my-mod", name: "My Mod",
version: "1.0.0", author: "You",
impact: "additive" // additive | story | overhaul
});
window.Game (aliased G). Nothing is imported.boot.js runs last.render() assigns it to #view-container.updateStatus() is cheap and updates numbers; renderView() rebuilds the panel. The 3-second tick only rebuilds tabs in G.LIVE_TABS.G.store wraps localStorage with an in-memory cache, so a save is durable the instant it is written — a save is about 13 KB.aethelgard_v7_slot1 vs aethelgard_v7_villain_slot1), so two games never overwrite each other.The four subsystems
The engine's runtime concerns live in their own files, wired together only through G. Each one is small enough to hold in your head.
js/ui.js · js/views/*.js · js/graphics.js
Each view returns an HTML string. render() writes it into #view-container; expensive lists (the wiki) are paged rather than drawn whole, and only live tabs rebuild on the tick.
js/ui.js · js/core.js
The shell owns tabs, the status bar, modals, settings and the mod panel. Keyboard shortcuts work throughout — Esc closes any overlay, 1–9 switch tabs — and overlays trap Tab focus.
js/music.js · js/core.js
Zero audio files. The soundtrack is improvised live from a scale via the Web Audio API, scaled down under the effects and gated by the Music switch and volume slider. Volume is clamped so no stored value can break a parameter.
js/maps.js · js/world.js
The world map and each region's local map are drawn from data: nodes come from G.WORLD_POS and edges from G.WORLD_LINKS. A mod can add a region; if it has no map position it is skipped rather than crashing the render.
See it running
The shipped game is the engine's reference build — a world map, turn-based combat with several enemies, and an in-game diagnostics panel you can open from Settings.
Get started
Play it, run it locally, or read how it is built before you change a line.
node tools/verify.js for the end-to-end check.python3 tools/static-audit.py . for the static audit.FAQ
<script> tags and share one global, window.Game. There is deliberately no single-file bundle.index.html from an extracted folder works; serving the folder over http:// (any static host, or python3 -m http.server) additionally enables the service worker, so it installs to a home screen and runs fully offline.js/data/ (enemies, classes, quests, items, wiki entries), or drop a .js file into mods/ — the engine discovers it at boot. Content mods apply instantly; story and overhaul mods only load on a fresh start, so a running save is never reshaped underneath you.Game.sprite(key, fallback), so an overhaul is drop-in — and anything a pack does not cover keeps its emoji, so partial packs work..github/workflows/ci.yml) runs a syntax check on every JS file, JSON validation, and three Node test suites on every push and pull request, followed by the static audit. Locally there is also an end-to-end verifier, twelve gameplay simulations, and a content validator (also available in-game under Settings → Diagnostics).