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.

Scene Manager

Written 9 Aug 2026 · Updated 15 Nov 2026 · Requires Mammut 0.6.2+

Most scenes aren't loaded up front. Only the splash screens and your project's start scene are loaded eagerly when the game boots. Every other scene is fetched on first use, the moment something calls SceneManager.SwitchScene(name) or LoadScene(name).

Switching scenes runs a fixed sequence:

  1. Start loading the new scene, along with the images and audio it uses (including those of any scenes nested inside it).
  2. Fade the screen to black (or skip this if fadeDuration is 0).
  3. Call the outgoing scene's onExit().
  4. Remove the outgoing scene's DOM.
  5. Wait for the loading to finish, for up to 10 seconds from the start of the switch. Anything still loading after that carries on in the background.
  6. Add the new scene to the page and wait for its fonts.
  7. Fade back in, while mounting the new scene: onEnter(), then onStart().

Loading overlaps the fade, so on desktop builds the switch usually takes no longer than the fade itself. On the web, a scene with large images may hold on black a little longer, but it appears complete instead of filling in while you watch.

To load a scene ahead of time, call SceneManager.Preload(name), for example on a menu for the scene its Start button leads to. Switching to it later then has nothing left to wait for. AttachScene preloads the same way before showing the scene.

onEnter() and onStart() are both fired once the new scene is mounted, but for nested scenes (see below) they don't fire at the same point in the walk: onEnter() fires parent-first, onStart() fires child-first. In practice, that means a scene's own onStart() is guaranteed to run only after every scene attached inside it has already finished its own onEnter() and onStart().

A scene can also be attached to a specific DOM element instead of taking over the whole screen, via SceneManager.AttachScene(name, target), useful for something like a persistent HUD that should keep running underneath whatever full-screen scene is active. DetachScene(name) reverses it. Scenes can also be nested directly inside another scene as a Scene node (see Scene Node); nested instances go through the same attach mechanism automatically, which is why the onEnter/onStart ordering above matters for them.

See Coding for the exact SceneManager methods, and Actions for the [SwitchScene(...)] markup that drives this from dialogue.

At runtime, the SceneManager keeps every scene it has ever loaded in a flat registry, keyed by name. Only one of them is the "current" full-screen scene at a time; there's no back-stack of previous scenes to pop, just whatever's currently loaded plus a swap to whatever's next.