// zanna studio

The workbench,
written in the
language it edits.

Zanna Studio is where you edit, build, run and debug Zanna projects: a Zia editor with full language services, visual 2D and 3D scene editors, a VM debugger, an integrated terminal, a Git view — and a Play button that runs your game inside the editor window. It is written in Zia and runs on the same GUI and 3D runtime it ships to games.

// dogfood

455 Zia files, one C file

The most direct evidence that the platform works is that its own IDE is built on it. Studio’s source tree is Zia essentially all the way down.

By the numbers

  • 455 Zia source files, around 150K SLOC
  • One C file in the whole tree — a native menu probe used by tests
  • Its manifest declares lang zia, and it builds to a native binary like any other Zanna project
  • It grew from 26K to 150K SLOC over a single development cycle

On the same runtime as your game

The widgets come from Zanna.GUI. The 3D viewport is a Zanna.Graphics3D.Canvas3D. The 2D scene surface is Zanna.Game2D.SceneDocument. There is no private editor framework underneath — Studio is a first-class client of the same library a game links against.

That means editor bugs and engine bugs are frequently the same bug, and fixing one fixes both.

// editing

Code first

Zia editing is the part of Studio with the deepest coverage. Language services come from the compiler’s own semantic model rather than a separate index, so what the editor tells you is what the compiler thinks.

Zanna Studio editing scene_editor_3d.zia, with syntax highlighting, line numbers, a document tab and a status bar
Studio editing its own source. This is scene_editor_3d.zia — the 3D scene editor’s implementation — open in the editor it implements. Captured from a real build through the GUI test harness.

Language services

  • Completion, diagnostics, hover, signature help
  • Document and workspace symbols, go to definition, find references
  • Incoming and outgoing call hierarchy, rename
  • Organize Binds, Extract Local Variable, Extract Function, Inline Local Variable
  • Formatting, and a split editor (v1: exactly two panes, distinct documents)

Working on a project

  • Multi-root project explorer, Quick Open, project-wide search with Replace All
  • Side-by-side diff against saved, against HEAD, or against any commit
  • Build and run jobs, and a new-project wizard with console, GUI and library templates
  • Rebindable shortcuts, settings search, session restore, crash-recovery snapshots
  • Dockable layout: the sidebar drags to either side, panels group left, bottom and right

// 2d scene editor

Tiles, objects and typed properties

Opening a .scene2d document mounts a visual editor instead of a text buffer. The atlas is decoded and drawn for real, so the tiles you paint with are the tiles the game will draw.

Zanna Studio's 2D scene editor showing a Xenoscape region with rendered tiles, a tile palette and a scene property inspector
XENOSCAPE region 01, open for editing. Rendered tile layers, the real scene-tile-atlas.png in the palette, and the scene’s typed properties — playerStartX, playerStartY, region, theme — in the inspector on the right. 150×17 tiles, one layer, 82 objects.
region-01.scene2d — Paint armed
The same 2D scene with the Paint tool selected, the tile palette showing the decoded atlas frames, and the tool chrome active in the command bar.
Tile editing works like an image editor. Paint, Erase, Rectangle, Fill, Line and Ellipse, all previewing the real atlas frames they are about to write. Box-select a region and copy, cut, delete, paste it, or turn it into a stamp and paint the whole block per stroke. Holding Alt in any of them samples the tile under the cursor without switching tools. Autotile resolves on the canvas using the same neighbour rules the runtime uses, so a wall you paint reads as a wall.

Authoring

  • Layers with visibility, lock, solo and opacity, reordered by drag
  • Select, Paint, Erase, Rectangle, Line, Ellipse, Fill and Object tools, each with an image-true translucent preview before it commits
  • Point selection plus an inclusive marquee with add and toggle modifiers
  • Object hierarchy with transactional before/into/after drops and a cycle-safe parent chooser
  • Pixel and tile nudging, axis alignment, deterministic distribution
  • Rulers, guides, grid, fit and 1:1, pointer-centred zoom

Tile behaviour

  • Collision kind and typed per-tile properties
  • Per-frame tile animations
  • 16-variant autotile rules with a live preview that matches the runtime exactly
  • Camera and lighting inspector groups
  • Import from Tiled, in both JSON and TMX form

// 3d scene editor

Hierarchy, gizmos and materials

A .scene3d document opens on a perspective viewport backed by a windowless Canvas3D — the same renderer the game uses, running inside the editor.

