Project Mammut is a HTML5 game engine, and uses Javascript as its core programming language.
Every scene in a project has its own script, made of lifecycle hooks and any custom methods you add. This page is a reference for what's available to write against.
Scene lifecycle
onEnter() {
// Scene is about to be shown.
},
onStart() {
// Called after onEnter, once attached scenes are ready. this.dom is populated here.
},
OnExit() {
// Scene is being removed. Clean up timers/listeners here.
},
// Any other method can be triggered from Revealer markup via [MethodName(...)]
Inside any of these, and any handler, this is the scene instance. this.dom, this.root, this.vars, and any custom methods or properties you've added are all available.
this, the scene instance
this.root/this.dom.root: the scene's own root element, which gets the same helpers as any other node (see below).this.dom.<nodeId>: every node marked "referenceable" in the Scene Editor, by id.this.vars: scene-local variables.this.getLocalVar(id, defaultValue)andthis.setLocalVar(id, value)read and write it.this.attachedScenes: child scenes attached to this one, populated automatically for "scene" nodes.
DOM node helpers
Attached automatically to every node in this.dom (see _setupDomHelpers in sceneBase.js):
hide(),show(),isHidden(),toggleVisibility(): toggle a shared hidden class, independent of the node's own Visible flag and of each other's internal state.disableEvents(),enableEvents(): block or restore mouse and pointer interaction.setState(name, repeat = true): swap in a named CSS state class. Returns aPromisethat resolves when that state's CSS animation finishes, or immediately if it has none:await this.dom.card.setState("flip");- Animator nodes:
play(clip),pause(),resume(),stop(),restart(clip). - Particle nodes:
start(),stop(),pause(),resume(),burst(n).
Revealer: text reveal and markup events
Revealer.Init(this.dom.text_box, { container: this.root });
const ctrl = await Revealer.StartRevealChapter("my_chapter");
Revealer.Register("SetCharacter", (event) => {
const key = event.args[0];
const variant = event.kwargs?.variant;
});
Register(eventName, handler)/Register(eventName, filter, handler): the filtered form only invokeshandlerfor one specific key or gamepad button. The check happens inside the manager, not your handler.Unregister/UnregisterAlltake the same arguments.- Every handler receives one event object:
event.name,event.args(positional),event.kwargs(namedkey=value), plus event-specific fields likeevent.optionsorevent.char. - Built-in lifecycle events:
NodeStart,NodeEnd,SectionStart,SectionEnd,WaitingForClick,OptionsReady,OnTextCharPlace,SetCharacter, plus any custom[ActionName(...)]tag you use in dialogue. Pause()/Unpause()/IsPaused(): freeze or resume the reveal. Returning aPromisefrom a handler does this automatically until it resolves.RevealNode(nodeId): jump straight to a node.- Full reference:
/revealer-reference.html, shipped with the app.
Assets: reading project data
Assets.GetCharacter("alice"); // { key, name, image, variants, ... }
Assets.GetChapter("chapter_01");
Assets.GetImage("bg_forest");
Assets.GetAudio("theme_music");
Assets.GetVariant("alice", "smiling"); // a specific variant object
Assets.Get(key, kind); // generic lookup
Assets.GetAll(kind); // every asset of a kind
Assets.Register(name, kind, data); // register something at runtime
Store: persistent variables
Store.Get("playerName", "Alex"); // reads, seeding the default if unset
Store.Set("playerName", "Sam");
Store.Register("playerName", (newVal, oldVal) => { /* react to changes */ });
Store.Unregister("playerName", handler);
{$varName} and {set($var, expr)} in dialogue text read and write through this same store.
SceneManager
SceneManager.SwitchScene("main_menu");
SceneManager.GetScene("settings_panel")?.togglePanel(); // reach another loaded scene directly
SceneManager.GetCurrentScene();
await SceneManager.LoadScene("settings_panel"); // ensure it's loaded before calling into it
SceneManager.AttachScene(name) / DetachScene(name);
Every scene registers itself globally by filename once loaded, so GetScene can reach any currently-loaded scene, sibling or not.
AudioManager
AudioManager.PlayMusic("theme"); // crossfades
AudioManager.StopMusic(fadeSeconds);
AudioManager.PlayAmbient("rain");
AudioManager.StopAmbient(fadeSeconds);
AudioManager.PlaySFX("click");
AudioManager.PlayVoice("line_042");
AudioManager.SetMasterVolume(0.8); // also Music/Sfx/Ambient/Voice
AudioManager.GetMasterVolume();
AudioManager.ApplySettings(settings);
AudioManager.SaveSettings() / LoadSettings();
AudioManager.Register("SFXPlayed", handler); // and other audio events
InputManager
InputManager.Register("KeyDown", (e) => { if (e.code === "Space") jump(); });
InputManager.Register("KeyDown", "Space", (e) => jump()); // pre-filtered by the manager
InputManager.Register("GamepadButtonDown", InputManager.GamepadButton.A, (e) => jump());
InputManager.IsKeyDown("ArrowLeft");
InputManager.IsGamepadButtonDown(gamepadIndex, button);
code is the physical key, layout-independent (e.g. "Escape", "Space"); key is the layout-aware character. GamepadButton has named constants (A, B, X, Y, LeftBumper, DPadUp, and so on) for the standard controller mapping.
Application
Application.GetPlatform(); // "tauri" or "web"
Application.Quit();
Application.Restart();
Application.EnterFullscreen() / ExitFullscreen() / ToggleFullscreen() / IsFullscreen();
Application.OpenURL(url);
Fullscreen uses the browser's Fullscreen API on web and the native OS window under Tauri. That means Escape always exiting fullscreen is a web-only browser restriction, not something the desktop build has to work around.
SaveManager
SaveManager.Save(slot);
SaveManager.Load(slot);
SaveManager.Exists(slot);
SaveManager.GetSlotInfo(slot); // { date, scene }
SaveManager.GetGameSettings() / SetGameSettings(settings) / SaveGameSettings();
Logging
log("hello");
logWarning("careful");
logError("something broke");
Logger.Register((level, args) => { /* pipe to your own UI, etc. */ });
Logger.Unregister(handler);
Debugging
You can learn how to debug your projects here.