# 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](https://rhai.rs). 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 ```bash 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) ```bash 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://`). A live build is at https://isokit.buildfor.life/: every push to `master` that touches the engine or the front end rebuilds and deploys it (`.gitea/workflows/deploy-web.yml`; server setup in `deploy/Caddyfile`). 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 `, 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 ` over stdio. In Hermes (`config.yaml`): ```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 ```text 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`: ```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 ` | 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 ```text 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 deploy/Caddyfile the web Studio's vhost on the buildfor.life web server (s1) ``` 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](docs/SCRIPTING.md). Plans: [docs/ROADMAP.md](docs/ROADMAP.md). ## Development ```bash 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 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 # 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`.