Making a game with Interverse Studio — the guide

Interverse Studio is the visual game maker for the Interverse engine. It runs in your browser (the Studio card on the hub) and as a Windows app. What you edit is the real engine, live — press ▶ Play at any moment.

1 · Start

TemplateViewWhat you get
Blank2DEmpty canvas + a hero
Garden ExplorerTop-downWalk anywhere, collect fireflies
Sunset Street2.5D · 3 screens wideA long street to journey down, camera follows
Blob Dash2DEndless runner with jumping + ramping speed
Slash Frenzy2DFruit-ninja tap-slashing
Blob ArenaTop-downAction survival waves
A Quiet Evening2DCozy room, stories, no fail state
Tiny QuestTop-down RPGNPCs, training, branching skill tree
Hilltop HopGravity run · wideRun + JUMP across 3 screens, camera follows
Firefly PartyMultiplayer co-opHost a room code, catch fireflies together
Hero's ErrandMenu → QuestTitle menu, village shop, find 2 chests, boss lair
⚔️ BlobvaleCo-op RPG · multiplayerMenu, class pick, shared world, wolves + a direwolf
🌱 BloomsteadCozy farmingPlant, harvest, sell at the stall — no enemies, no fail state
🕯️ HushfallHide & seek · multiplayerLight every lantern, reach the gate before the Seeker

Hero's Errand is the capstone — it uses almost everything in this guide at once (a start-button menu level, a shopkeeper NPC selling database items, two chests that count into a chests variable, a gate that only opens at 2, a boss with hearts and an ability, and a victory fanfare). Load it and read its ⚡ events and ⛓ Flow map to see how a full small game is put together.

First-person / 3D templates arrive when the engine grows a 3D renderer.

1¼ · ⋯ The toolbar, on any size screen

The top bar holds more than fits on a laptop, so it adapts instead of running off the edge:

  1. With room to spare, every control shows its full label.
  2. Tighter, the labels drop and the icons stay (each one has a tooltip).
  3. Tighter still, whole controls move into the ⋯ More menu, grouped by what they belong to — 🗺 This level, 📦 Whole project, 🎮 Game options, 📁 File.

What goes first is least-important-first: File before Game options before project tools before the level controls you build with. ▶ Play, the level picker, ✚ New, ↶ undo and the project name never move.

1½ · ↶ Undo and redo

Every edit can be taken back: ↶ / ↷ in the toolbar, or Ctrl/⌘+Z and Ctrl/⌘+Shift+Z. Hovering the buttons tells you _what_ will be taken back ("Undo move", "Undo add actor") rather than just "undo".

A drag counts as one step, not one per pixel — continuous edits (moving, dragging a slider, typing in a field) collapse while you are doing them. Discrete actions do not: clicking "add" twice is two things you did, so it takes two undos. Loading a template is itself undoable.

Ctrl+Z inside the Code window belongs to the Code window, so you never lose a line of script to an editor undo.

1¾ · ⬚ Selecting several things at once

Levels are built out of groups — a row of crates, a wall of trees, a cluster of pickups — so you can pick and move several actors as one.

The last one you clicked is drawn with a brighter ring — that is the primary, and it is what the inspector's property fields edit. Drag any member and the whole group travels with it, spacing intact.

With more than one selected, the inspector becomes a group panel:

ActionWhat it does
⇤ ⇥ ⤒ ⤓Line them up on an edge
↔ ↕Centre them on an axis
Space them evenly (along whichever axis they are most spread out on)
⧉ Duplicate · 📋 Copy · 🗑 DeleteDo it to all of them

Arrow keys nudge the selection by one unit; hold Shift for a whole tile (32).

📋 Copy, cut, paste

Ctrl/⌘+C, Ctrl/⌘+X, Ctrl/⌘+V, and Ctrl/⌘+D to duplicate in place. Delete (or Backspace) removes the whole selection in one undo step.

Paste drops the copies under your pointer, keeping the group's internal arrangement, and it works across levels — copy a lamp-post row in one level, switch level, paste. Copies get their own ids and non-colliding names (Crate, Crate 2, Crate 3) so scripts and events that refer to actors by name keep pointing at the right one. Anything pasted outside the world bounds is pulled back inside rather than lost.

