Our lifetime deal increases from $97 to $127 in03 days19 hours35 min13 secGet access for just $97 NOW
Studio

Scripting & automation

Use the browser console to inspect and run Studio commands, manage open projects, and connect a local MCP client to a live Studio tab.

AP
Aron Prins
Founder · Updated Aug 11, 2026 · 7 min read
On this page (7)

Studio exposes an advanced scripting surface as window.studio. It is useful for repeatable editing steps, debugging a project, or connecting a local MCP client. This is a browser API for the Studio you have open; it is not a hosted REST API and it does not make every visible control available as a parameterized command.

Before scripting a tool, open Character, Editor, or Map in the Studio. Tools register their commands when they are mounted, and the command list reflects what is available in the current browser session.

Inspect and run commands#

Open your browser's developer console and check the API version:

js
window.studio.apiVersion
// 1

List the commands currently registered:

js
window.studio.commands.list()

Each entry carries the command's id, title, description, category, capabilityIds, execution context (browser or headless), and current enabled state. Description, category, context, and capability ids are filled in when the command registers, so you never have to handle them as missing; shortcut and params appear only when the command actually has one. Run a command by id:

js
window.studio.commands.run('view.frame')

Commands that accept parameters validate them before changing the document. The return value reports success or a structured error instead of making you infer what happened:

js
const result = window.studio.commands.run('map.seedFromWorldPlan', plan)

if (!result.ok) {
  console.error(result.error.code, result.error.message)
}

Parameters are how you drive things a keyboard cannot. map.view.camera sets the Map viewport camera to a pose — position x/y/z in metres, orbit target tx/ty/tz — cutting instantly when ms is omitted and easing over ms milliseconds when it is present (orbit input suspends for the duration):

js
window.studio.commands.run('map.view.camera', {
  x: 40, y: 26, z: 40, tx: 0, ty: 0, tz: 0, ms: 2500,
})

The available ids and parameter shapes should be discovered with commands.list() in the session you are automating. Commands from a tool you have not opened may be absent, and some Editor or Map controls are intentionally UI-only.

Work with projects#

The documents API supports the three project kinds:

ToolKind
Characteranim
Editoreditor
Mapmap

You can list projects without opening an editor. Creating, opening, saving, exporting, or replacing a document requires that tool to be mounted:

js
const studio = window.studio

const maps = await studio.documents.list('map')
const created = await studio.documents.create('map', 'Courtyard')

await studio.documents.save('map')
const mapData = studio.documents.export('map')
const activeId = studio.documents.active('map')

documents.export(kind) returns the serialized JSON object; it does not open a download dialog. Use the normal export UI when you need a GLB or ZIP. documents.replace(kind, object) validates and replaces the active document, so keep a saved project or exported JSON backup before using it.

Project lifecycle methods work for Character, Editor, and Map. The lower-level documents.store(), documents.setKey(), selection, and transport helpers are Character-only:

js
window.studio.documents.setKey('anim', {
  bone: 'mixamorig:LeftArm',
  time: 0.5,
  euler: [0.4, 0, 0], // radians
})

window.studio.selection.selectBone('mixamorig:LeftArm')
window.studio.transport.setPlayhead(0.5)

Calling a mounted-tool method for the wrong kind fails with a clear error instead of silently changing another project.

Script the Character Creator#

window.studio.characters is a typed wrapper over the character.* commands the Character editor's Create sub-mode registers — same validation, same actions, and the same undo steps as the panel's own buttons. Open the Character editor first; characters.available() tells you whether those commands are registered, and it is the one method here that never throws.

js
const characters = window.studio.characters

if (characters.available()) {
  characters.submode('create')
  await characters.add('character-model-basic')
  characters.setStyle('hero')
  characters.setProportion('headW', 9.1)
  characters.setPaletteColor('shirt', '#12a150')
  characters.setExpression('happy')
}

Four conventions run through the whole namespace, so you rarely need to look up an individual method:

  • An omitted id targets the active character. get(), duplicate(), rename(), remove(), and every appearance and face method act on the character in the viewport unless you pass one.
  • An omitted optional value resets or toggles. setProportion('headW') returns the field to the style preset, setPaletteColor('shirt') drops the colour override, setFaceParam(id) clears the control, and setFaceToggle('autoBlink') flips it.
  • A failure throws. The registry's own message is raised instead of a silent null being returned, so a script stops where it went wrong rather than continuing against a character that was never built.
  • Reads stay cheap. list(), get(), and products() return plain data — the cast with its active id and live cap, one character, and the character products this account can see.

