# Monogatari: a visual novel engine that treats your game as a web page

> Monogatari is a TypeScript engine for interactive fiction that ships as a web page first, with a script file you edit, custom elements for the interface, and packaging for desktop and mobile on top of it. The trade is that you write a game with the tools of the web, and the engine asks you to serve it over HTTP rather than open it from disk.

**Monogatari/Monogatari** — Monogatari is a simple web visual novel engine, created to bring Visual Novels to the web.

- Repository: https://github.com/Monogatari/Monogatari
- Website: https://monogatari.io/
- Stars: 889 · Forks: 128
- Language: TypeScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/monogatari-monogatari

## The web page comes first, and that choice propagates

The most important line in the documentation is a statement of orientation rather than a feature list. With Monogatari, your visual novel is a web page first and a game later.

That ordering has consequences that run through the whole engine. Responsiveness, the fact that your game will adapt to any screen or device size, comes first because it is a property of the document rather than something added later. The documentation says you do not necessarily have to think of your game this way as well, but that you will certainly take the most out of Monogatari if you do.

The topic list on the project record names the mechanism precisely: web-components and custom-elements, alongside visual-novel-engine, interactive-storytelling, progressive-web-app and text-based-adventure. So the engine is built on custom elements rather than on a monolithic set of DOM manipulation calls, and the visual novel surface is a set of web components you can extend.

The engine is written in TypeScript with full type definitions shipped for the engine, the actions and the components, and the package manifest points at those types directly. That is a meaningful difference from most engines in this space, where the script format is a bespoke text language with no type checking at all.

The authoring model follows from the same orientation. The workflow the documentation describes starts with playing the sample game, then opening the directory with your editor, then opening script.js and finding the variable called script, where all the dialogs you saw are a simple list. You change a dialog, save the file, reload the game, and play it again.

The whole loop is: make changes, save, reload, try and repeat. That is a web development loop applied to narrative, and it is why the engine feels different from the ones where you click a visual editor and export a binary.

## Serving over HTTP is not an optional detail

There is a warning in the setup section that anyone evaluating this needs to read carefully, because it determines your entire local development setup.

You can open your game by just double-clicking its index.html, but several of Monogatari's features, and the documentation names them, only work when the game is served over HTTP instead of opened straight from disk. The named features are offline support and service workers, asset preloading, and anything that loads files through fetch.

That last one is the reason. A file:// page cannot make a fetch request the way a served page can, and the browser's security model blocks it. So the documented recommendation is to run a small local web server while developing, and three one-liners are given:

```bash
npx serve                # Node.js
bunx serve               # Bun
python3 -m http.server   # Python 3
```

Any of them will do, and the documentation says you do not need anything fancy. Visual Studio Code users are pointed at the Live Server extension, which does the same thing with a single click.

It is a small constraint, but it is the kind that trips people up. A first-time user who opens index.html from the download and finds that images do not appear will conclude the engine is broken, when the actual cause is the file:// origin. The engine is designed around a served origin, and the service worker that makes the game installable and playable offline is precisely the thing that cannot work from disk.

The toolchain in the repository confirms the direction of travel. There is a .nvmrc, a bun.lock rather than a package-lock or a pnpm lockfile, a build.ts file at the root, and the package scripts call bun directly: the build script runs clean, then lint, then check, then bun build.ts, and the check script is bunx tsc --noEmit. The engine is built with Bun and type checked with the TypeScript compiler.

## The script file is a list of dialogs you edit directly

The workflow section describes the authoring loop in a way that makes the data model clear, and it is worth reading the steps as a description of the format.

You open the index.html file inside the directory you just unzipped and play the sample game through. Then you open the directory with your editor to start making changes. Then you open the script.js file, find the variable called script, and change one of the dialogs. The documentation says that as you will see, all the dialogs you just saw are just a simple list in there, and it links to the documentation on script and labels for more.

After that the loop repeats: save the file and reload the game, just like you reload a website. Then add more dialogs and you will quickly get how things are done. The last step is a deliberate challenge, adding a scene, which means adding an image file to the img/scenes/ directory.