The clipboard also lives in your browser storage, so it survives a reload and you can paste into a project open in another tab.

Inside the Code window, Ctrl+C/V belong to the text — the editor never steals them from a field you are typing in.

2 · Build the scene

The left panel starts with the 🌲 Hierarchy — every level and its actors as a tree. Click a level's name to open it, click an actor to select it (⚡ marks actors with events). Below it, the palette adds new actors.

Everything folds. Click any heading — 🌲 Hierarchy, Characters, Enemies, Props, 🥞 Layer, 🎲 Generate — to collapse it, and the twisty beside a level folds its actor list. Folds are remembered between sessions, so the panel stays the shape you left it.

The twisty and the name do different things on purpose: the arrow folds, the name opens the level. That means you can browse another level's actors without leaving the one you are editing — clicking one of them takes you there and selects it.

3 · Views, level size, camera & gravity

Every level has a View (how it's seen), a length (how far the board runs), and Gravity (a physics toggle). Rotating the device is neither — a landscape phone/tablet simply sees a wide window into the same world, like every classic mobile RPG.

The engine renders with WebGPU/WebGL (PixiJS) — hardware-accelerated on phones and desktops alike.

3⅛ · 🎛 HUD layout

🎛 HUD in the toolbar is where the on-screen furniture lives — hearts, score, coins, the level bar, the ability buttons and the joystick.

Each piece has an anchor (which corner) and a distance from it, rather than a fixed position. That is what makes one layout work on a portrait phone, a rotated phone and a tablet: "16 from the top-left" means the same thing on all of them, where "x: 16, y: 12" does not.

Edits apply immediately while Play is running, so you can nudge the HUD with the game live.

3¼ · ⚡ Abilities — the buttons your players tap

This is where a mobile game's controls really get made. Select an actor and the inspector has an ⚡ Abilities section: tick the abilities that actor should have. When that actor is the player, each one becomes an on-screen button automatically — no code, no api.ability() call.

✚ Create an ability… makes a new one and opens the editor. You choose what it _does_ from a list, and only the numbers that effect uses are shown:

EffectWhat it doesYou set
🗡 Attack nearby enemiesHits everything in reachDamage, Reach
🏹 Shoot the nearest enemyFires a homing shotDamage, Shot speed
❤ Heal yourselfRestores heartsHearts
👟 Dash forwardLeaps the way you faceDistance
🐣 Drop somethingSpawns an actor beside youWhat to drop
📝 Custom codeAnything the list missesYour code

Plus a cooldown, an icon, a particle burst, a sound, and an optional key — handy while editing on a desktop, though phone players just tap the button. A line under the dropdown tells you in words what the ability will do, and switching the effect resets the numbers so you never end up with "heal 130 hearts".

Deleting an ability also removes it from every actor that had it, so nobody is left with a button that does nothing.

🌳 From a skill tree: give a skill node a grants field with an ability id and investing in that node hands the ability over mid-game — the button appears as the player unlocks it. Point an actor at a tree with the 🌳 Skill tree dropdown in its inspector (trees live in the project database under an id).

3½ · 🎮 Controls — key and button binds

🎮 Controls in the toolbar (also the first item in the left palette) is where every control lives. Each one has a NAME, and keys and the on-screen button are two bindings of that same name — which is how one game serves both a phone player and a keyboard player.

3¾ · Paint the world (Tiles)

Hit 🗺 Tiles in the toolbar and the palette becomes a paintbox: grass, flowers, dirt and stone paths, sand, water, rock walls, trees, brick. Click/drag on the canvas to paint terrain (18×32 grid per level), use the eraser to clear, and ✓ Done painting to go back to placing things.

Tiles marked _solid_ (water, rock, trees, brick) block players in Play mode automatically — paint a maze and it just works. The Garden Explorer template ships with a painted garden to start from.

Ledges and platformers. Paint the Ledge (jump-through) tile and turn Gravity on, and the level becomes a real platformer: solid tiles (rock, brick, water, trees) are floors and walls you collide with, and ledges are one-way — you land on top but jump up through them from below. Hold ⬇ while jumping to drop through one.

A gravity level with no tiles painted keeps the original single flat ground line, so every platformer built before this still plays exactly as it did.

🎲 Generate a level (bottom of the paintbox) builds one for you: Maze (rock walls, fully-connected dirt paths), Dungeon (brick walls, stone rooms joined by corridors), Island (grassy heart, sandy shore, water, scattered trees). Generated tiles are ordinary paint — touch them up by hand afterwards. From code the same generators power endless games: api.setTiles(api.gen.dungeon()) rebuilds the level live, collision included.

Generated levels come out walkable: corridors are at least two tiles wide, because a tile is 40 units and a character is drawn about 68 across — a one-tile corridor is narrower than the person walking down it, so the art overlaps both walls and every corner snags. Mazes get there by being carved at half size and scaled up (which cannot open a shortcut), dungeon corridors are drawn two thick, and an island's coastline has its pinch points widened or returned to the sea.

🥞 Layers

Terrain paints onto one of three layers, chosen at the top of the paintbox:

LayerWhere it drawsBlocks players
⬓ BehindUnder everything — ground, paths, rugsNo
⬛ MainThe level itselfYes, for solid tiles
⬒ In frontOver the actors — treetops, roofs, mistNo

Only the Main layer collides. That is a rule, not a default: terrain that could quietly block the player is exactly the confusion layers exist to remove, so "solid" only appears on a swatch while you are painting Main.

The ⬒ In front layer is what lets a character walk _behind_ a tree canopy or a roof. In the editor it is drawn faded so you can still see what you are placing underneath it; in Play it is solid-looking and actors pass beneath it.

A layer you never paint on is not saved at all, so games that do not use them cost nothing.

4 · Animations, VFX, SFX

alpha, scale }, seconds)` for cutscenes and juice.

0..1)` — remembered per device. Garden Explorer opens on 'adventure', Blob Arena on 'battle'.

