- isokit-wasm: wasm-bindgen build of the engine; a project in memory behind the same JSON API as `isokit studio`, plus the MCP tools - isokit-project: Store trait (FsStore folder, MemStore memory) under a generic Workspace; Project::from_files; shared Studio API module (api.rs) used by serve.rs and the WASM build; web_time for clocks in the browser - studio.js runs on a pluggable backend (window.IsoKitBackend); no Studio keys while a dialog is open - web/: projects in IndexedDB, new/demo/delete, zip export/import (stored and deflated), folder import by drop or picker, folders opened live via File System Access with outside edits followed, code editor, page build and preview, two tabs in step, isokit.mcp() and WebMCP registration - tools/build_web.py -> dist/web (static), tools/check_web.py (44 checks)
8.3 KiB
isokit
Isometric scene engine for B4L projects: site plans, plants and buildings drawn as SimCity-style views that you can pan, zoom, rotate, cut open by storey and click for details. One Rust binary does everything:
- Studio: a GUI editor in the browser for people who don't code. You add objects from a palette, drag them into place, and edit their properties in forms.
- Web Studio: the same Studio running entirely in the browser, with the engine compiled to WebAssembly. Nothing to install: projects live in the browser, or in a folder on disk (Chrome / Edge) that agents edit alongside.
- MCP server: AI agents (Hermes and others) edit the same scene with the same operations; their changes show up live in an open Studio.
- Build: a self-contained HTML page with every view prerendered, or standalone SVGs for documents.
- Engine and scripting: everything you can place is a prefab written in Rhai. Projects add their own prefabs, scripts and colours without touching the engine.
The engine is a Rust port of the hand-written JS views in cnx_vinyl
(fleet calculator), cnx_scuba (centre view) and mooban (estate view),
and it keeps their look: same projection, depth sort, face shading, palette and
viewer behaviour.
Quick start
cargo build --release # target/release/isokit.exe (one file, ~7 MB)
isokit new C:/dev/myproject/site-view # starter project
isokit studio C:/dev/myproject/site-view # opens the editor in the browser
isokit build C:/dev/myproject/site-view # dist/index.html, no server needed
Try the demo: isokit studio examples/demo.
Studio (for everyone)
| Do | How |
|---|---|
| Add | pick an object on the left, click on the map (Shift-click places several) |
| Select | click it on the map or in the object list |
| Move | drag it, or arrow keys by the snap step (0.1 / 0.5 / 1 / 5 m) |
| Turn | R / Shift+R or ⟲ ⟳ |
| Edit | the right panel: size, storeys, colours, solar, tooltip text, label, levels |
| Copy / delete | Ctrl+D / Delete; Ctrl+Z / Ctrl+Y undo and redo |
| Look | drag the ground to pan, Ctrl+wheel or pinch to zoom, ↺ ↻ rotate, Site / GF / 1F levels |
Every change is saved to scene.json at once. When the file changes on disk
(an agent, git pull, another editor), the Studio reloads it, and that change can
be undone like any other.
Web Studio (no install)
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli --version 0.2.129 # must match Cargo.lock
python tools/build_web.py # -> dist/web/ (static files, ~2.8 MB, 0.75 MB gzipped)
dist/web/ runs from any static web server (http or https, not file://).
The first visit opens a copy of the demo. Everything the desktop Studio does
works the same, because the page runs the same engine and answers the same
requests. In addition:
| Projects ▾ | list, open, delete; new project; demo copy; export as .zip; import a .zip or a project folder (drop it on the strip, or pick it) |
| Open folder live | Chrome / Edge: edits are written straight to the folder's files, and changes made there (an agent running isokit mcp <folder>, an editor, git pull) show up within 1.5 s and can be undone |
| { } Code | edit scene.rhai, lib/*.rhai prefabs, theme.toml, isokit.toml; Ctrl+S applies, script errors show with file and line |
| Build page | downloads the self-contained HTML page (a folder project gets dist/index.html like isokit build) |
| Preview | the built page in a new tab |
Projects in the browser are stored in IndexedDB (per browser and site; export
them to keep a copy). Two tabs on the same project follow each other. The 15
MCP tools also run inside the page: await isokit.mcp({jsonrpc: "2.0", id: 1, method: "tools/list"}), and browsers with WebMCP (navigator.modelContext)
get them as page tools.
Agents (MCP)
While the Studio runs, it also serves MCP over HTTP at http://127.0.0.1:7878/mcp.
Without the Studio, run isokit mcp <project> over stdio. In Hermes
(config.yaml):
mcp_servers:
isokit:
command: C:\dev\isokit\target\release\isokit.exe
args: [mcp, C:\dev\myproject\site-view]
Tools: project_info, list_prefabs, list_objects, get_object,
add_object, update_object, remove_object, duplicate_object,
apply_ops (batch, one undo step), set_params, render_view,
check_project, build_html, undo, redo. Bad input comes back with a
message that says what is allowed, e.g. the list of types or a prop's range.
A project
isokit.toml title, levels, parameters, theme files, prefab libraries
scene.json the objects (edited by the Studio and agents)
scene.rhai optional script, drawn before the objects
lib/*.rhai optional project prefabs (listed under prelude)
theme.toml optional colours and materials
An object in scene.json:
{ "id": "hall", "type": "building", "name": "Workshop", "x": -12, "y": 6, "rot": 90,
"props": { "w": 24, "d": 12, "floors": 2, "pv": true },
"tag": "Workshop", "info": "Two storeys, solar on the roof", "levels": [] }
Coordinates are metres: x points east, y north, z up. x, y is the centre of
the footprint, and rot turns the object in 90° steps. Levels: site (or any
other id) shows buildings closed, while gf, 1f, 2f … cut them open at that
storey.
Commands
| Command | |
|---|---|
isokit new <dir> |
starter project |
isokit studio [dir] [--port N] [--no-open] |
GUI editor + /mcp + /view live preview |
isokit mcp [dir] |
MCP over stdio |
isokit build [dir] [-o file] |
self-contained HTML (all levels × 4 rotations) |
isokit render [dir] -o x.svg [--level gf] [--rot 2] [--light] |
one view as SVG |
isokit check [dir] |
lint: unknown types, props out of range, script warnings, NaN, node budget, unstyled classes |
isokit prefabs [dir] |
object types and their properties |
Architecture
crates/isokit-core projection (4 rotations), primitives, sprites, depth sort, SVG (no deps but serde)
crates/isokit-theme TOML palettes / materials / rules -> CSS; built-in default theme
crates/isokit-script Rhai runtime: drawing API, prefab library (stdlib/*.rhai), scene document driver
crates/isokit-project project folder, document ops, export, check, Workspace (undo, reload), MCP tools
crates/isokit-cli the `isokit` binary: Studio HTTP server, MCP stdio, build/render/check
crates/isokit-wasm the engine for the browser (wasm-bindgen): a project in memory behind the Studio API
viewer/ isokit-view.js/.css: pan, zoom, levels, rotation, tooltips (any page can embed it)
studio/ the Studio GUI (served by `isokit studio`, and by the web Studio)
web/ web Studio shell: WASM backend for studio.js, projects, folders, zip, code editor
examples/demo demo project
The layers are independent. A Rust program can draw with isokit-core alone.
isokit-script reads sources from memory, with no disk access, and a
Workspace reads and writes through a Store (a folder on disk, or memory),
so the same code runs natively and in the browser. The Studio, the web Studio
and MCP all apply the same ops::Op JSON operations through a Workspace, and
both Studio front ends speak the JSON API in isokit_project::api.
Writing prefabs and scripts: docs/SCRIPTING.md. Plans: docs/ROADMAP.md.
Development
cargo test # engine, runtime, ops, demo, MCP
cargo clippy --all-targets -- -D warnings
isokit studio examples/demo --port 7890 --no-open &
uv run --with playwright python tools/check_studio.py <shot-dir> http://127.0.0.1:7890 # GUI + MCP end to end (Edge)
cargo clippy -p isokit-wasm --target wasm32-unknown-unknown -- -D warnings
python tools/build_web.py && uv run --with playwright python tools/check_web.py <shot-dir> # web Studio end to end
python tools/build_web.py --skip-cargo re-copies the front end (studio/,
viewer/, web/) without rebuilding the WASM. Code that runs in the browser must
not use std::time (it traps on wasm32-unknown-unknown): use web_time.
Set ISOKIT_ASSETS=C:/dev/isokit to serve viewer/ and studio/ from disk, so
you can edit the GUI without rebuilding. tools/css2theme.py converted the
original views' CSS into crates/isokit-theme/themes/default.toml.