Zanna Studio's 3D scene editor showing an Ashfall campaign mission with collider wireframes, node markers, a view-orientation cube and a scene inspector
Ashfall mission 03, framed in the viewport. Authored mesh nodes with collider wireframes and typed marker nodes, the view orientation cube, and the scene inspector’s lightmap bake, history and environment groups.

Transform and hierarchy

  • Move, Rotate and Scale gizmos with a per-scene Local/World toggle
  • XY, XZ and YZ plane handles, projected rotation rings, snapping at 0.5 units, 15° and 0.1 scale
  • Hierarchy tree with multi-selection, retained collapse state and transactional row drops
  • Exact preserve-world reparenting, with preserve-local as an explicit opt-out; singular or shear conversions roll the whole group back rather than silently approximating
  • Triangle-accurate picking, with Alt-click to cycle overlapping hits, and marquee selection

Content

  • PBR material authoring with truthful Mixed values across a multi-selection, and batch texture map assignment
  • All seven runtime light types, with hierarchy and viewport markers
  • Terrain as canonical 33×33 heightfields, with Raise, Lower, Smooth and Flatten brushes
  • Placement helpers: Drop, Surface, Align to surface normal, and Vertex snapping
  • Collider authoring with wireframe overlays; camera nodes with look-through and a preview inset
  • Shaded, Wireframe and Shaded+Wire viewport modes

// seeing your game

The editor frame is the game frame

Most level editors show you markers in a grey void and leave you to imagine the rest. Studio can draw the level. Your project describes, in a few lines of checked-in configuration, what its sprites, models, materials, backgrounds, terrain, sky, water and colour grade look like — and the editor reproduces them. It never runs your game to work any of it out, and nothing it draws is ever written back into your scene.

region-01.scene2d — Game View, 1280×720 project output
Zanna Studio in Game View: a platformer level drawn with its real tiles, coins, mushrooms and player sprite, framed at 1280 by 720, with the object list showing sixteen named enemies and the tile palette below the canvas.
Game View, 2D. Editor chrome — grids, rulers, guides, markers, selection outlines — is gone; the project’s own background art, tiles and sprites remain, composed at the output size the project declares. Leaving restores your camera, zoom and scroll exactly.
mission-03.scene3d — Game View, 1600×900 project output
Zanna Studio in Game View on a 3D mission: a fogged road with mech silhouettes, letterboxed to the project's 1600 by 900 output aspect inside the viewport pane.
Game View, 3D. The scene is driven at the game’s real aspect ratio and matted into the pane, so resizing a dock no longer changes the composition you are judging — and a screenshot taken here is directly comparable to one from the running game.

What a project can declare

  • A spawn marker draws as the enemy sprite it will become; a prop marker as the actual model
  • A 2D scene sits on the game’s own background art, with up to 64 variants selected by a scene property
  • A 3D scene opens at the player’s start pose and eye height, under a sky generated from the game’s own palette
  • The scene is surrounded by the terrain and water the game builds at runtime, in up to 32 conditioned layers
  • The viewport runs the project’s own tone mapping, bloom, colour grade, vignette and FXAA
  • Draw order follows the game’s: a bridge or canopy covers what passes under it, and clicking picks the sprite you can see on top

Rules, not a runtime

All of it comes from declarative preview profiles in the same scene-components.json the schema lives in. They are data you check into source control and review in a pull request, not a plugin and not a script the editor executes.

Everything Studio draws for your benefit is presentation only. It never enters your scene’s bytes, its revision, its dirty state or its undo history. Metadata that is missing or the wrong type falls back to ordinary editor lighting rather than failing.

Effects that cannot promise the same result on every backend — SSAO, screen-space reflections, depth of field, temporal AA, motion blur — are deliberately left out of the preview chain rather than shown differently per platform.

mission-03.scene3d — Focus layout
The same 3D mission with Studio's outer docks and scene chrome hidden, so the viewport takes nearly the whole window.
Focus. Shift+Space hides the outer docks and the scene chrome so the viewport takes the window, and restores the exact layout you had on the way back.

// play

Your game runs inside the editor

Press Play and Studio builds the project if its sources changed, launches it, and shows the running game’s frames in the scene viewport while the editor keeps working around it. On macOS, Windows and Linux — including both Wayland and X11.