4¼ · 🏠 Menus — main, pause and settings

Every game needs screens that are not gameplay. Three come built in, and all of them are reachable from a plain button with no code:

A main menu is just a level: a Text actor for the name, and Button actors whose tap events 🚪 go to your first level or ⚙ open settings. Blobvale, Bloomstead and Hushfall all start with one — load any of them and look at the Menu level to see the pattern.

From code: api.menu.settings(), api.menu.pause(), api.menu.close(), api.menu.isOpen().

4½ · Events — build logic with no code

Select anything and use ⚡ Events in the inspector (RPG-Maker style): pick a trigger — 👆 When tapped · 🚶 When the player touches it · 🎬 When the level starts · ⏲ Every N seconds — then stack actions from a list: 💬 Say message, 🪙 Give coins, ⭐ Add score, ✨ Grant XP, ❤ Heal hearts, 🔊 Play sound, 🐣 Spawn a thing, 🗑 Remove this, 🚪 Go to level, 🔛/⏹ Turn a switch on/off, 🔢 Add to variable, 🛒 Open shop, 🎒 Open inventory, ⚙ Open settings, ⏸ Pause, 🏠 Title screen, 🏆 Win, 💀 Lose.

🎥 Camera direction (code): api.camera.panTo(x, y, secs) parks the camera somewhere (a door opening across the map), api.camera.follow('Hero') returns it, .shake(power, secs) for impacts, .letterbox(true) for cutscene bars. And imported models animate themselves: clips named idle / walk / jump auto-switch as players and mobs move — a state machine with zero wiring.

What goes in each box

An action row is [ action ] [ box ], and the box means something different for every action. Hovering it says so; here is the whole list:

