Monogatari v2.8.0
Download Link: monogatari-v2.8.0.zip
v2.8.0 is the largest release since v2.6.0. Under the hood, the entire engine has been rewritten in TypeScript and broken apart into focused modules; on the surface, you get explicit asset management, save screenshots, native desktop storage, a big library of dialogue text effects, and a long list of fixes. Here’s everything that changed and how to take advantage of it.
π° Release Change Log
π New Features
π§ A Full TypeScript Rewrite
The core engine, every built-in action, and every component have been migrated to TypeScript. The @monogatari/core package now ships its own type definitions, so whether you’re writing custom actions and components or just scripting your game in an editor, you get full autocomplete and type-checking for the engine API, game settings, and the new typewriter effects.
π§© The Engine, Split Into Modules
The old ~3,500-line monogatari.ts “god object” has been split into focused modules β assets, characters, i18n, input, lifecycle, persistence, and ui. The build now produces two clearly separated targets:
lib/monogatari.module.jsβ an ES module, themainentry of the npm package, ideal for bundlers (Parcel, Vite, webpack, etc.).dist/engine/core/monogatari.jsβ a self-contained browser bundle, the one used by the classic drop-in game template.
Nothing changes about how you write a game, but the engine is now far easier to maintain and to load from a bundler.
π¦ Preload & Unload Actions
Two new script actions give you explicit control over when assets live in memory. preload pre-caches music, sounds, voices, scenes, images, and character sprites before you need them; unload frees them when you don’t.
// Single assets
'preload music main_theme',
'preload scene classroom',
'preload character y happy',
// Wait until the asset is ready before continuing
'preload scene boss_room blocking',
// Free memory again
'unload scene classroom',
'unload music', // a whole category
'unload all permanent', // memory *and* the IndexedDB cacheYou can also group assets into named blocks and load or free them in a single statement β great for per-chapter management:
monogatari.action('Preload').blocks({
'Chapter1': {
music: ['theme_song', 'battle_music'],
scenes: ['school_gate', 'classroom'],
characters: { 'y': ['happy', 'sad', 'normal'] },
},
});
// ...later in your script
'preload block Chapter1 blocking',
// ...and at the end of the chapter
'unload block Chapter1',Audio is decoded once and persisted in IndexedDB, so re-preloading across sessions is instant β unless you unload ... permanent, which clears the persistent cache too.
πΈ Save Screenshots
Save slots can now show a screenshot of the exact moment a save was made instead of a generic scene image. Turn it on with a single setting:
monogatari.settings({
'Screenshots': true,
});When enabled, the engine captures the game screen (via modern-screenshot) as you save and stores it in a dedicated IndexedDB database, kept separate from your save data so it never bloats the save object. Slots then render that screenshot as their thumbnail. β οΈ Screenshots have a couple of requirements around CSP and cross-origin assets β see the upgrade notes below and the Saving documentation.
πΎ File System Storage
For games packaged as desktop apps (Electron / Electrobun), there’s a new FileSystem storage adapter that writes saves straight to disk through a desktop bridge instead of the browser’s IndexedDB:
monogatari.settings({
'Storage': {
'Adapter': 'FileSystem',
'Store': 'SaveData',
},
});If the same build is opened on the web (no desktop bridge present), it automatically falls back to IndexedDB, so it keeps working everywhere.
π Show & Hide Textbox
Two tiny but frequently requested actions to get the dialog UI out of the way β perfect for showing off a CG or a scene:
'show scene cg_sunset with fadeIn',
'hide textbox',
'wait 2000',
'show textbox',
'e ...beautiful, right?',Visibility is part of the saved state, so it survives save/load and rollback. One thing to remember: once hidden, the box stays hidden for every following line until you show textbox again β so always bring it back before a line you want the player to read. Docs: show textbox Β· hide textbox.
β¨ A Big Library of Text Effects
Dialogue now supports inline text effects with a simple {effect}β¦{/effect} markup. Open with {effect} and close with {/effect}:
'y I am {angry}absolutely furious{/angry}! {pause:500}{whisper}...but I will be okay{/whisper}.',
'narrator The {glitch}corrupted{/glitch} signal pointed somewhere {mysterious}strange{/mysterious}.',There are 40+ effects across several families:
- Movement β
shake(plusshake-hard,shake-slow,shake-little,shake-horizontal,shake-vertical),wave/wave-slow/wave-fast - Reveal β
fade,blur,scale,scale-bounce,slide-up,slide-down,redacted,invisible-ink,handwriting,flicker - Glitch β
glitch,glitch-hard,glitch-slow - Style β
bold,italic,big,small,impact,rainbow,glow - Emotion presets that bundle several of the above into one keyword β
angry,scared,happy,sad,mysterious,excited,whisper,shout,dizzy,dreamy,robotic,static
Effects nest, so you can combine them:
'{shake}{angry}STOP{/angry}{/shake} right there!',β¦and they’re all driven by CSS custom properties, so you can re-tune them globally without touching the engine:
type-character[data-component] {
--effect-shake-intensity: 4px;
--effect-wave-amplitude: 0.5em;
--effect-wave-speed: 0.6s;
--effect-glitch-color-1: #ff00ff;
--effect-glitch-color-2: #00ff00;
}The full list and details live in the Dialogs and Type Writer docs.
β© Semi-Instant Skipping
If you keep InstantText off β so finishing a line rushes the typewriter while keeping its effects, rather than dumping the whole line at once β you can now also let players make that rush genuinely fast. Set the min value of the text-speed slider in your settings screen below 0:
<input type="range" min="-100" max="50" step="1" data-action="set-text-speed">Players who drag the slider into the negative range get a near-instant fast-forward that still respects formatting and effects.
π¦ A Proper Action-Blocking API
Actions that need to pause the story until they finish β waiting on a choice, a notification permission, an async load β now declare a static blocking flag and a shouldProceed() check instead of toggling a global. The old block / executing sub action globals still work for now but are deprecated; if you maintain custom actions, see the upgrade notes below.
π Bug Fixes
- Fixed audio volume being reset to
0on fade-in instead of fading from the configured level. - Fixed word-wrap making text “jump” mid-typewriter β unrevealed characters now use
opacityinstead ofvisibility. - Text speed now updates live from the settings slider (previously it only took effect after a refresh), and the slider shows the correct value on reload.
- Fixed
Messageactions soft-locking the screen and blocking progress. - Fixed
AutoPlaySpeedflipping to the opposite value every time the player changed it and refreshed the game. - Fixed the
selectorparameter not being passed toinit()in actions and components. - Fixed the page not centering correctly when aspect-ratio enforcement was active.
- Fixed dialog-log and text-box scrolling behavior.
- Made visual actions (
show/hideimage, character, layer, canvas, video, etc.) record state and history consistently, for more reliable rollback. - Fixed a recursive-call bug in the engine core.
- Re-exported
tsParticlesfor legacy compatibility.
π¦ Misc
- Added the
modern-screenshotdependency that powers the screenshots feature. - New service-worker caching strategy: network-first for navigations (so fresh CSP and markup reach players immediately), stale-while-revalidate for your JS/CSS (instant load, updated on the next visit), and cache-first for everything under
assets/. Opaque cross-origin responses are no longer cached β this is what keeps screenshots from coming out black. - Documentation has moved into the main repository and now lives at monogatari.io/v2.
- The development toolchain now runs on Bun, with a custom dev server (WebSocket live-reload), a
build-web.tsweb build, and Electrobun scripts for desktop distribution. None of this is required to ship a game β the downloadable template is still a static site you can serve any way you like. - Removed the old Parcel / Yarn toolchain, cleaned up leftover debug logs, added missing debug-mode error messages, and updated the CI lint/test tasks.
- The npm package has been updated to
2.8.0and now ships TypeScript types.
How To Upgrade
If you’re on a v2.x.x version
For most games, upgrading is the usual drill: replace the files in your engine directory with the new ones from the download. A few things are worth double-checking:
- Custom dialog markup: if you use inline text effects, make sure your closing tags are
{/effect}(for example,{shake}boom{/shake}). - Custom actions: if you wrote actions that called
engine.global('block', β¦)or relied on_executing_sub_action, migrate them to the new pattern β a staticblockingproperty plus ashouldProceed()that throws while the action is blocking. The old globals are still honored, but deprecated. - Turning on screenshots: besides
Screenshots: true, you’ll need to:- add
blob:to thescript-srcdirective of yourContent-Security-Policy(the template’sscript-src-elemalready allowsblob:;modern-screenshotalso needs it inscript-src), - serve any cross-origin assets (for example, from a CDN) with CORS headers (
Access-Control-Allow-Origin), otherwise the browser taints the canvas and your thumbnails come out black, and - replace your
service-worker.jswith the new one, which skips caching opaque responses.
- add
If you’re on v1.4.1 (stable) or below
If you’re upgrading from a game built on v1.4.1, we recommend reading the Upgrade Guide first β the jump to v2 is a big one.