Two things to watch:

  • characters.remove() runs the command id character.delete. That is the name the palette, the cheat sheet, and MCP use. Unlike the panel it does not ask for confirmation — a script asked for it explicitly.
  • characters.submode() called bare toggles the sub-mode; it is not a getter. Pass 'animate' or 'create' when you mean to set one.

characters.exportGlb() is deliberately narrow, but the character.exportGlb command behind it takes more. Alongside id, all, download, and outline it accepts clips (names of library clips to embed — omit for a rigged mesh with no animation), expression (baked into the face texture; the face is a static snapshot, so no face animation survives export), rigReport, and zip. Run it through commands.run('character.exportGlb', …) when you need those.

Listen for changes#

Subscribe to document changes and keep the returned function so you can unsubscribe:

js
const off = window.studio.events.on('document', () => {
  console.log('The active Studio document changed')
})

// later
off()

The selection and playhead events follow the Character tool. The document event covers mounted Character, Editor, and Map documents.

Undo and session boundaries#

Commands that edit through a tool's normal document actions participate in that tool's undo history. The history is intentionally bounded to the current editing session: Character and Editor keep up to 200 steps, while Map keeps up to 100. Opening or replacing a project starts a fresh history.

Map undo is keyed, not timed: consecutive edits fold into one step only when they share a coalescing key, so a rapid-fire script no longer collapses distinct actions into a single step just because they landed in the same instant.

Do not assume every browser method is undoable merely because it is callable. Save or export a backup before a large script, run a small sample first, and inspect each command result.

Connect a local MCP client#

The MCP server is a developer feature in the Studio source repository; it is not a hosted service. You need a local checkout with its dependencies installed. From that checkout, run:

bash
npm run mcp

Configure your MCP client to start that command over stdio. At startup the process builds the MCP bundle and prints a localhost bridge port, a one-time token, and an exact Studio connection hint to its log. Use that hint to open the Studio tab with the ?mcpBridge=<port>:<token> query, or run the studio.bridge browser command with the printed port and token.

MCP tools fall into two groups:

  • Headless tools run inside the local MCP process without a browser tab. The set is small and fixed — pure document operations: clip_new, clip_duplicate, clip_makeInPlace, clip_extractRootMotion, clip_loopFix, map_item_delete, map_layer_clear, map_layer_lock, map_layer_remove, and map_layer_rename.
  • Browser tools are everything else. They need a connected, open Studio tab and act on the project in that tab.

Tool names convert command dots to underscores, so a command such as map.place appears as map_place.

The Character Creator's 36 character.* commands ship as tools too — character_add, character_setStyle, character_setProportion, character_setExpression, character_exportGlb, and the rest, matching the window.studio.characters namespace above one for one. They are browser tools: the creator builds and renders in the tab.

The MCP tool list comes from command descriptors generated from the local checkout's registered commands, with a CI check that fails when they drift out of date. It does not inspect whichever production tab happens to be open, so keep the checkout and Studio version aligned.

The bridge listens only on localhost and uses a new token for each run. One Studio tab is connected at a time; a newer tab replaces the previous connection. If a browser tool reports requires_bridge, reconnect using the current startup hint. Keep the tab open while browser tools run.

Current boundaries#

  • Opening the relevant tool is required before using its browser commands or project methods.
  • The command surface is broad but does not promise one-to-one coverage of every button, drag gesture, or inspector field.
  • Character has the richest direct scripting helpers. Editor and Map use generic project methods plus their registered commands.
  • A license key can unlock entitled catalog access, but it does not create an account session, cloud identity, or account perks for automation.
  • Imported models and local projects remain in browser storage unless you explicitly save, export, or use an available cloud action.
  • Licensed file downloads remain subject to the normal entitlement checks; the MCP surface is not a way around them.

For the data returned by project export methods, see Studio file formats. For the available keyboard and palette discovery surfaces, see Keyboard shortcuts reference.

Still stuck?

Ask us directly — we answer support mail ourselves.

Contact support
Share
Scripting & automation — threejsassets.com