ActionThe box holds
💬 Say messageThe line of dialogue. YOU ESCAPED!
🚪 Go to level…The level's name, exactly as it appears in the level picker. Village
🔛 / ⏹ Turn switch ON/OFFA switch name you invent. gate-open
🔢 Add to variableA counter name you invent, plus how much to add. lanterns +1
🏆 Win / 💀 LoseThe banner text shown on the end screen. ROOM CLEAR
🐣 Spawn a thingWhat to spawn. crate
🪙 ⭐ ✨ ❤ (coins, score, XP, heal)Just a number — no text box.
🔊 🎵 ✨ 🎁 (sound, music, effect, item)A menu, not free text.
🛒 🎒 ⚙ ⏸ 🏠 🗑Nothing — they take no settings.

Switch and counter names are yours to invent: there is no list to register them in, and the same name anywhere means the same thing. That is how one actor's action reaches another actor's condition.

The two boxes _above_ the actions are conditions on the whole event, not actions:

Three extras make real quests possible:

Switches and variables are shared with the Code window (api.switches.on/off/isOn, api.vars.get/set/add), so no-code events and scripts work on the same world state.

Level events — logic the level owns

Click empty ground (nothing selected) and the inspector becomes the level. It has its own ⚡ events, using the same blocks actors use but with no actor involved:

Actors run first (each firing its own 🎬 start), then the level's events, then the Code window — so code always gets the last word.

4⅝ · The 🗄 Database — items, shops, languages

The 🗄 toolbar button opens the project database:

4¾ · The ⛓ Flow tab — your game as a graph

The Flow tab (bottom panel) is the visual scripting map: every level and every actor with ⚡ events appears as a draggable node card. Wires show the connections that make quests work — gold wires from a switch-setter to every event gated on that switch, purple wires from 🚪 go-to-level actions to their destination level. Click a node to select that actor for editing in the inspector; double-click a level node to open it.

Wire authoring: drag from a node's ◉ port onto another node and the logic writes itself — actor → level adds a 🚪 door action; actor → actor invents a switch, makes the source set it, and gates the target's events on it. A chest-opens-door quest is one drag. Levels can be a wire source too, not just a destination.

Drag off into empty space and you get a search box instead (Unreal Blueprints' move): type a few letters and pick what should happen — any ⚡ action, a new trigger, a new level with the door to it already written, or a new actor placed where you dropped. Arrow keys and Enter work; Esc cancels and changes nothing. The list is scope-aware, so a level is never offered "remove this".

The bottom panel has three states, from the buttons at the right of its tab bar: docked (the default), ▾ minimized when you want the whole canvas, and ⇱ undocked — a floating card you can drag by its tab bar and resize from the corner, so the Code window or the Flow map can be as big as you like while the canvas keeps the full width underneath. docks it again, and whichever state you left it in is remembered on this device.

4¾ · 💬 Branching conversations

Select an NPC → 💬 Conversation in the inspector → 🌿 Make it branching. Your existing Story lines become the first nodes, so nothing is lost.

Each node is a line the character says. Add + reply to give the player something to say back, and pick where each reply goes from a dropdown of your nodes (or — end — to finish the conversation). A node with no replies just flows on to whichever node you point "then →" at.

Two things make it a real conversation rather than a menu:

Conditions are checked as each line appears, not once at the start, so a choice you make early really does change what you can say next.

Nodes that point at something that no longer exists are flagged (⚠ goes nowhere) — the classic way a dialogue tree breaks, and invisible if you just read it top to bottom.

5 · Stories (narratives)

Select a character → Story tab → one line per row → Save story. In Play mode, tapping them plays the lines through the dialogue box (typewriter, tap-to-advance). Writing a story upgrades a Blob into a Story character. From code you can also narrate directly: api.say('Narrator', 'Night falls…', 'Something stirs.').

6 · Levels

+ Level adds a scene; the toolbar dropdown switches between them. Each level has its own background, entities, and script. Move players between levels with api.goto('Level 2').

7 · The Code window

Each scene has a script that runs when the scene starts in Play mode. Apply to game also hot-runs it against the live game. The whole API:

api.entity('Hero')            // the entity by name (a live engine object)
api.entities()                // all of them, keyed by name
api.player('Hero', 320)       // WASD/arrows + touch joystick movement
api.onUpdate((dt) => { ... }) // run every frame
api.every(1.5, () => { ... }) // repeat on a timer
api.after(3, () => { ... })   // run once, later
api.spawn('crate', x, y)      // create entities from code
api.remove(thing)             // ...and remove them (name or reference)
api.overlap(a, b, 60)         // circle collision test
api.onTap(thing, () => {})    // tap handler (name or reference)
api.score.add(1)              // score with an automatic HUD
api.random(10, 20)
api.sfx.chime()
api.say('Elder', 'Hello!')    // dialogue from code
api.goto('Level 2')           // switch levels
api.gameOver('YOU WIN! 🌟')   // end screen with the score
api.verium                    // the local Verium wallet (earn/spend)

Fight: monsters, bosses, abilities, hearts & leveling

Drag a 👾 Monster or 👹 Boss from the palette — they're real actors with combat stats in the inspector: Health, Contact damage, XP reward, Move speed, and a Behavior AI you pick from a dropdown:

BehaviorWhat it does
🏃 Chasehunts the player
↔ Patrolwalks left-right around its spot
🎲 Wanderdrifts randomly
🛡 Guardstays home, chases when you get close, then returns

Monsters show a health bar when hurt; a Boss gets a big named bar at the top of the screen. Touching a mob costs the player a heart (with knockback + a moment of invincibility) — at zero hearts the game ends. Hearts switch on automatically when a player and mobs share a scene, or set them yourself with api.hearts(5).

Ranged enemies — set Shoot every (secs) in the inspector and the mob fires glowing projectiles at the player; they're dodgeable (sidestep, or jump over them in 2.5D). Bosses have phases: at half HP a boss _enrages_ — it flushes red, moves 40% faster, and shoots almost twice as often, with an announcement toast. NPC routes: walk any character along a looping waypoint path with api.patrol('Villager', [[160, 300], [560, 300]], 90) — great for town life (the Garden Explorer's gardener strolls one).

