Reference
Hearth Roadmap
Edit on GitHubWhat is next and what is deliberately missing from the preview.
v1.2.0 is the current release. It reworks the editor’s Agent panel: the
panel now lives in its own full-height dock beside the Inspector, first open
is a one-click “Launch your agent” screen (Claude Code, Codex, OpenCode,
Hermes, or a plain terminal), missing CLIs can be installed inline, and the
permission mode, re-detect, and manual setup moved behind a settings menu.
Saved layouts migrate automatically. v1.1.0 added script modules: require
for same-language Lua and JavaScript helpers under scripts/, recursive
script discovery/export, whole-graph hot reload, and validation for broken
or cyclic requires. v1.0.0 closed the road to 1.0 with the final hardening
wave: a
project-format stability guarantee (an automatic migration rail, strict
version-stamp rules, upgrade tests against 0.13/0.14 projects), a native
close guard for unsaved script edits, a performance regression fence in CI,
and a docs completeness pass. Before it, v0.15.0’s agent game-craft wave
(Wave L2, below) sharpened how agents actually make games. What follows is
the running history.
v0.14.0 (“Tightening”, below) added no new engine features — 129 commits that make the
editor work properly everywhere, front-load the workflows, and unify the
design language, driven by a 16-surface live audit (a 121-entry ledger, 100
fixed, every entry dispositioned) and closed by six independent re-audits.
Game keyboard capture is now scoped to the game, so editor dialogs, buttons,
and fields never lose Escape/Enter/Space again and WASD/axis-only bindings
work in play mode and exports; switching to the Code panel auto-pauses the
run (simulation and audio) and switching back auto-resumes; desktop-export
zips preserve executable bits so unzip-and-launch just works; a real
File/Edit/View/Help application menu, a minimal toolbar that holds up at
1024px, a hand-authored icon set, keyboard-accessible tooltips, and real
empty states retire the last raw-JSON Inspector fields; prefab edits are
trustworthy (same-value edits record no override, no-op reparents don’t
detach, broken markers self-repair); the intermittent editor batch-pool
crash is root-caused and fixed for good (0 crashes in 250 stress sessions);
and every project now ships a fact-checked best-practices skill
(.claude/skills/hearth/SKILL.md, drift-gated against the canonical copy)
that agents can read. Registry is unchanged at 71 commands; MCP grew
68 → 70 command tools (72 tools total, including screenshot/
get_agent_instructions, adding set_entity_enabled/set_entity_tags).
v0.13 projects open byte-non-destructively. See
Shipped in v0.14.0 below.
On top of v0.13’s ship-your-game wave — desktop game export (Electron),
a macOS signing ladder, itch.io zip parity everywhere, and project
templates (see Shipped in v0.13.0 below) — v0.12’s
animation state machines (an AnimationStateMachine
component drives a sibling SpriteRenderer from a params/states/
transitions asset — assets/statemachines/*.asm.json,
createStateMachineAsset/updateStateMachineAsset,
ctx.animator.setParam/getParam/fire/state in both Lua and JS, a typed
Animator editor panel), 47-blob tilemap autotiling (setTileAutotile/
hearth autotile set binds a tile char to a blob47 rule so its sheet
frame is picked from its 8 neighbours at render time instead of
hand-placing every edge/corner variant, plus a per-char sprite/autotile
mode toggle in the Inspector), live-linked prefabs with per-field
overrides (editing an instance’s field directly now records it as an
override instead of silently drifting; updatePrefab auto-syncs every
other instance in the same command, merging the new payload with each
instance’s own overrides rather than replacing them wholesale;
revertPrefabOverride/hearth prefab revert plus ember override dots
and per-field/whole-instance revert buttons in the Inspector; a
structural edit inside an instance detaches it instead of guessing),
in-scene particle preview (a Scene toolbar toggle simulates the
selected entity’s ParticleEmitter live, off the same deterministic
stepper the runtime uses, without pressing Play), and bulk asset
import (importAssets/hearth import asset <paths...> [--recursive]/
MCP import_assets — one atomic undo entry per batch, auto-suffixed name
collisions, bad files reported in skipped instead of failing the whole
import; the editor’s Assets panel accepts multi-file picks and
whole-folder drag-drop). Registry grew 65 → 70 commands; MCP grew 64 → 67
command tools (69 tools total, including screenshot/
get_agent_instructions). See
scripting.md,
editor.md, and
prefabs.md.
On top of v0.11’s script hot-reload during play (edit a script, every live
entity running it picks up the new code without a Stop/Play round-trip),
live Inspector property patching during play, a “Scene changed — Restart”
badge for structural changes that can’t be live-patched, runtime error →
exact line, format-on-save (StyLua/Prettier), Code panel tabs, ctx.
hover docs, in-file search, and cross-script search/replace (⇧⌘F;
hearth script search|replace/MCP search_scripts/replace_in_scripts,
dry-run-first) — Registry grew 62 → 65 commands; MCP grew 61 → 64 tools —
v0.10’s post-processing system (Camera.postEffects —
bloom/CRT/vignette/chromatic-aberration/pixelate/color-grade, up to 8
stacked — and per-sprite SpriteEffects outline/hit-flash/dissolve,
ctx.effects.flash), editor Code panel (lazy CodeMirror 6, ctx.
autocomplete, inline checkScript lint) plus a Live panel and
Pause/Step for frame-by-frame inspection, stricter
property-path validation with did-you-mean suggestions, a checkScript
pre-flight command, and Origin/Host enforcement on the local project
server — v0.9’s prefabs (tracked-stamp reusable entity templates:
create/instantiate/update/sync, CLI + MCP parity, ctx.scene.spawnPrefab
— the richer live-link model above is what v0.12 built on top of these)
and a round of editor friendliness — plain-language chrome
(Checkpoint/Review/Changes), visible toolbar Undo/Redo, a keybind registry
with a ? shortcut cheat sheet, and direct-manipulation transform handles
in the scene view. On top of v0.8’s embedded agent panel (a real terminal
running the user’s own claude, pre-wired to the project via MCP, with a
live trust timeline and Snapshot/Review/Revert), the disk-backed command
journal that made the editor live-follow any external agent, published
performance numbers plus the perf work (spatial-hash broadphase,
entity/tilemap caching, particle pooling) that makes them look good, and
scene-management/tilemap-editing ergonomics with a 9th example proving the
new scale ceiling — v0.7’s disk-backed undo/redo, gamepad input with
virtual analog axes,
deterministic camera effects (shake/flash/fade/zoom punch), and a second
generation of UI widgets (UILayout/UISlider/UIToggle, focus
navigation) — v0.6’s asset pipeline v2 (imported spritesheets
with typed frame slicing and sheet-backed animations, a single
crossfading music channel, real font assets) — v0.5’s physics v2
(mass/restitution/friction, collision layers, one-way platforms,
circle-accurate resolution), script stdlib v2 (ctx.math,
ctx.events/onEvent), and pathfinding (ctx.scene.findPath, hearth inspect path/inspect_path) — v0.4’s 2D lighting, line
rendering, deterministic particles, sprite animation playback, a
debug-draw overlay, and screenshot capture for agents — v0.3’s Lua-first
scripting, scene management, and chrome-free exports — v0.2’s dockable
editor workspace, screen-space game UI, polygon colliders, working audio
with procedural sound effects, and production web export — and v0.1’s
full human+agent loop (editor ⇄ command system ⇄ CLI/MCP ⇄ runtime ⇄
playtests ⇄ diff review). This page is the honest list of what’s next and
what’s deliberately missing.
The standing rule for everything below: agent-native first. Each system
ships as schemas + commands (inspectable via hearth … --json, exposed as
MCP tools, testable in headless playtests) before it gets editor UI.
Shipped in v1.2.0 — Agent panel
- Own right-hand dock: the Agent panel moved out of the bottom dock into a full-height column beside the Inspector, so the terminal gets height and the Inspector stays visible while an agent works. Existing saved layouts migrate automatically; only the agent panel relocates.
- One-click launch: first open shows “Launch your agent” with one tile per detected CLI (Claude Code, Codex, OpenCode, Hermes), installed ones first. Clicking a tile writes the tool’s MCP config and starts it — no setup screen. A plain terminal stays available for any other CLI.
- Inline install: agents with a known installer (Claude Code, Codex, OpenCode) install from the tile, visibly in the terminal; Hermes links to its connect guide.
- Progressive disclosure: the permission mode (default: safe edit), re-detect, and manual setup live behind a settings menu. Mode changes lock while a session runs. The activity timeline sits under the terminal as a collapsible section with Checkpoint / Review changes / Restore checkpoint.
Shipped in v1.1.0
- Script modules: scripts can now share code with load-time
require("lib/noise"), resolved againstscripts/with the requiring script’s extension inferred. Explicit extensions work too. Lua requires Lua, JS requires JS, and ESMimportremains unsupported. - Libraries are just scripts: no new asset type or schema change. A helper is a script that returns a table (Lua) or exports an object (JS) and usually has no lifecycle hooks.
- Deterministic module graph: module bodies run once per scene load and
are memoized. Cycles are errors that name the full chain, and resolution
cannot escape
scripts/. - Graph-aware validation and reload:
checkScript,hearth validate, andvalidate_projectreport broken or cyclic requires (SCRIPT_REQUIRE_NOT_FOUND,SCRIPT_REQUIRE_CYCLE). Editing a library hot-reloads its dependents; a compile failure anywhere leaves the entire previous graph running. Module top-level state resets on reload, so reload-surviving state still belongs inctx.vars. - Scripts are a tree:
listScriptsrecurses,scripts/lib/*appears in CLI/MCP/editor script lists, and nested scripts ship in exports. CLIhearth create script noise --dir liband MCPcreate_script’sdirparameter create helpers in subdirectories.
Shipped in v0.14.0
“Tightening.” No new engine features. 129 commits making the editor work properly everywhere, front-loading the workflows, and unifying the design language — driven by a 16-surface live audit (a 121-entry ledger, 100 fixed, every entry dispositioned) and closed by six independent re-audits.
- Every control works: game keyboard capture is scoped to the game, so editor dialogs, buttons, and fields never lose Escape/Enter/Space again; WASD and all axis-only bindings work in play mode and exports; Space activates the focused button app-wide; clicking the canvas arms game input and releases it cleanly afterward.
- A tighter iteration loop: switching to the Code panel auto-pauses the run — simulation and audio freeze in place — and switching back auto-resumes without stopping it; hot-reload never tears the session down; undo/redo is burst-safe, serialized, and narrates what it reverted; checkpoints refresh the Changes panel instantly; console errors deep-link to the failing line and the unread badge respects your scroll position.
- Honest shipping: desktop-export zips now preserve executable bits, so
unzip-and-launch just works (silently broken for every shipped zip
before); per-game macOS bundle ids; folder web builds explain the
file://limitation instead of failing cryptically; window icons accept a sprite or tile asset and are validated before export. - One deliberate product: a real File/Edit/View/Help application menu
(native on macOS in the desktop app, a slim in-window strip in the
browser), a minimal toolbar (transport, scene picker, undo/redo arrows)
that holds up at 1024px, a hand-authored icon set, a keyboard-accessible
tooltip primitive replacing every native
title, shared Menu/Button primitives, a tokenized type scale with Bricolage Grotesque brand moments, and real empty states in every panel. No raw-JSON Inspector fields remain. - Prefab trust: same-value edits no longer record overrides, no-op reparents no longer detach instances, renames stay unique, saving an instance as a new prefab warns before re-linking, and broken markers are repaired instead of masquerading as live instances.
- Agents are first-class: full editor↔agent capability parity — new
set_entity_enabled/set_entity_tagsclose the last gaps (Registry unchanged at 71 commands; MCP grew 68 → 70 command tools, 72 tools total). Every project now ships a fact-checked best-practices skill (.claude/skills/hearth/SKILL.md, drift-gated against the canonical copy) plus a pointer in AGENTS.md andget_agent_instructions— agents don’t just have the tools, they know the house playbook. - Stability: the intermittent editor crash (“Cannot read properties of null (reading ‘clear’)”) was root-caused to Pixi’s global batch pool being released by scene-view teardown and fixed for good (0 crashes in 250 stress sessions); saved dockview layouts from earlier versions self-heal; v0.13 projects open byte-non-destructively.
- Upgrade note: no format changes; v0.13 projects open unchanged.
Shipped in v0.13.0
- Desktop game export (Electron):
exportDesktopwraps a project’s web build in a minimal, hardened Electron shell (contextIsolation: true,nodeIntegration: false, no preload, navigation locked to the loaded file) and packages one app per platform —darwin-arm64,darwin-x64,win32-x64,linux-x64, all four by default. CLIhearth export desktop [--out dir] [--platform p]...(repeatable), MCPexport_desktop, and a Desktop pane in the editor’s Export dialog (platform checkboxes, output dir, a live per-platform progress stream, per-build zip paths) all wire into the same new@hearth/shippingpackage, which drives@electron/packagerand moves the CLI’s zip helper (nowzipDirectory, shared by web and desktop output alike) out of the CLI and into one implementation. Output is<project-slug>-<platform>.zipnext to a per-platform app directory, requires thebuildpermission likeexport web. Cross-packaged Windows/Linux artifacts built from a macOS host are packaging-verified but not execution-verified — CI only runs the host platform’s packaged app; the docs say so plainly. See export.md. - macOS signing ladder: ad-hoc
codesignby default (falls back to unsigned rather than failing the export if codesign itself can’t run);HEARTH_MAC_IDENTITYsigns with a real identity (a signing failure here is a hard error); addingHEARTH_APPLE_ID/HEARTH_APPLE_PASSWORD/HEARTH_TEAM_IDalso notarizes (xcrun notarytool submit --wait) and staples the ticket. Windows/Linux builds are unsigned this release. Each build reports its ownsigned/notarizedstate, surfaced in the CLI output and the editor’s pre-export signing status line (GET /api/export/capability). - Shippable app icon + Game Settings panel:
buildSettings.icon(a sprite asset id, defaultnull) is converted to.icns/.icofor desktop exports (png2icons, pure JS — falls back to a bundled default Hearth icon on a decode failure rather than failing the build). Setting it needed a settings surface that didn’t exist yet, so this wave also added a typed Game Settings editor panel (Window/Loop/Loading/ Shipping sections) covering everybuildSettingsfield, not just the icon — closing a standing editor/agent parity gap wherebuildSettingshad no UI at all. Every edit is a normalupdateSettingscall, so undo/ journal/live-patch all work for free. - itch.io zip parity everywhere:
export web --zip(<project-slug>- web.zip,index.htmlat the zip root) was CLI-only; the MCPexport_webtool gained azipparam and the editor’s Export dialog gained a “Zip for itch.io” checkbox, both wired through the samezipDirectoryimplementation the desktop export uses. A zip failure after a successful export reports as aZIP_FAILEDwarning rather than turning a real export into a failure. New guide: shipping-to-itch.md (web upload, one zip per desktop platform as separate itch.io channels, butler as a documented manual step — Hearth doesn’t wrap it). - Project templates:
hearth init <name> --template platformer|topdown|arcade(default stillblank) scaffolds a small, playable genre skeleton — one scene, a camera, a commented movement script, a few obstacles, onesmokeplaytest — instead of an empty scene.--list-templatesprints the available ones; an unknown name lists them in the error. New checked-in workspace package@hearth/templates(mirrors howpackages/examplesis generated and regenerated on every version bump) backs both the CLI flag and a new template-picker card row in the editor’s Launcher (blankfirst, preselected). Pre-project, so CLI/editor only — no MCP tool; see cli.md. - Registry grew 70 → 71 commands (
exportDesktop); MCP grew 67 → 68 command tools (70 tools total, includingscreenshot/get_agent_instructions).
Shipped in v0.12.0
- Animation state machines: an
AnimationStateMachinecomponent (assetId,playing) drives a siblingSpriteRendererfrom a params/states/transitions asset (assets/statemachines/*.asm.json,StateMachineDataSchema— bool/number/trigger params,exitTime- and condition-gated transitions,from: 'any'wildcards) instead of a single looping clip; wins over a plainSpriteAnimatoron the same entity (one warning, not a hard error).createStateMachineAsset/updateStateMachineAsset(CLIcreate asset state-machine/set-state-machine, MCPcreate_state_machine_asset/update_state_machine_asset) author the document; scripts drive it withctx.animator.setParam/getParam/fire/state, identical in Lua and JS — a fired trigger latches until an eligible transition actually consumes it, never auto-expiring on its own. Script hot-reload preserves a machine’s live state (current state, params, latched triggers); reloading the asset itself resets every entity on it to the newinitialstate on purpose, so a renamed/removed state can’t leave a live machine dangling. Editor: a typed Animator editor panel (params/states/transitions row lists, no raw JSON, oneupdateStateMachineAssetper Save). See scripting.md and editor.md. - 47-blob tilemap autotiling:
setTileAutotile(CLIautotile set, MCPset_tile_autotile) binds aTilemapchar to a blob47 rule — the char’s on-screen frame is picked from its 8 neighbours (an 8-bit mask canonicalized down to the 47 visually-distinct shapes) every time the map changes, instead of hand-placing every edge/corner tile variant.--mappingoverrides individual shape keys with custom frame names when a sheet’s slice order doesn’t already match the standardblob_<shapeKey>template. Editor: a per-char Sprite/Autotile mode toggle in the Inspector’stileAssetsrow list, with a collapsed Advanced-mapping editor for all 47 shapes; both the editor preview and the runtime re-render a map’s tiles live the moment a rule or a paint changes, no restart. See editor.md. - Live-linked prefabs with per-field overrides: editing a component
property directly on a prefab instance (root or descendant) now records
it as an implicit override on the instance’s marker instead of just
silently drifting until the next full rebuild.
updatePrefabauto-syncs every other tracked instance of that prefab, across every scene, in the same command — a true merge: locals that still exist keep their scene ids, new locals mint fresh ones, removed locals drop out, and every instance’s own overrides are re-applied on top (a stale override that no longer fits is dropped with aPREFAB_OVERRIDE_STALEwarning naming exactly what and why).revertPrefabOverride(CLIprefab revert <scene> <entity> [component] [path], MCPrevert_prefab_override) restores the prefab’s own value(s) and clears the matching override records. A structural edit inside an instance (add/remove an entity or component) can’t be merged, so it detaches that instance instead of guessing (PREFAB_INSTANCE_DETACHED). Editor: ember override dots + per-field Revert buttons in the Inspector, and a running override count + Revert all on the instance banner. See prefabs.md. - In-scene particle preview: a Scene View toolbar Particles
toggle simulates the currently-selected entity’s
ParticleEmitterlive, directly over the canvas, without pressing Play — driven by the same seeded, deterministicEmitterStatestepper the real runtime uses (not an approximation), so previewingrate/spread/gravity/color edits while dragging Inspector fields matches Play and exported builds exactly. See editor.md. - Bulk asset import:
importAssets(CLIimport asset <paths...> [--recursive], MCPimport_assets) imports many files — or a whole folder, recursively — in one atomic undo/journal entry, auto-suffixing name collisions instead of failing the batch and reporting any bad file inskipped(with a code and message) rather than aborting everything else. Editor: the Assets panel’s file picker takes multiple files and the whole panel accepts folder drag-and-drop, both funneling through the same command and summarized in one toast. See editor.md. - Ledger fixes:
create scriptgains--no-format(parity withedit-script’s existing flag — CLI-created scripts could not previously opt out of house-style reformatting), andset-settings’s boolean-flag parsing (--format-on-save, etc.) is now strict instead of accepting anything JS treats as truthy. - Three existing examples were extended to showcase the new systems
rather than adding an 11th: ember-horde gained a real
Enemyprefab with a live-linked “Elite Enemy” override instance, sky-courier gained acourier-motionstate machine driving its idle/walk animation off amovingparam, and glow-caves gained blob47-autotiled cave terrain. - Registry grew 65 → 70 commands (
setTileAutotile,createStateMachineAsset,updateStateMachineAsset,importAssets,revertPrefabOverride); MCP grew 64 → 67 command tools (69 tools total includingscreenshot/get_agent_instructions).
Shipped in v0.11.0
- Script hot-reload during play: saving a script while the editor is
playing swaps its compiled code into every live entity running it,
preserving
ctx.vars, pending timers, and running tweens.onStartdoes not re-run on reload; an error-disabled script re-enables on a successful reload; a compile failure leaves the old code running unchanged. Pinned caveat: existingctx.events.onsubscriptions keep firing their old closure (reload can’t re-register them) —onEventalways resolves to the newest code. Works for external CLI/MCPedit_scriptcalls too, via the command journal, not just the editor’s own Code panel. See scripting.md. - Live Inspector property patching during play: an Inspector edit
dual-writes — always saved and undoable through the normal
setComponentProperty/setPropertiescommand, and, while playing, live-patched straight into the running preview. Closes the long-standing “ambientLightneeds Stop/Play” gap. Property writes from an external CLI/MCP session apply live too, resolved through the command journal. Structural changes (new/removed entity or component, reparenting, and the like) can’t be live-patched — they raise a “Scene changed — Restart” toolbar badge instead of guessing. - Runtime error → exact line: a script error logged to the Console during play is clickable, jumping the Code panel to the failing line. Lua errors always carry a line; JS errors resolve one on a best-effort basis (some have none).
- Format-on-save:
editScript/createScriptreformat to Hearth house style automatically — StyLua (2-space indent, 100-column) for.lua, Prettier defaults for.js— unlessformat: falseis passed or the project’scodeStyle.formatOnSaveis off. CLIhearth script format [path] [--all], MCPformat_script. hearth scriptCLI group:edit|check|format|search|replaceis the forward surface for every script-editing verb; the older top-leveledit-script/check-scriptstay as aliases.- Cross-script search/replace:
searchScripts/replaceInScriptscommands (line-based plain-text or regex matching, no multiline patterns) — CLIhearth script search|replace, MCPsearch_scripts/replace_in_scripts, and an editor panel (⇧⌘F). Replace always previews per-file match counts withdryRun/--dry-runbefore writing; a real apply writes verbatim (not reformatted — runscript format --allafter) and is one undo entry regardless of how many files it touched. - Editor: Code panel tabs: scripts open as tabs (soft cap 12) with
independent per-tab undo history,
ctx.hover docs (signature + description + example, off the sameCTX_APItable the autocomplete and docs use), in-file search/replace (Mod-F, CodeMirror’s own search panel), and a format-on-save toggle wired to the project setting. - WS-status dot: a small toolbar indicator for the editor’s WebSocket link to the local project server (connected / reconnecting).
- Registry grew 62 → 65 commands (
formatScript,searchScripts,replaceInScripts); MCP grew 61 → 64 tools (format_script,search_scripts,replace_in_scripts).
Shipped in v0.10.0
- Post-processing system:
Camera.postEffects, an array of up to 8 screen-space filters (bloom,crt,vignette,chromaticAberration,pixelate,colorGrade), rendered in stack order as hand-written Pixi GLSL filters, cached per view and only rebuilt on a stack-shape change (param edits just refresh uniforms). Paired with per-spriteSpriteEffects(outline/hit-flash/dissolve — every field defaults to a no-op) andctx.effects.flash(color?, seconds?)for a one-entity hit flash, deterministic linear decay, no RNG. TheassertPostEffectplaytest step checks presence of an effect type on the main camera. Both work identically in the editor preview,hearth screenshot, and exported games — no editor-only preview effect. See effects.md. - Editor: Code panel: a single-document script editor (CodeMirror 6,
lazy-loaded so the CM6 chunk never lands in the main bundle until
opened) with
ctx.autocomplete generated from the sameCTX_APIarrayhearth inspect apiand the docs use, inlinecheckScriptlint, and external-change follow (a dirty buffer never gets silently overwritten — a conflict banner offers Reload/Keep mine instead). - Editor: Live panel + Pause/Step: a read-only 10Hz runtime inspector
(entity transform/velocity/timers/tweens/recent events, including
runtime-spawned entities) alongside new toolbar Pause (freezes the
running game in place) and Step (advances exactly one fixed frame
while paused) controls, for frame-by-frame debugging.
SceneRuntime.getSchedulerSnapshot/stepFrameback both. PostEffectsField+ one-undo corner drags: a typed Inspector control forCamera.postEffects(per-effect cards, typed number/color fields, reorder, an 8-entry cap) — no raw JSON, ever. Also fixed: a corner transform-drag on aSpriteRenderer/boxCollider(which edits two separate scalar fields) now commits as one undo step instead of two, using the newsetPropertiesbatch command internally.- Strict property paths +
setPropertiesbatch:setComponentPropertyand the newsetProperties(set several dot-path properties on one entity in a single undo step, all-or-nothing validated; CLIset-many, MCPset_properties) both now reject an unknown dot-path segment with a did-you-mean suggestion and the valid-keys list, instead of Zod silently stripping it and corrupting the write.hearth validatealso warnsUNKNOWN_COMPONENT_KEYon scene files with a stray key. Registry grew 60 → 62 commands, MCP 59 → 61 tools. checkScriptcommand: syntax-check script source or an existing script file without saving — read-only, a pre-flight beforeeditScript/edit-script/edit_script. CLIcheck-script, MCPcheck_script; also what the Code panel’s inline lint runs./api+ WebSocket Origin/Host enforcement: the local project server’s/api/*routes and/api/wsupgrade now reject a presentOrigin/Hostheader that doesn’t resolve to a loopback hostname — closing a hole where a hostile webpage could otherwise drive the local dev server just by pointing a browser tab at its port (no Origin header at all, e.g. from the CLI or MCP server, is still allowed). See agent-panel.md.- Editor bundle code-split: the Agent panel’s embedded Terminal
(
@xterm/xterm) and the spritesheetSliceDialognow lazy-load withReact.lazy, alongside the already-lazy Code panel editor — the editor’s main chunk dropped from 1,251,480 B to 910,263 B (-27.3% raw, -26.3% gzip). See performance.md. - Seeded ids + CI clean-tree check: example generation now uses
deterministic seeded id generation so regenerating the bundled examples
is byte-reproducible, and CI verifies the working tree is clean after a
fresh
npm run build:packages && node packages/examples/generate.mjs. Tilemap.gridsize cap: rows/columns are capped at 1024 (TILEMAP_MAX_DIM), enforced at the schema level, shared byresizeTilemap/fillTilemapRect.- The all-Lua
ember-arcadeexample (the 10th) is a showcase built to exercise the whole post-effects/SpriteEffects surface: a stackedCamera.postEffectslook,ctx.effects.flashhits, and a dissolve death animation.
Shipped in v0.9.0
- Prefabs (tracked stamps): reusable entity templates.
createPrefabserializes an entity’s full descendant subtree into a new prefab asset (assets/prefabs/<slug>.prefab.json, normalized local ids, root-first);instantiatePrefabplaces a fresh, freshly-id’d copy into a scene;updatePrefabpushes a modified instance’s subtree back onto the asset;syncPrefabInstancesrebuilds every tracked instance (one scene or all) from the current payload, preserving each instance’s id/name/position/ enabled state but replacing its whole descendant subtree. Registry grew 56 → 60; CLIhearth prefab create|place|update|sync, MCPcreate_prefab/instantiate_prefab/update_prefab/sync_prefab_instances, four newPREFAB_*validation codes, andctx.scene.spawnPrefab(name, opts?)for runtime spawning (returnsnil/nullon an unknown name; destroying the returned root does not cascade to its children). No live linking, no per-field overrides — a sync is an all-or-nothing rebuild by design. Editor surfaces: Hierarchy’s “Save as prefab”, Assets panel’s “Add to scene”/“Sync instances”, and Inspector’s “Update prefab”/“Sync all” banner on a selected instance. See prefabs.md. - Editor friendliness: human-facing chrome dropped engine jargon
(Toolbar/Changes-panel copy: Snapshot → Checkpoint, Diff →
Review/Changes; agent-facing CLI/MCP names are unchanged) and
gained visible Undo/Redo buttons in the main toolbar (previously only
in the Changes panel). A new central keybind registry
(
apps/editor/src/keybinds.ts) is the single source of truth for every shortcut and drives a?cheat-sheet overlay, so the dispatcher and the documentation can never drift apart. New bindings: ⌘D duplicate, Delete removes the selection, F focuses the camera on it, arrow keys nudge 1px/10px, and ⇧⌘S checkpoints (⌘S alone just logs a “saved automatically” reassurance and swallows the browser’s Save dialog — binding checkpoint to plain ⌘S would silently reset the review baseline on a habitual keypress). The Scene View also gained direct-manipulation transform handles: 8 resize handles + 1 rotate handle on the current selection, targetingSpriteRenderer/box-or-circleCollider/Transform.scaleby priority, one undo step per gesture except a corner drag on a sprite or box collider (which edits two separate scalar fields, so it commits two). See editor.md.
Shipped in v0.8.0
- Embedded agent panel: the editor hosts a real terminal
(
@lydell/node-pty+@xterm/xterm) running the user’s ownclaudeCLI, pre-wired to the project by merge-writing ahearthentry into.mcp.json. A 4-tier permission-mode picker, install/detect forclaude/codex, an always-available plain-shell “Open Terminal” for Codex or anything else, a live activity timeline fed by the command journal, and Snapshot/Review changes/Revert session header actions. The panel embeds a genuine, unmodified CLI in a real PTY and never touches its stream, credentials, or flags beyondcwdand standard MCP config — see agent-panel.md for the full subscription-safety position, permission-mode table, and troubleshooting. - Command journal + external-change awareness:
.hearth/log/ commands.jsonlrecords every mutating command (plusrunPlaytest/validateProject) from any session — CLI, MCP, or editor — independent of undo/redo history;listJournalcommand,hearth log, MCPlist_journal. The editor now watches this file over a WebSocket channel and live-reloads/refreshes whenever an external agent session changes the project, instead of going stale until a manual reload — this benefits every agent workflow, not just the panel, and structurally fixes the earlier drag-drop-import staleness gap. See project-format.md. - Content scale & published performance numbers: a headless benchmark
harness (
npm run bench) with results and honest guidance in performance.md; a spatial-hash broadphase, a per-entity tilemap-collider cache, a frame-scoped entity-list cache, and particle-object pooling — all bit-identical to prior behavior (golden determinism tests pin full-run state hashes) — turned the worst synthetic scenario (1500 colliders) from over 2x the 60Hz frame budget to comfortably under it (13.5x mean speedup). Also: pointermove reflow coalesced to once per rendered frame instead of once per native event. - Scene management surfaced end to end:
duplicateScene(fresh entity ids, optional--with-playtestscloning) and a newduplicateEntity(deep-copies an entity and its full descendant subtree with fresh ids in one command) are now real CLI verbs, MCP tools, and an editor scene-menu (⋯ next to the scene picker: Duplicate/Rename/Set as initial/Delete) — closing the gap where several of these existed only as core commands.renameScenealso rejects collisions with an existing scene the waycreate/duplicatealready did. - Tilemap editing ergonomics: batched
paintTiles/fillTilemapRect/resizeTilemapcore commands (one undo entry per stroke, not one per cell), CLIpaint tiles/fill tiles/resize tilemap, matching MCP tools, an editor Scene View paint tool (palette + click/drag paint + shift-drag rect-fill), and a typedtileAssetsInspector control (a char → asset-picker row list) replacing the old raw-JSON textarea. - Gamepad axis hysteresis: digital gamepad-axis-crossed-threshold
bindings latch with ±0.05 hysteresis around the effective threshold so
stick noise can’t flap synthetic key codes or
justPressed. See input.md. hearth delete assetCLI verb forremoveAsset(with--keep-file), closing the last CLI delete-verb parity gap.- The all-Lua
ember-hordeexample (the 9th) is a survivors-like proof of the new ceiling: waves of enemies pathing toward the player up to several hundred concurrent, pooled hit-spark particles, camera shake, a HUD counter and pause menu, gamepad supported.
Shipped in v0.7.0
- Undo/redo: every mutating command (except
undo/redo/revertProject/snapshotProjectthemselves) is captured into a disk-backed, 25-entry history under.hearth/history/—undo/redo/listHistorycommands,hearth undo|redo|historyon the CLI,undo/redo/list_historyMCP tools, and Cmd+Z / Shift+Cmd+Z / Cmd+Y plus a History section in the editor’s Diff panel. Independent ofsnapshot/revert’s single diff baseline — this steps through individual changes. Asset files removed withdeleteFile: falseare left alone byundo, but a subsequentredomoves the still-on-disk file into.hearth/trash/so disk stays consistent with the model (round-trip safe — the matching undo pulls it back out). - Gamepad input + virtual axes:
inputMappingsgains named gamepad buttons (a/b/x/y/lb/rb/lt/rt/back/start/ls/rs/dpad-*), digital gamepad-axis-crossed-a-threshold bindings, and virtual analog axes (ctx.input.axis(name),-1..1, gamepad stick or keyboard fallback, per-axis or global deadzone). Browser-only (the Gamepad API doesn’t exist headlessly); playtests drive axes directly with thesetAxisstep. CLIset-settings --input-gamepad-buttons/ --input-gamepad-axes/--input-axes/--input-deadzone, matching MCPupdate_settingsfields, and an editor Input panel (key capture, gamepad dropdowns, axis rows). See input.md. - Camera effects:
ctx.camera.shake/flash/fade/zoomPunch, all deterministic (seeded shake) and last-call-wins per kind;fadeis persistent and carries across scene switches. TheassertCameraEffectplaytest step counts calls per effect kind; results exposecameraEffectsandcameraOverlayAlpha. - UI widgets v2:
UILayout(stacking containers),UISlider,UIToggle,UIElement.focusable, and a full keyboard/gamepad focus system (ctx.ui.focus/getFocused/moveFocus/activate/adjust).onUiEvent’stypeunion grows toclick|press|release|enter|exit| drag|change|focus|blurwith an optionalvalue. Playtest stepsdrag/assertFocus. See ui.md. - The all-Lua
drift-cellarexample (the 8th) exercises the whole set: analog drift movement, wall-bump shake/flash, a gem run with a fade transition, and a pause menu with a focus-navigable slider and toggle. - Editor: enum dropdowns everywhere a schema field is a fixed set of string values, replacing free-text/raw-JSON entry in the Inspector.
Shipped in v0.6.0
- Asset pipeline v2:
importAssetprobes real image dimensions (PNG/JPEG/GIF/WebP/SVG) intometadata;sliceSpritesheetcuts an imported spritesheet into a named, typed frame grid stored on the asset itself (metadata.frames/metadata.grid), andcreateAnimationFromSheetbuilds animation assets from those frames (<sheetAssetId>#<frameName>refs);SpriteRenderer.framedraws a sliced frame instead of the whole texture, and every textured sprite is now tintable via its existingcolorfield (default#ffffffis a no-op, so nothing existing changes color). See architecture.md for the metadata/accessor design and assets.md for the full guide. - Music: a single shared, crossfading music channel —
ctx.audio.playMusic/stopMusic/setMusicVolume,AudioSource.musicfor scene-start autoplay, survivesctx.scenes.loadscene switches, streamed through a real<audio>element in the browser host rather than fully decoded up front — plus theassertAudioCountplaytest step (filter by asset/action/music, checked against equals/min/max). - Fonts:
.ttf/.otf/.woff/.woff2import asfontassets, loaded viaFontFaceat mount and registered under exactly the asset’s name, soText.fontFamilyreferences an imported font like any other asset name — no@font-faceCSS to hand-write. - Editor: a spritesheet slicing dialog and asset-browser thumbnails
(frame grids, font samples, animation previews), plus
Text.fontFamilyandSpriteRenderer.framepickers in the Inspector. - The all-Lua
sky-courierexample is the first built from imported binary assets rather than procedural SVGs: a sliced PNG walk/idle spritesheet, a streamed WAV music loop, and an imported.ttfHUD font.
Shipped in v0.5.0
- Physics v2:
mass/restitution/frictiononPhysicsBody(restitution/friction combine per contact pair by taking the max of both sides; restitution is suppressed below a 20 px/s incoming speed to kill micro-bounce jitter), named collision layers (Collider.layer/collidesWith, both sides must list each other,'*'matches any), one-way platforms (Collider.oneWay), and circle-accurate collision resolution (true circle-circle/circle-box/circle-polygon MTV, not bounding-box). Still a positional resolver deriving velocity response from the MTV normal, not an impulse solver — see architecture.md. - Script stdlib v2:
ctx.math(16 pure vec2/color helpers — add, sub, scale, normalize, lerp, hex/RGB conversion, …) andctx.events/onEvent(synchronous, deterministic pub/sub; an 8-deep recursion guard; subscriptions auto-removed when their owning entity is destroyed; theassertEventCountplaytest step plusevents/eventCountsin run reports). - Pathfinding: deterministic grid A* —
ctx.scene.findPath(from, to, opts?)in scripts,hearth inspect pathon the CLI, and theinspect_pathMCP tool, all resolving to the same core module so an offline query matches what a running script gets. - The all-Lua
bounce-patrolexample exercises the whole set headlessly: a bouncy ball, friction-contrasted floor strips, a one-way ledge, a findPath-chasing kinematic patroller, and coin pickups driving actx.events-based score UI.
Shipped in v0.4.0
- Rendering v2:
Light2D+Camera.ambientLight(a lightmap pass that costs nothing when unused — existing projects with no lights render byte-identical to before),LineRendererpolylines, deterministicParticleEmitters (ctx.particles.burst/count, per-emitterseed, and theassertParticleCountplaytest step), a toggleable debug-draw overlay (colliders, velocity vectors, light radii — off by default, never in exports), andSpriteAnimatorplayback (ctx.animate, frames fromcreateAnimationAsset). The all-Luaglow-cavesexample exercises the whole set headlessly. - Screenshot capture for agents:
hearth screenshot [scene] [--debug](and the MCPscreenshottool) renders a deterministic PNG via headless Chromium, so an agent can see its work. - Editor: gizmos for every new component (light radius, line points,
particle emitter bounds, animator frame preview), a uniform Vec2
point-list Inspector control (used by
LineRendererand colliders alike, replacing raw JSON editing), and a preview debug-draw toggle.
Shipped in v0.3.0
- Lua scripting, first-class:
hearth create scriptemits Lua by default (--language jskeeps JavaScript, which remains fully supported); the same sandboxed Lua 5.4 VM (wasmoon) runs in the editor preview, headless playtests, and exported games, with the identicalctxAPI in both languages. - Scene management + script stdlib v1:
ctx.scenes.load(user-built menus/start screens),ctx.timers,ctx.tweens, seededctx.random, persistentctx.save/ctx.load, andctx.cameracontrol — one deterministic surface, documented viahearth inspect api. - No-chrome export: shipped games boot straight into the initial
scene; loading visuals come from
buildSettings.loading(reachable via the newupdateSettingscommand); audio unlocks silently on the first natural input. Zero Hearth branding anywhere a player can see. - Structured script diagnostics:
hearth validatereports script syntax errors with file + line for both languages. - Playtests gained
seed,click, andassertScenesteps; the all-Luaember-trailexample proves the whole stack headlessly.
Shipped in v0.2
- Dockable editor workspace (drag tabs, splits, persisted layouts).
- Game UI:
UIElementcomponent (anchor + offset screen space,interactive+onUiEventpointer events). - Convex polygon colliders with SAT physics.
- Audio:
ctx.audio.play/stop, functionalAudioSourceautoplay,audioEventsin headless run reports, and procedural sound effects (hearth create sound --preset coin). - Web export:
hearth export web [--single-file] [--zip]— static self-contained builds, itch.io-ready zips.
The road to 1.0
The end goal, stated so every release aims at it: a solo dev or an agent can take a 2D game from empty project to a polished, distributable game without leaving the tool or hitting a wall. The feature set is complete: v0.13’s ship-your-game wave closed the last feature gap, v0.14’s tightening wave made every surface actually work, v0.15’s game-craft wave sharpened how agents make games with those tools, and v1.0.0’s hardening wave sealed the guarantees. Each release completed a loop rather than scattering features; anything that didn’t serve that loop waited (see Non-goals) and still does.
Wave L2 — agent game-craft (shipped v0.15.0)
No engine features — a focused pass on how well agents actually make games now that every tool works and each project ships the house skill.
- A game-craft skill: a second fact-checked skill,
.claude/skills/hearth-craft/SKILL.md, teaches juice and game-feel as concretectxrecipes (camera shake/flash/fade/zoom-punch, particle bursts, tweens, layered sound, animation state machines) plus game-UX conventions and an agent quality bar. It scaffolds into every new project and was backfilled into all 13 example/template fixtures — each now carries bothhearthandhearth-craft— drift-gated against the canonical copy. - An asset-sourcing playbook: where to get sprites, audio, and fonts an agent is licensed to ship — Kenney (CC0), itch.io’s CC0 filter, OpenGameArt, Freesound, and OFL/Apache Google Fonts — with the licensing rules and a fetch → import → verify loop. Every source and license claim was fact-checked live.
- One-command connectability: the Agent panel launcher reached parity
across Claude Code, Codex, OpenCode + Ollama, and Hermes —
detect → prepare → launch with a per-tool native MCP config writer
(
.mcp.json,opencode.json,codex mcp add, Hermes YAML) and already-configured detection so it never double-writes. Every embedded terminal now guarantees thehearthCLI onPATHvia a shim, so agents runhearth …with no setup. - Journey-grouped docs + guides: the docs reorganized into a journey-grouped IA (get started / build / script / ship / agents / reference), with seven new guides — per-agent connect pages (Claude Code, Codex, OpenCode, Hermes, any agent) and ship-it destination guides (web hosting, desktop, with honest Gatekeeper/SmartScreen notes).
- AI-native positioning: the README and website say what Hearth is — an AI-native game engine whose knowledge layer (the house skill, the verification loop, journal review, determinism) is the story; MCP and the CLI are plumbing, one of several front doors.
- Counts unchanged: 71 commands, 72 MCP tools — no new engine commands this wave. No format changes; v0.14 projects open unchanged.
Wave M — final hardening & verification (shipped v1.0.0)
No features. The format-stability guarantee is now real and documented (see
project-format.md):
projects load through an automatic migration rail (empty today, by design —
the point is the tested mechanism 1.0 promises), a project stamped newer
than the engine is rejected with UNSUPPORTED_PROJECT_VERSION instead of
being silently mangled, opening an older project never writes to disk, and
the first edit advances its hearthVersion stamp — all covered by upgrade
tests against real 0.13.0 and 0.14.0 fixtures. Closing the window (or Cmd+Q,
or the browser tab) with unsaved script edits now confirms before discarding
— the one non-autosave surface no longer loses work silently. CI gained a
performance regression fence: the bench harness’s two heaviest scenarios run
three times and fail the build if the median exceeds loose absolute budgets,
a tripwire for order-of-magnitude losses (like losing the spatial-hash
broadphase), not a profiling target. A docs completeness pass closed real
gaps: twelve MCP tool names were missing from mcp.md,
cli.md gained the full validation-code list, and the README’s
getting-started commands were re-verified against the shipping CLI. The Pixi
loader also gets an explicit format hint for the editor’s extensionless
asset URLs, retiring a long-standing console warning. Remaining editor
ergonomics ideas (hierarchy multi-select, tree filtering, a dedicated
tilemap-grid editor) moved to the parallel track below rather than blocking
1.0.
Parallel track / post-1.0
None of these block 1.0; they ride alongside or after it:
- Custom chat UI over the agent — only when it can be built API-key-only (Claude Agent SDK) or subscription terms allow a wrapped UI; see agent-panel.md. The embedded terminal is the answer for now.
- Custom shader assets (user GLSL) — the curated
postEffectsset covers the common cases; user-authored shaders need a sandboxing and asset-format design first. - TypeScript scripts with a compile step and typed
ctx; multi-instance components (formatVersion: 2); MCP resources. - Incremental spatial hash and further scale headroom (physics islands/sleeping, worker physics, culling) — only if a real benchmark demands it (see performance.md).
- Multi-select transform handles; finer-grained undo for continuous gestures, if whole-command granularity proves too coarse.
- Agent-facing “explain this scene” structured summaries; plugin/component SDK; multiplayer-friendly determinism research (input serialization + rollback).
Further out — directional, no dates, none of it before 1.0:
- AI asset generation integrations — generate sprites, UI elements (buttons, panels, icons), and sounds on request. These would be optional integrations that agents and the editor call from outside, same shape as the agent panel: the engine core stays model-agnostic and fully usable offline (the no-built-in-LLM-calls non-goal stands).
- Community asset marketplace — a place to sell or share sprites, sounds, fonts, prefabs, and templates, with a structured CLI/MCP-first interface so agents can search it, check licenses, and pull assets straight into a project as smoothly as they import local files. A natural Hearth Cloud surface; the local engine never depends on it.
- Multiplayer games — building on the determinism research above (input serialization + rollback).
- Hearth Cloud — hosted services on top of the free local core (builds, collaboration, the managed agent, the marketplace). Nothing local ever gets gated behind it.
Non-goals
- A visual logic editor. Cut, not deferred: agents write code, and humans get a real code editor and an approachable scripting language. A node graph is a second, worse programming language to maintain, and it drags every future feature into building two UIs.
- Competing with Unity/Godot on 3D, shaders, or console targets.
- Built-in AI/LLM API calls. Agents connect from outside via MCP/CLI; the engine stays model-agnostic and fully usable offline.
- Cloud project storage. Projects are local files; use git. (A hosted Hearth Cloud may exist far down the road — see “Further out” above — but the local engine never requires it.)
- Concave polygon decomposition (split concave colliders into convex pieces yourself) and audio mixing buses.