So the format is JavaScript. Not a domain specific language, not a JSON manifest, but a JavaScript file with a variable holding a list of dialogs. That has a specific consequence: a syntax error in your story is a JavaScript error, and it stops the whole game from loading, which is a different failure mode from a missing label in a bespoke scripting language.

The engine provides the actions and the components that the script drives. The features list names actions, components and decorators as the extension points, and the documentation point about adding a scene with an image file shows the level at which you work: place a file in a directory, name it in the script, and the engine renders it.

The engine's own feature set covers the parts that make an interactive fiction engine feel finished. Full multimedia support for images, video, music, sound effects and voices. Built-in multi-language and translation support. Save and load, with optional save-slot screenshots. Rollback, auto-play, text skipping and a dialog log, all out of the box. Dialogue text effects. And asset preloading and unloading for fine-grained memory control.

## Offline play and desktop packaging come on top of the page

The distribution story is the part that differentiates the engine from a browser-only visual novel tool.

Progressive Web App features allow installable, offline gameplay, which is a real capability rather than a slogan: the service worker caches what the game needs, and the engine adds asset preloading and unloading so that what is cached is under the author's control rather than left to the browser's guesswork. For an interactive fiction that is text plus a handful of images and audio, that is a completely reasonable way to ship, and it means the game can be installed to a phone home screen and played on a plane.

The same commitment to the web platform shows up in the packaging options. The features list says you can ship to the web, or package for desktop with Electron or Electrobun and for mobile. The engine documentation names the same idea in the section about what you need to get started, where it describes the requirements as essentially a text editor that can handle HTML, JavaScript and CSS. The documentation says almost any editor will work, yes even Notepad, but you will have a much nicer time with one that has syntax highlighting, and it recommends Visual Studio Code, VSCodium and Zed.

The core is also published to npm as @monogatari/core, and the manifest gives the shape of it. The package is an ES module with a main and module entry point at dist/engine/core/monogatari.module.js and types at dist/types/index.d.ts, plus a separate ./browser export that resolves to a self-contained browser bundle. The documentation says the package ships both a self-contained browser bundle and an ES module along with TypeScript type definitions, so you can drop it into a page as a global library or import it from the bundler of your choice.

For the browser case, loading the bundle exposes a Monogatari namespace and a ready-to-use monogatari instance on the global scope:

```html
<script src='./monogatari.js'></script>
```

and the documentation notes that monogatari is available globally while Monogatari.default points at the same instance. For a bundler, the import is a single line.

## Testing is done in Cypress against a real browser

The test setup is worth a look, because a visual novel engine has a lot of surface area in its user interface and the way that is tested tells you how much attention the interface gets.

There is a cypress directory and a cypress.config.js at the top level, and the README carries a badge pointing at the Cypress dashboard for the develop branch. The package manifest has cypress at 15.7.1, and the test script is bunx cypress, with an eslint-plugin-cypress in the dev dependencies and a separate eslint config file for it.

Running the test suite is therefore a browser automation run rather than a unit test run, which for this kind of engine is the right call. A visual novel engine's correctness is mostly about what appears on screen after a sequence of script actions, and that is exactly what a browser test can assert and a unit test cannot. The presence of a dedicated eslint plugin for Cypress is a small sign that the tests are treated as code with their own rules rather than as an afterthought.

The rest of the toolchain is a modern TypeScript setup. The type check is bunx tsc --noEmit, the linting is bunx eslint over ./src, and the build is a bun build.ts file, which is Bun's own bundler script rather than a bundler configuration. The dev dependencies include dotenv, which suggests the engine reads configuration from an environment file, jsdoc, tsx for running TypeScript directly, and the typescript-eslint stack.

The version history is the other thing to check. The recent releases are v2.8.0 on 2026-06-18, v2.0.2 on 2021-02-27 and v2.0.1 on 2020-12-29, so there is a six year gap between the 2.0.2 tag and the 2.8.0 tag. The last push to the repository was on 2026-06-18, the same day as the 2.8.0 release, and the default branch is develop while the release line lives on a versioned branch referenced in the documentation links.