Ability buttons are how the player fights back — real on-screen buttons (bottom-right, like every mobile action game) with a cooldown sweep and an optional keyboard hotkey:

api.ability('Slash', { icon: 'sword', cooldown: 0.5, key: 'j' }, () => {
  api.meleeAttack(140, 1); // hit mobs within 140 for 1 damage
});
api.ability('Heal', { icon: 'heart', cooldown: 8, key: 'h' }, () => {
  api.hearts(3); // any code works — it's just a button
});

Built-in icons: sword fire bolt snow shield boot heart star — or use an imported image with icon: '@<assetId>'. More combat tools:

api.meleeAttack(140, 1); // swing around the player, returns hits
api.hurt('Warden', 2); // damage a mob/boss by name
api.hpOf('Warden'); // its current HP (0 = defeated)
api.onDefeat((name) => {}); // react to any defeat (loot! win checks!)

Leveling ties it together: call api.levels() and defeated mobs grant their XP reward toward a level curve with a HUD — and every level-up awards a skill point into api.skills, so leveling and your skill tree are one system. api.xp.add(n) grants XP from quests; api.level() reads the current level. The Blob Arena template shows the whole loop: slimes with different AI, two mapped abilities, hearts, XP, and a boss.

Enemies collide with painted terrain like players do — they cannot walk through rock, water or trees. Their collision box is a little smaller than the player's: an enemy that snags on a corner looks broken, while one that hugs walls slightly tightly does not. An enemy mid-🐗 charge that runs into a wall stops charging rather than grinding along it.

🧭 Finding their way round

🏃 Chase and 🛡 Guard enemies path around walls. With a clear line to the player they steer straight at them; with something in the way they route around it — including heading _away_ from the player first, if that is where the door is.

There is nothing to switch on and nothing to author: paint terrain and enemies use it. It works on hand-painted levels and generated ones alike.

The engine does this with one shared flow field — a single sweep out from the player labels every reachable tile, and every chaser walks downhill. A dozen enemies cost the same as one, which is what keeps it affordable at 60fps on a phone. Enemies never cut the diagonal between two wall corners (they would not fit), and an enemy with no route at all — a player standing somewhere it cannot reach — falls back to walking toward them, rather than standing still looking broken.

⚔ How an enemy attacks