How it works

  • A project declares a run-profile of vm or native
  • A native project builds first if it needs to; a VM project launches straight under the interpreter
  • Studio injects a shared-memory channel; the game’s frames arrive in the 3D viewport or on the 2D canvas
  • Mouse and gameplay keys forward while the pane has focus
  • The game’s own window stays hidden, so you get one window and not two
  • The pane says building, waiting, live, stopped or exited rather than guessing

And the slower loop, when you want it

Run Scene (Ctrl+R) launches the game as a normal process at the scene you have open, using the project’s ordinary run configuration.

A documented --scene-watch convention closes the loop: a game that honours it reloads the scene whenever the file’s timestamp changes, so saving in Studio updates the running game. A torn write or a file that does not parse keeps the previous scene rather than crashing the run.

Nothing about either path touches the scene you have open.

// editor to game

What you author is what loads

A scene file is data, not a serialised object graph — it never instantiates game classes by name. Game code loads a scene and maps typed metadata onto its own entities, which keeps the editor from dictating a game’s architecture.

Projects declare their own component templates in a scene-components.json file. Studio reads it and offers those components in the editor with correct types and defaults, so a designer places an Enemy Spawn rather than remembering that enemy.archetype is a string:

{
  "version": 13,
  "components": [
    {
      "name": "enemy-spawn",
      "label": "Enemy Spawn",
      "target": "both",
      "createObjectType": "enemy",
      "createNodeName": "Enemy Spawn",
      "fields": [
        {"key": "enemy.archetype", "label": "Archetype", "type": "string", "default": "grunt"},
        {"key": "enemy.level",     "type": "int",    "default": 1},
        {"key": "enemy.radius",    "type": "float",  "default": 4.0},
        {"key": "enemy.active",    "type": "bool",   "default": true}
      ]
    }
  ]
}

Round trip

  • Add Missing applies a component across a whole selection as one undoable action, preserving authored overrides and aborting the entire batch on a type conflict
  • Create Object / Create Node places a correctly typed object with all its defaults and its project preview in one edit
  • Game View strips editor chrome in one reversible action and renders the scene at the project’s authored output size
  • Run Scene (Ctrl+R) launches the owning project with --scene <path>

Proven against the old way

Two complete games were rebuilt from Studio-authored scenes: XENOSCAPE’s ten 2D regions and Ashfall’s nine 3D missions plus its hub. In XENOSCAPE, roughly 3,500 lines of level-builder code became a ~120-line loader.

Both ship parity probes proving each authored scene reproduces the code-built level byte for byte through the canonical serialiser. That is what makes the scene files, rather than the code, safe to treat as the source of truth.

// running and debugging

Debugger, terminal, source control

VM debugging

  • Backed by zanna run --debug-adapter
  • Breakpoints, stepping, run to cursor, call stack
  • Locals, watches, evaluate, conditional breakpoints and logpoints
  • Structured expansion of collections and class-instance fields
  • Boxed struct payloads stop at a leaf rather than expanding, and direct-IL and BASIC sessions have layout-sidecar gaps

Terminal and Git

  • An integrated PTY terminal covering the vim / less / htop escape-sequence table: alternate screen, DECSTBM scroll regions, bracketed paste, SGR truecolor
  • A Git source control view: status, staging, commit, paged history, per-commit diffs, and PTY-backed push and pull with in-app credential prompts

// plainly

What it isn’t yet

Studio’s own status document sets the rule that the documentation should be less optimistic than the code. These are the limits worth knowing before you form an opinion of it.

Known limits

  • Not a complete scene editor. Deferred: prefab per-field descendant overrides, multiple simultaneous 3D viewports, visual water and navmesh authoring, and advanced terrain splat, LOD and hole tools.
  • No GPU lightmap baking. The CPU path tracer is the only baker. Lightmap-only and baked-preview viewport modes, and MSAA, are unimplemented.
  • The offscreen viewport is only GPU-accelerated on macOS today. Metal has a real headless context; the OpenGL and Direct3D 11 headless contexts are still pending, so those platforms fall back to software for the editor viewport.
  • Not a full Git client. There is no merge or rebase orchestration and no ours/theirs conflict recovery.
  • BASIC is not as deeply served as Zia. Completion, diagnostics, hover and symbols are compiler-backed, but semantic navigation comes from a lightweight scanner rather than the compiler’s semantic model.
  • No detachable or multi-monitor windows. The floating panel group is in-window only.
  • No automatic unattended schema migration for project component schemas.
  • Cross-platform is intended, not verified. Studio’s own feature matrix lists it that way, and it is the honest status.