## Where Monogatari is the wrong choice

Three situations rule it out, and they are all about the web platform rather than about the engine's quality.

The first is a project that needs a native feel. An engine built on custom elements and a served page will look like a web page, because it is one, and it will be constrained by what a browser does with a text advance, a sprite and an audio buffer. If the interaction you are designing depends on frame-accurate animation, on a layout the browser will not give you, or on a native input model, the web is the wrong substrate no matter how good the engine is.

The second is a large audio or video project. The engine supports music, sound effects and voices, and it has asset preloading and unloading for fine-grained memory control, which tells you the author thought about memory. But a browser delivering a few megabytes of audio for a long play session is a different proposition from a native game streaming from disk, and the whole architecture, with a service worker deciding what is cached and what is not, is aimed at a title whose total asset size is modest.

The third is a team that expects an authoring tool. The documentation is explicit that you edit script.js in a text editor, and the workflow is a reload loop. A writer who wants to click through a visual timeline, and who does not want to touch a JavaScript file, will not be served by this engine. The engine is for a person who is comfortable with the file format.

There is also a maintenance consideration to weigh, and the release history is the evidence. The gap between v2.0.2 and v2.8.0 suggests the project moved from a slow cadence to a burst, and the repository's default branch is develop, so the version you get by default is not necessarily the version in the documentation. The licence is MIT, so forking and maintaining it is straightforward, and the npm package means the engine can also be used as a library inside a larger page rather than as a whole game.

## Conclusion

Monogatari fits an author who wants to write interactive fiction as a web page, who is comfortable editing a script file and a little markup, and who wants to publish one build that works on a phone, on a desktop and offline. It is the wrong choice for a project that needs a native look, a real audio pipeline or a large asset budget served from a game engine, and it is the wrong choice if you expect the engine to hide the web platform from you. Before adopting it, read the script and labels documentation, run the sample game over a local HTTP server rather than from the file system, and check the current release, because the tag history shows a long gap between v2.0.2 and v2.8.0 and the repository's default branch is develop.

## FAQ

### What is Monogatari and what is it built for?

It is a simple web visual novel engine created to bring Visual Novels to the web, written in TypeScript with full type definitions for the engine, actions and components. The project states that your visual novel is a web page first and a game later, which is why responsiveness and progressive web app support come first.

### How do I run a Monogatari game locally?

You can double-click index.html, but the documentation warns that offline support, service workers, asset preloading and anything using fetch only work when the game is served over HTTP. It recommends running a small local server, with npx serve for Node.js, bunx serve for Bun, or python3 -m http.server for Python 3.

### How do I write the story in Monogatari?

The story lives in a script.js file as a variable called script holding a simple list of dialogs, and you edit it in a text editor, save and reload the page. Scenes are added by putting an image file in the img/scenes/ directory and referring to it from the script, and the workflow documentation links the script and labels page for the details.

### Can I use Monogatari as a library rather than a whole game?

Yes. The core is published to npm as @monogatari/core and ships a self-contained browser bundle and an ES module with TypeScript type definitions. The browser build exposes a Monogatari namespace and a ready-to-use monogatari instance on the global scope, and the ES module build is imported from the bundler of your choice.

### Is Monogatari still being maintained?

The last push to the repository was on 2026-06-18 and the repository is not archived, with v2.8.0 released the same day, after v2.0.2 from 2021-02-27. The default branch is develop, the test suite runs on Cypress, and the licence is MIT.

## Sources

- [License: MIT](https://github.com/Monogatari/Monogatari/blob/develop/LICENSE)
- [Monogatari/Monogatari on GitHub](https://github.com/Monogatari/Monogatari)
- [Project website](https://monogatari.io/)
- [README](https://github.com/Monogatari/Monogatari/blob/develop/README.md)
- [Releases](https://github.com/Monogatari/Monogatari/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/monogatari-monogatari
