Project Mammut Wiki
Project Mammut is currently in open beta. The UI is being updated a lot during this time, and you may run into bugs. If you'd like to help develop the engine or suggest features, come find us on Bluesky or Discord.

Variable Store

Written 5 Aug 2026 · Updated 8 Aug 2026 · Requires Mammut 0.6.2+

Store is the runtime module that holds every one of your project's variables while the game is running, and it's the single source of truth several other systems build on: the Variables Manager is where you create variables and set their starting values, {$varName} and {set($var, expr)} in dialogue read and write through it, and SaveManager snapshots it wholesale to build a save file. Scene scripts talk to it directly. This page covers how the module itself behaves; for the one-line method signatures see Coding.

Reading and writing

Store.Get("playerName", "Alex"); // reads, seeding the default if unset
Store.Set("playerName", "Sam");

Get(key, defaultValue) isn't purely read-only: if key hasn't been set yet, the store writes defaultValue into itself right then and returns it, rather than just handing back undefined. That means the first call to Get for a given key is effectively also the moment it gets created, which is why a variable can work perfectly well from a scene script even if it was never explicitly added in the Variables Manager first.

Set(key, value) always writes through immediately and synchronously, there's no batching or async delay between calling Set and every reader (including {$var} in currently-revealing dialogue) seeing the new value.

Reacting to changes

Variables can me monitored for chages, so that when one changes a function is called to react to it. This is handy for setting up reactive elements in your game.

function onScoreChanged(newValue, oldValue) {
  this.dom.score_text.textContent = newValue;
}

Store.Register("score", onScoreChanged.bind(this));
// ...later...
Store.Unregister("score", onScoreChanged);

Register(key, handler) calls handler(newValue, oldValue) every time Set is called for that key, including when the new value is identical to the old one, the store doesn't diff before notifying.

Watch out with .bind(): Unregister removes a listener by comparing function references directly. If you register a bound function (onScoreChanged.bind(this)), that call produces a new function each time, passing the original unbound method to Unregister later won't match it, and the listener will silently keep firing. Keep a reference to the exact bound function you registered, and unregister that same reference (typically in onExit).

Relationship to dialogue markup

{$varName}, {set($var, expr)}, and {print($var, ...)} in chapter content read and write the exact same store as Store.Get/Store.Set. A variable set from a scene script is immediately visible to {$var} in dialogue, and vice versa. See Logic for the full expression syntax available inside those blocks.