Pick a pattern on any 👾 monster or 👹 boss, and set Attack every (secs):

PatternWhat the player sees
👊 Just touch meNo special attack — bumping into the player costs a heart.
🎯 Aimed shotOne shot straight at them. Easy to sidestep.
🔱 Spread shotThree in a fan — sidestepping is not enough.
💨 Burst fireThree quick shots, then a long pause. Punishes standing still.
💥 Ring of shotsShots in every direction. Find the gap and run. Good for bosses.
🐗 ChargeWinds up, then rushes where the player was.
🌊 Ground slamA shockwave rolls outward. It only hurts as the ring passes.

Every attack telegraphs first: the enemy stops and a ring closes in around it. That wind-up is the player's whole chance to react, so the engine owns it — you cannot turn it off, and aim is only taken at the _end_ of it, which is what makes stepping aside actually work. An enraged boss telegraphs faster, but never so fast that the tell disappears.

Enemies that only walk into you (👊) ignore the timer entirely.

Saving — your game, and your players' progress

Two save systems, both on-device:

💾 Save (the toolbar button) keeps the whole game in My Games — one named slot per project. Open ✚ New and your saved games are listed at the top: open any of them, or 🗑 delete one. (The editor also autosaves your current project continuously; Export/Import still moves games between devices as files.)

The in-game save file is for players: each game gets its own persistent storage that survives closing the browser.

💾 Three save slots

api.title() shows a title screen listing three separate runs. Each one remembers _where_ it was, not just that it happened:

Hero's Errand
  ▶ Boss Lair      1. Boss Lair · 1 done · 12 🪙 · 4m   🗑
  ✚ New game       2. Empty
  ▶ Village        3. Village · 2m                      🗑

Tapping a run resumes in the level it was in. Tapping an empty slot starts a fresh game there — it cannot overwrite another run, because starting over means erasing that slot with its own 🗑 first.

A slot records itself as you play: arriving in a level sets where ▶ CONTINUE resumes, and finishing one adds to its progress. Nothing is claimed until a player actually picks a slot, so a title screen never shows a run nobody started.

Level progression comes out of that record rather than being configured. A level is open if it is the first, if the player finished it, if they finished the one before it, or if it is where they are standing. In practice that means the game opens up as they get through it, while a level they never reached stays shut.

api.save.levelDone(); // mark this level finished (the 🏆 Win block does this too)
api.save.unlocked(); // ['Menu', 'Village', 'Cave'] — what they may replay
api.save.slot(); // 1..3, which run this is

Each slot has its own api.save storage, coins, XP and skill unlocks, so two runs never see each other's progress. A game saved before slots existed is slot 1's run — nothing is lost.

api.save.set('quest', 'met-the-elder'); // any JSON-serializable value
api.save.get('quest', ''); // read with a fallback
api.save.remove('quest');
api.save.clear();

Progress persists automatically, no code needed: with api.levels() on, XP and level carry over between plays (skill unlocks already did); api.gameOver tracks the best score and shows it on the end screen; and defeated mobs drop coins that scatter where they fall — walk over them to bank them (🪙 HUD chip). The balance persists in the save file, ready for shops:

api.coins.get(); // current balance
api.coins.spend(10); // true if they could afford it
api.coins.add(5); // grant bonus coins from code

Dress-up: cosmetics & attachments

Every character actor (blob, story character, monster, boss) has a 🎩 Hat and 🗡 Held item in the inspector — crowns, wizard hats, horns, halos; swords, shields, staffs, lanterns, flowers — all drawn in the house art style and attached to the body (they wobble and flip with it). Restyle live from code, which is exactly how cosmetic shops work:

if (api.coins.spend(25)) api.outfit('Hero', { hat: 'crown', held: 'sword' });

What everything is (the actor taxonomy)

Everything you place in a scene is an actor — selectable, editable in the inspector, scriptable by name, and eligible for ⚡ events. The palette groups them by what they're for:

GroupKindsExtra powers
CharactersBlob, Story characteroutfits, stories, player control
EnemiesMonster, BossHP/AI/loot, outfits, boss bar
PropsCrate, Lantern, Plantevents, tap sounds
UIText, Buttonfonts, tap handlers
AssetsImported imagesspritesheet clips, any art you own

