Discovery and proposed implementation sequence, 2026-10-05. No game files have been moved. P1’s single-level save/load/recovery slice is implemented; the remaining migration milestones are still proposals.
Bring Gamify into the GameBuilder system as a reference game students can load, play, inspect, modify, save, and eventually author through the panels. Keep ordinary GameEngine JavaScript visible: the workspace is a teaching tool, not a replacement for learning code.
Confirmed decisions:
This is a discovery roadmap, not a gated, executable migration plan. A formal feature specification and reviewed design artifacts have not been supplied. Freeze the document, persistence, and module-loading contracts before turning these milestones into a final implementation task set.
BaseRunner restores
stored editor text and its Save button writes that text through
StorageManager.
The key comes from the page permalink and runner ID in
game.html.
app.mjs independently creates a default builder document on
every load. It preserves existing runner code at initialization and asks
before replacing differing code through Push. The initial AST-based Pull reads
one supported GameBuilder-style level without executing or changing source;
custom behavior remains code-owned and blocks destructive Push.
Therefore the current source does not establish an unconditional startup overwrite bug. It does establish a mismatch: saved code returns, but the matching panel settings do not. Generating from the reset panel can then replace that saved code after confirmation. Browser reproduction should verify the reported symptom before changing the shared runner.
Saving only a runner entry module also does not save changes to imported level files. A complete game save must include the editable module sources.
Proposed event: ocs:runner-saved, dispatched from the runner container and
bubbling within the page. Keep the event generic so other CodeRunner consumers
can reuse it, while GameBuilder filters by its own runner identity.
Proposed versioned detail:
{
schemaVersion: 1,
runnerId: "gamebuilder-v2",
storageKey: "_gamebuilder_v2__gamebuilder-v2",
source: "<exact source successfully persisted>",
revision: "<unique save revision>"
}
The storage key above is illustrative; use the actual runner key, not a hardcoded value. The event describes a source save. Game identity, selected module, and builder configuration belong to the workspace, not the generic runner. Capture the game/module association at save initiation, especially if workspace persistence later becomes asynchronous.
runner-bridge.mjs and
captures the matching source/configuration, without automatic generation.
A failed workspace save leaves the workspace dirty even if source storage
succeeded.Acceptance criteria: one successful save produces one event containing the exact persisted source; a failed storage write produces no saved event and visible failure feedback. Another runner’s save is ignored by GameBuilder. Editing after a save marks source dirty; returning to saved text clears that source flag without falsely clearing pending panel changes. Reload restores the saved snapshot without regeneration, and a late subscriber can query it. An integrated workspace-save failure never shows complete-workspace success.
The current source has four entry levels, another nested level, a standalone minigame, and 41 image files:
| Source | Existing behavior to retain | Initial workspace treatment |
|---|---|---|
GameLevelWater.js |
Gravity Player, custom sprite directions, NPC portal, Shark, audio | Source-editable; gravity and portal panels later |
GameLevelDesert.js |
Many NPCs, Coin, Clicker, dialogue, AI interactions, nested games and links | Source-editable; preserve callbacks and dependencies |
GameLevelEnd.js |
Two Players, parallax and regular backgrounds, Enemy, Collectible, counters and timer | Source-editable; do not force into one Player/background |
GameLevelOverworld.js |
Background, Player, Villager, Creeper, custom update behavior and embedded platformer | Source-editable; preserve custom lifecycle code |
GameLevelStarWars.js |
Player, turret NPC, two Projectiles, rotations and translation settings | Small source-editing pilot, but not fully panel-supported |
PlatformerMini.js |
Custom canvas, gravity, platforms, input, dialogue and exit lifecycle | Preserve as a custom module, not a generated GameEngine level |
The sources are currently under gamify/.
Imports also reach shared GameEngine modules such as MeteorBlaster, Coin,
Shark, Creeper, Projectile, and AI/dialogue helpers. Shared audio and referenced
external pages must be inventoried as dependencies, not assumed to be owned
Gamify images.
The builder currently supports one background, one Player, NPC greetings, and spline barriers. Its catalog has only two movement presets; its generator chooses frame ranges and default timing. That cannot faithfully regenerate all these levels. In particular, a 3-row mirrored sprite is not equivalent to a 4-row movement preset.
Use GameBuilder as the registered system and Gamify as a bundled example game:
_projects/systems/gamebuilder/
├── index.md # Workbench, existing entry point
├── notebooks/
│ └── gamify.ipynb # Moved play/teaching page; keep /gamify
├── games/
│ └── gamify/
│ ├── game.json # Identity, entry, ordered levels, dependencies
│ ├── assets.json # Asset metadata and stable IDs
│ └── levels/ # Preserved ordinary JavaScript sources
├── images/
│ ├── bg/gamify/ # Background images
│ ├── sprites/gamify/ # Characters and spritesheets
│ └── objects/gamify/ # Items, projectiles, UI art, platform textures
├── js/ # Existing modules plus focused workspace modules
├── scripts/
│ └── build-game-catalog.mjs # Proposed deterministic build-time catalog
├── docs/
│ └── gamify/ # Moved reference-game documentation
└── tests/
This layout is proposed, not created. Keep nested games as content of the registered GameBuilder project, not as deeper independently registered projects: the current template assumes flat or category/project depth.
Before moving:
this.classes./gamify and its documentation permalinks. Update imports,
notebook references, registry entries, navigation, and teaching references.Design a versioned game document above the current single-level builder state. The reviewed contract should cover:
Do not treat a saved document as a running-game checkpoint: runtime scores, positions, timers, and progress are a separate feature.
Two deliberate authoring modes prevent destructive conversion:
Panel import comes later. Parse supported syntax with an AST, never execute constructors or callbacks to discover data. Preview recognized fields and unsupported expressions. Preserve original source; block conversion if it would lose behavior. Do not promise arbitrary JavaScript round trips.
Current status: the single-level slice is implemented: automatic draft recovery, a separate explicit save/load return point, JSON import/export, exact source export, and awaited runner Save integration. Unfinished edits are kept. Named Save As libraries and multi-module packages remain future work. See the workspace usage guide.
js/; keep DOM wiring
in app.mjs and validation separate.runner-bridge.mjs; keep source saved/dirty, workspace saved/dirty, and
generated-code synchronization as distinct states.Done when: create a game with two NPCs and a hidden spline barrier, manually edit its source, save, reload, and recover both the exact source and all panel settings. Export/import restores the same document. Failed saves do not show success. Ordinary lesson Save Code still works.
GameExecutor: run the entry source using saved module text rather than
accidentally re-importing unchanged published levels.Done when: change a greeting inside one imported level, save/reload, and play the saved change. A second level and a supporting module retain their independent edits. Export/import preserves all module source text.
make dev, including
deletions/renames. A failed catalog build must not publish a stale catalog
as if it were fresh.Done when: identical inputs produce identical JSON; an invalid dependency
fails the build visibly. All original entry levels and nested games remain
accessible. Existing /gamify links and needed legacy imports still resolve.
make dev rebuild the catalog.Done when: adding an asset through metadata makes it selectable after a development rebuild; renaming its display label does not break a save. A panel edit cannot remove a code-owned component.
GameControl nested-game pattern with explicit
parentControl; do not create another general game runtime.PlatformerMini and the embedded Overworld platformer as custom
integrations until their different canvas/exit lifecycle is supported.Done when: enter and exit a child repeatedly without duplicated controls or losing the parent level; Stop while nested cleans up both games.
GRAVITY flag first, with clear teaching
text about its actual falling/movement behavior.PlatformerMini’s independent acceleration,
jumping, ground, and platform collision implementation.Done when: enabled and disabled gravity produce the existing documented Player behavior, survive save/load, and do not alter ordinary movement.
Use the repository Makefile workflow, not an alternate publishing pipeline:
make dev for the GameBuilder edit/watch loop.make -C _projects/systems/gamebuilder build for a focused distribution
check after Makefile generation.Migration comparisons must distinguish pre-existing level defects from
regressions. For example, Water currently uses an older nested-game construction
pattern, while Desert uses parentControl. Do not silently modernize both as
part of a directory move. Record failures and handle tightly related fixes in
the feature milestone with explicit behavior checks.
| Requirement | Milestones | Completion evidence |
|---|---|---|
| Load, edit, save complete games | P1, P2 | Exact panel/source restoration and multi-module saves |
| Emit and expose successful runner save state | P1, P4 | Exact saved-source event, queryable snapshot, failure and import safeguards |
| Preserve handwritten code and existing behavior | P1-P5 | Code-owned mode, source preservation, migration comparisons |
| Bring Gamify and GameBuilder together | P3 | One registered system, bundled reference game, retained links |
| Move assets into builder-compatible ownership | P3, P4 | Audited path map, stable metadata, resolving asset references |
| Generate needed JSON through make | P3 | Deterministic catalog and visible build failures |
| Load and modify individual levels | P2, P4 | Independent buffers, saved-source execution, bounded panel import |
| Add assets | P4 | Documented metadata workflow and watch rebuild |
| Support Game-in-Game authoring | P5 | Generated actions and parent/child lifecycle checks |
| Support gravity authoring | P6 | Existing engine flag preserved and verified |
The first P1 slice now saves/reopens the current builder game, including manual source edits, without moving Gamify. Next expand to P2 so moving Gamify produces genuinely editable saved games rather than a catalog of imports into read-only published modules.