Branching dialogue
A per-line speaker, ten expressions and a portrait that changes with the mood of the line. Choices branch, and @label/@goto let a branch rejoin the main line.
A visual-novel engine · MIT
A small, dependency-free visual-novel engine for the browser.
Plain JavaScript · one stylesheet · no build step · works offline
Free & fully offline — nothing to install. The Android .apk is sideload-only (allow installs from unknown sources).
What it does
Saga runs from a static file server with nothing installed. It reads your story from plain .scene text, keeps the state, and plays it — and it was built to make Aethelgard — The Books.
A per-line speaker, ten expressions and a portrait that changes with the mood of the line. Choices branch, and @label/@goto let a branch rejoin the main line.
Options with a requirement like [Bonds 2] are shown greyed-out with the reason, never hidden. You always know what you missed.
Every choice moves one of four hidden counters — Mercy, Truth, Bonds or Conviction. The ending is their shape, resolved from Dawn to Hollow.
The Diary fills itself as you play — people, places, lore, decisions and deeds. The Backlog scrolls back through everything said. Intro cards greet a character the first time they appear.
The score is generated with the Web Audio API — there are no audio files at all. Save to localStorage, and export or import a run as a JSON file.
Drop a folder in mods/ and the engine finds it. Mods add chapters, cast and regions — and can never break a running game: every load is wrapped, conflicts are refused visibly, and order is deterministic.
How it works
The engine ships no story of its own. To make a game you replace three things and nothing else changes.
Scene files are read over http, so serve the folder. Any static server works — no install, no build, no dependencies.
# from the project folder
python3 -m http.server 8000
# open http://localhost:8000
A chapter is a .scene file you could write in a notebook. Add one line to scenes/manifest.js and it appears.
@bg road
narrate A road at dusk.
keeper warm "I am."
choice
* "Tell the truth." => truth+1
Your cast and regions in js/chars.js, your chapters in scenes/, your art in assets/sprites/. The engine never needs to know your chapter ids.
js/chars.js your cast
scenes/ your chapters
assets/sprites/ your art
See it
Illustrative mock of the engine's stage — background, portrait, dialogue box and choices. Not a screenshot.
The engine ships with a short demo story — two chapters and all seven endings — so you can see the format and the shape resolver at work.
Space/Enter, pick choices with Tab + Enter.D opens the Diary, B the Backlog, S saves, Esc closes.The demo is MIT, like the engine. Aethelgard — The Books — a visual novel in three books, 36 chapters and seven endings — is the game Saga was built to make; its artwork and story are © Dr. Sohil Momin, all rights reserved.
Get started
# 1. clone
git clone https://github.com/drsamonline/saga-vn-engine.git
cd AethelgardVN
# 2. serve (nothing to install)
python3 -m http.server 8000
# 3. open http://localhost:8000
If you change any scene, regenerate the embedded bundle so the engine still boots from file://:
python3 tools/make-bundle.py
FAQ
python3 -m http.server, npx serve, nginx or GitHub Pages.file:// as well as over http. The classic path is still to serve the folder over http.js/chars.js, your chapters in scenes/ (with a manifest.js listing them), and your art in assets/sprites/. Nothing else needs to change.js/endings.js is pure data — rewrite it freely.