The title / save-slot screen

api.title() at the top of your first level's script gives players a proper front door: the game's name, ▶ CONTINUE (only when a save file exists), ✚ NEW GAME (wipes the save file AND skill unlocks), and 🎵/🔊 volume bars — gameplay stays paused until they choose.

Skill trees

🌳 Skills are branch-based, like a Borderlands character sheet. api.skills.define() takes named branches, each with a colour, an optional big hex action skill, and a grid of skills in tiers:

api.skills.define({
  title: 'VAULT HUNTER',
  points: 1,
  pointsPerTier: 5, // points spent IN a branch to open its next tier
  branches: [
    {
      id: 'brawl',
      name: 'BRAWLER',
      color: 0xff6b6b,
      action: { id: 'rampage', name: 'Rampage', emoji: 'fire', cost: 1, maxRank: 1, tier: 0 },
      nodes: [
        { id: 'muscle', name: 'Muscle', emoji: 'sword', cost: 1, maxRank: 5, tier: 0 },
        { id: 'cleave', name: 'Cleave', emoji: 'sword', cost: 1, maxRank: 3, tier: 1 },
      ],
    },
  ],
});

star`); any other string is drawn as an emoji.

The Vault Hunter template is the worked example.

7¾ · Where your game lives (and what MCP is for)

Your game is one JSON file, and it can live in four places:

  1. This device — continuous autosave + named 💾 My Games slots.
  2. A folder on your computer — Publish → _A folder on this computer_ saves <name>.interverse.json anywhere you pick (documents, a synced drive, or a local git checkout you commit yourself). Chrome/Edge; on Safari/iPhone use Export/Import — the files are identical.
  3. Your GitHub repo — Publish → _Push to your GitHub repo_ with your own token; everyone gets a play link.
  4. The Interverse world — register it against an IVX node.

Separately from ALL of that: the MCP connection is for editing the ENGINE and the Studio itself — Claude Code attaches to the interverse-engine repo through the interverse MCP server (studio_* tools) to build features, fix bugs, and drive the dev cycle. Your game projects never need MCP; they're data files in the places above.

Finding your way around the api

The Code window has a 🔍 Find a command dock down its right side — every call the engine offers, grouped (Player, Actors, Timing, Combat, Progress, World, Screens, Items, Looks, Audio, Save, Language, Multiplayer). Type to filter (it matches names, descriptions and the preset lists, so "confetti" finds the particle call), click an entry to read its signature and example, and double-click — or press ⤵ Insert into script — to drop a working line at your cursor.

An empty Code window now shows a runnable starter script rather than a blinking cursor, and pressing Apply to game on an empty script runs that starter, so the first thing you do produces a result.

When a script fails, the error appears under the code with a plain-language hint ("There is no _apu_ here. Every command starts with api.") instead of a browser pop-up.

7⅞ · 📍 Where your project lives

Next to the project name the toolbar shows where this game is saved● on this device, or ✓ owner/repo@branch once you connect one. The dot means unsaved changes, the tick means saved. Click it to change any of it.

Your token stays in this device's browser storage and never enters the game file — that is why the repo link is stored per device rather than inside the project (spec §8.4: no credentials ship with a game). The exported JSON is pure game data, safe to publish anywhere.

8 · Multiplayer blocks

Tick Multiplayer in the toolbar and press ▶ Play — you get a lobby: HOST A ROOM (share the 4-letter code), JOIN a friend's code, or play solo. It's drop-in co-op: friends can join mid-game, and every player's avatar walks around live in everyone else's world (name tag included) with no netcode written. Try the Firefly Party template to see it wired up.

From the Code window (api.net is null when playing solo):

if (api.net) {
  api.net.code; // the room code
  api.net.isHost; // am I the host?
  api.net.players(); // everyone in the room
  api.net.setState('score', 5); // shared state — host-authoritative,
  api.net.state('score'); //   synced to every player
  api.net.onState((k, v) => {}); // react to changes
  api.net.send({ hi: true }); // custom messages
  api.net.onMessage((from, d) => {});
}

The pattern for shared objects: when a player takes/changes something, set a state key; every client (including late joiners, who get a full state sync) applies it locally. Firefly Party's script is a worked example.

🛰 Who decides what — host authority

The host runs the world; everyone else renders it. That is not a tuning choice, it is the only arrangement that works: two machines running the same enemy AI roll different random numbers and start diverging on the first frame, so players end up fighting monsters in different places and killing things that are still alive next door.

So in a room:

None of this needs wiring: tick Multiplayer and the roles sort themselves out. api.net.isHost tells you which side you are on if a game wants to do something different as host.

⏱ Smoothing. Snapshots arrive ten times a second; frames are drawn sixty. Rather than chase the newest snapshot — which stalls between packets and lurches when one lands — a joiner renders about 120ms in the past, interpolating between two things the host actually said. That is one snapshot interval plus a margin: enough to always have a pair to work from, not enough to feel behind. Nothing you control is delayed by it.

Interpolation runs on local arrival times, not the host's clock, because two devices' clocks can differ by any amount while the gaps between arrivals are always right. If snapshots stop coming, the last pose is held rather than extrapolated, so a dropped connection cannot send a monster sailing off the map.

If the connection drops, a joiner shows 📶 catching up… and then 🔌 reconnecting… while it rejoins the same room code, backing off between attempts. It says 🔌 lost the host rather than freezing with no explanation — a host that leaves has taken the room with it, and there is nothing to rejoin.

9 · Publish — your repo, and the Interverse world

🌍 Publish opens two paths (your credentials stay on YOUR device):

  1. Push to your GitHub repo — paste a fine-grained token (Contents: write), pick owner/repo, push. You get a play link — anyone can play your game instantly through the Studio player (…/studio/?load=<your-file>&play=1). Update = push again.
  2. Add it to the Interverse world — enter your Interverse (IVX) node URL and developer name; Studio registers the game (POST /games/register), stores the world gameId in the project, and keeps the returned reward api-key on your device only. With the Interverse toolbar toggle on, Play mode shows the local Verium wallet — and the on-chain IVX balance once a wallet address is configured in the project.

10 · AI copilot

No API key needed — the AI dev cycle runs on your Claude Code login, two ways:

  1. The AI Chat tab + the local bridge. One-time setup on the computer you build on — no pnpm install, no npm dependencies:
    • Install Node.js (nodejs.org) and Claude Code (claude.com/claude-code); run claude once in any terminal to sign in.
    • Get the interverse-engine repo — git clone, or just GitHub's Download ZIP, unzipped anywhere.

Then whenever you want the copilot: double-click start-ai.cmd (Windows) or run ./start-ai.sh / pnpm ai, leave the window open, and open the Studio — the chat shows "✦ Connected" within a few seconds and the key box disappears (it retries automatically, so the order doesn't matter). Type "add a spooky forest" or "give the boss a second phase" and Claude edits the live project through studio tools. Works from the dev server AND the installed app / hosted page. The bridge signs in with whatever claude is logged in as; if the repo's dependencies happen to be installed it uses the Claude Agent SDK, otherwise it drives the claude CLI directly — same result either way.

If the chat says it can't find the bridge:

  1. Claude Code driving the Studio directly over MCP. Open this repo in Claude Code and the interverse MCP server exposes studio tools (studio_open, studio_project, studio_add_entity, studio_update_entity, studio_set_script, studio_load_template, studio_play, studio_screenshot). Claude attaches to your running Studio (pnpm dev:studio), edits the project, presses Play, and LOOKS at the result — the full engine dev loop.

Only if you have neither (e.g. a friend using just the website): the AI Chat tab falls back to an Anthropic API key, stored locally on that device and never shipped in a game.

11 · Installing Studio (Windows, iOS, Android)

Studio is an installable app straight from the URL:

There's also a native Windows installer: the Studio Windows app GitHub Actions workflow builds one (Tauri shell — the same shell that will target iOS natively later). The web Studio is always the source of truth.