Character Studio help
The same SVGMotion editor — canvas, timeline, inspector, Connect graph, AI dock, exports — wearing a character hat. Three extra things on top: rig roles, a micro-action verb kit, and a Stage floor where many entities act together under an AI director. This page mirrors the main editor guide — everything in it still applies here. Two guided tours run live inside the studio — the quick tour (rig → micro → macro → world, ~2 min) and the full tour (everything incl. keyframing, links and the director) — plus a How it's made walkthrough on every gallery item.
Quickstart
npm run dev # → http://localhost:4173/characterstudio ollama serve && ollama pull qwen3:8b # optional — same providers as the editor
Character Studio shares the editor's ☰ Menu → Settings… → AI provider — Ollama, OpenRouter, or OpenAI, BYOK, keys stay in this browser. Settings → Advanced can also enable a local browser AI engine (WebLLM, runs on this device) that handles tool work — commands, edits, and world-director beats — before any provider is needed. With neither, everything works offline; the world director falls back to a deterministic built-in brain so the choreography pipeline never dead-ends.
blobby pick up bone in the command bar. Or just run the tour: Get started → ▶ Tour / ☰ Menu → ▶ Studio tour walks the same loop live, and Help → ▶ Full tour covers everything end-to-end.
Core concepts
| Term | Meaning |
|---|---|
| Rig role | A semantic alias for a layer — head, body, legL/R, armL/R, tail, earL/R, wingL/R, snout, mouth, prop. Verbs find parts through roles, not layer names. |
| Archetype | The role spec a creature is checked against: blob, biped, quadruped. Aliases bridge them — arm means a biped's arm or a quadruped's front leg; mouth resolves to a pet's snout. |
| Micro-action | A small ordinary action that poses only the parts it needs (wave = one arm). Same machine as editor actions — keyframes, easing, transitions. |
| Micro-verb | A parameterized function that builds a transient micro from args — wave({side:'r', duration:400}). The panel, the command bar and the AI all call these. |
| Macro | A named chain of micros — greet = nod → wave → bow — wired as ordinary complete transitions, or queued verb calls with $target/$self bindings. |
| Entity | Anything on the world floor — a rigged character (●) or a prop (▣, any unrigged .svg). Each keeps its own actions and click triggers. |
| Link | An entity→entity rule: when source finishes action X / is near / touches target → target plays action Y. |
| Director | The world-level AI that sees the full cast state and returns {thought, actions[]} beats, dispatched through the same invoke() as everything else. |
| User | You — a ◈ presence on the floor, not an actor. Characters can face(user) / walkTo(user); drops and clicks emit events the director reacts to. |
The three modes
| Mode | For |
|---|---|
| Rig | Build the character — draw/import parts, assign roles, add micros, keyframe on the same timeline. This is the editor canvas. |
| Connect | The same state-machine graph — macro chains show up as readable rows of nodes. Rig/world panels park here. |
| Stage | The floor — sidebar hides, cast tray spawns entities, drag them, link them via the console, type commands, let the director run beats. |
The mode rail sits where the editor's view switcher lives; every panel, shortcut (B/I/M/G/F), the AI dock, autosave, undo and export behave exactly as in the main editor. Canvas gestures too — click a part to select it, drag to move it, or use the arrow keys to nudge the selected layer or shape (⇧ = 10×); with nothing selected ←/→ step the playhead.
The sidebar is contextual: instead of every studio section open at once, it keeps the relevant one expanded — select a rigged part → Parts, select something unrigged or nothing → Rig, pose a non-idle action → Micro actions. In Stage mode the sidebar and inspector hide entirely — stage chrome (cast tray, autopilot, console drawer) takes over. You can always ▾-open another section by hand; the next selection change re-tidies.
Rigging a character
New character… (top bar) opens the picker:
| Option | What you get |
|---|---|
| Blank biped / quadruped / blob rig | Placeholder skeletons with every role pre-registered — all presets work immediately; draw better art over the placeholders. |
| Blobby / Stick / Robot | Hand-authored template characters. |
| Describe it — AI generate… | The AI dock with a rig-aware prompt — ask for "a dog" or "an alien" and it emits the right archetype. |
| Open .svg project… | Any SVGMotion .svg (or generic SVG) becomes the working character. |
Assigning roles
The Rig sidebar lists the archetype's roles as a checklist. Pick a role row → click a layer on the canvas → assigned. Shape-level roles (eyes, mouth, ears) tag an element inside a layer. Presets a creature can't perform — Wag tail on a blob — are greyed out automatically.
<g> per movable piece (draw with ⧉ Into-layer off) → pick archetype biped → assign body, head, armL, armR, legL, legR → the checklist goes green and every biped preset unlocks.
Parts library
The Parts sidebar panel restyles a character by swapping rig slots — eyes, mouths, ears, snouts, heads, torsos, limbs, tails — plus attachable accessories (hats, glasses, scarves, headphones…). It only shows slots your current rig actually resolves, and swaps keep the rig keys, transforms and pivots, so every micro-action and macro keeps working after a restyle. The library has ~90 authored parts plus everything below.
| Gesture | What it does |
|---|---|
| ★ Original | Pinned first in every slot — restores the exact part the character shipped with (geometry, position, pose metadata). Swap Nova's arms, click ★ Original, Nova's arms are back. Works from the popover too. |
| ★ Gallery chip | Parts harvested live from the /characters gallery — Nova's eyes, Biscuit's tail, Orbit's body — applied with the donor's own colors. Under All they appear after the authored set. |
| Slot chips | Eyes / Ears / Mouth / Snout / Body / Head / Torso / Arms / Legs / Tail — whichever exist on this archetype — plus Accessories. Selecting a part on the canvas auto-filters the panel to its slot. |
| Style chips | All / Soft·round / Boxy·mech / Critter·pet / ★ Gallery — personality-grouped so nothing clashes. |
| Part grid | Click a thumb to swap. Pairs (eyes, ears, arms, legs) apply to both sides; left limbs auto-mirror. One ⌘Z undoes a swap. |
| Right-click a part | On the canvas, right-click any rigged part → the same picker opens as a popover for that slot. |
| Accessories | Attach to the head (hats, glasses, antennae, halo) — click to add, click again to remove. They're ordinary shapes: pose them, micro them, or delete them. |
| ✦ describe a part… | With AI configured, type e.g. "cyberpunk goggles" → Make generates a style-locked fragment into the active slot. |
Parts arrive style-locked: snippets paint with palette tokens ($ink, $body, $skin, $soft, $accent) sampled from your character's colors — a swapped eye inherits the face ink, a swapped arm inherits the body tone.
Micro-actions
The Micro actions panel offers presets — Nod, Tilt head, Wave, Blink, Smile, Bark/speak, Wag tail, Prick ears, Sit, Bounce, Hop, Shiver. Click + to add one; it lands in the Actions list like any action and plays so you can refine it on the canvas/timeline. Right-click a preset to add it with tuned params ({"degrees":20,"duration":500,"side":"l"}).
+ Custom micro… scopes a sparse action to a role (or the whole body); + From selected layer scopes one to the selection. Then pose and keyframe exactly like the main editor — unposed layers simply hold.
Micro-verb API
Every preset is a call into js/char/microapi.js — functions that build a transient sparse action from clamped args. The command bar and the AI call the same verbs directly:
| Group | Verbs |
|---|---|
| Primitives | setPartRotation(partId, degrees, duration?, hold?), setPartPosition, setPartScale, setPartOpacity, resetPart, setShape, physMove (bake-then-play physics on one part) |
| Compounds | tiltHead, nod, shakeHead, raiseArm, wave, lookAt, squash, stretch, hop, bounceBody, wagTail, blink, mouthOpen, perkEars, lieDown, sit, relax, emote, breathe |
partId is a rig role; numbers clamp to safe ranges; an unresolvable verb is a no-op, never a crash. hold:true keeps the pose until the next call — that's how sleep-style behaviors are built.
Macros
A macro is a named chain of micros — "greet" = nod → wave → bow. In the Macro composer: tap action chips in play order, name it, pick an entry trigger (click/hover/press), hit ⌁ Wire. The composer writes ordinary idle → steps → idle transitions — fully visible and editable in Connect.
Parameterized macros: steps may be verb calls — {call:'walkTo', params:{entity:'$target'}} — where $target, $self, $user, $x, $y bind at trigger time. So eat can be walkTo($target) → consume($target), triggered by the AI as triggerMacro('eat', {targetEntityId:'prop_apple'}). Verb-step macros run through the entity's queue (each step waits for the previous) and only play in Stage mode.
Connect
The shared state-machine graph, unchanged: action nodes, transition edges, triggers (click/hover/press/layer-targeted), guards, inputs. Macro chains appear as readable rows of nodes. No world entities here — it's deliberately the same manual canvas as the editor's Connect mode.
Stage floor
Switch to Stage to stage many entities on one floor — the sidebar and inspector hide so the stage gets the whole canvas:
- Add entities — the + Cast tray (left edge) holds bundled characters & props, your recent scenes, and import — click a tile or drag it onto the floor.
⇪ Send to stagepushes the character you're editing; dropping any.svgfile on the floor works too. Project files keep their actions and click triggers — a lamp that toggles still toggles. - Direct them — drag any cast member to move it (a tap still fires its own click triggers); with one selected, click the floor and it goes there — characters walk, props arc. Hover shows action/macro chips plus a floating ⋯/✕ (menu / remove —
⌦works too);Escdeselects; double-click a character to edit it in Rig. Or type in the command bar:blobby wave,dog go to bone,cat pick up apple. - Edit back — a chip's Edit in rig editor (or double-click the cast member) loads the entity into Rig;
↺ Update entitywrites it back. - Tags — entity menu →
Tags…gives the AI affordances:edibleprops get eaten,toyprops get played with. - The user — with nothing selected, click the floor and a ◈ marker moves there;
face(user)/walkTo(user)work, drops/clicks emit director events. With a cast member selected, a floor click directs it instead. - ⌄ Console — a bottom drawer (bar button) holds the plumbing: Director log, Links form, Events feed, and the State JSON the AI sees.
Entity methods — what AI and you can call
walkTo(x,y | entity) goto(entity) face(entity) tellAt(entity, action) pickUp(prop) drop() consume(prop) interact(entity, act, targetAct) nudge(prop) say(text) seq(steps[]) launch/throw(x|entity|vx,vy) micro(verb, args) triggerMacro(name, bindings) macro name → runs the macro
pickUp makes the prop ride with its carrier; consume plays the prop's own eaten/vanish action (or shrinks it away). Movement is a real walk, not a glide: the entity advances stride-by-stride in sync with its own gait cycle — eased sub-move + a footfall pause per stride, the loco-tagged action re-firing each step — and it faces travel direction (left-authored art sets rig.facing:-1 so it never moonwalks). Positions track live as it walks, so near/moves links fire mid-journey. Positions clamp to the floor. seq queues ordered steps — walkTo blocks until arrival. launch/throw arcs a prop (or any entity) on a real ballistic path — the same solver as the editor's projectile recipe, run in stage space.
Everything funnels through one dispatch:
invoke(entity, { type: 'micro'|'macro'|'verb'|'say'|'seq', action, params })
Chips, the command bar, links and the director all use it — malformed actions drop with a visible ✗, never a crash.
Links — entity → entity rules
In the console drawer's Links tab pick source → when → target → action. Conditions:
| When | Fires |
|---|---|
finishes (after) | When the source's chosen action completes |
is near (near) | When the entities come within range |
touches (touch) | When their boxes overlap |
| moves | While the source keeps changing position (label-drag, launch, another verb) — the target re-fires its action on a ~1s cooldown. Pair it with walkTo on the destination and you get a follow rule: drag the ball, the dog chases it. Skipped while the follower carries the source. |
"When Nova finishes wave → Orbit plays greet." "When the dog is near the bone → bone plays pickup." Links fire once when the condition begins and re-arm when it ends — that's how the gallery's Buddy duo world runs its call-and-response greeting with zero code.
AI director & the autopilot
With a provider configured or the local engine enabled, ▶ Autopilot (stage bar) hands the director the full state — every entity's kind, position, tags, callable methods and micro-verb signatures, plus links and recent events — and it returns a structured beat:
{ "thought": "The user placed an apple next to Blobby…",
"actions": [
{ "target": "char_blobby", "type": "micro",
"action": "setPartRotation", "params": { "partId": "head", "degrees": 12, "duration": 200 } },
{ "target": "char_blobby", "type": "macro",
"action": "eat", "params": { "targetEntityId": "prop_apple" } } ] }
thought shows as a 💭 bubble over the entity it's about to move (the full log lives in the console's Director tab); actions run through invoke(). Beats are periodic and reactive — drops, clicks, arrivals and landings wake the director in under a second; user-caused events react fastest (~0.5s). While the model thinks, a light ambient micro keeps the cast alive so nobody freezes. Quiet world → ambient micros (blink, breathe, wagTail) or an empty list.
Offline: a deterministic built-in director and a parser handle "X does A", "X go to Y", "X pick up / eat Y", "X throw Y", "X say …" — so the pipeline still runs end-to-end. The gallery's scripted beats are canned presets in the same {thought, actions[]} shape — the AI director buttons and the "tell the cast" box ask the real model (local browser AI first, provider fallback). In the editor the same local-first path powers the ⚡ command box — it also edits: "make it red", "add a spin action", "move head up".
Gallery & deep links
| URL | Opens |
|---|---|
/characterstudio?scene=<id> | A character scene from /chars/items/<id>.svg in the Rig canvas |
/characterstudio?world=<id> | A world bundle from /chars/worlds/<id>.json, staged on the floor |
&guide=1 | Starts the item's guided build — same machinery as the editor's tours |
/characterstudio#tour · ?tour=1 | Runs the studio quick tour (basics) |
/characterstudio#tour-full · ?tour=full | Runs the full studio tour — every feature end-to-end |
/characters/gallery?item=<id> | The gallery with that item's detail open |
World bundles are JSON: {name, floor, entities:[{name, file, x, y, w, tags}], links:[…], presets:{note, beats:[{thought, label, actions}]}} — entities reference ordinary gallery/studio .svg files by path. Deep links merge into the current stage rather than wiping it.
Files & persistence
| Thing | Where it lives |
|---|---|
| Character scene | A normal SVGMotion .svg — export/save/share exactly like the editor; gallery chars are the same format. |
| World | Autosaves to the mf.charworld.v1 pref; bundles for sharing live as JSON under chars/worlds/. |
| Autosave | char-autosave — separate from the main editor's autosave. |
Physics-lite: proximity uses bounding boxes polled per frame — enough for near/touch/pick-up. Real motion physics reuse the editor's solvers (launch, physMove).
Worked example — Biscuit fetches a bone
/characterstudio?scene=biscuit loads the pup → Stage → ⇪ Send to stage → + Import chars/items/prop-bone.svg → tag it toy edible → drag it across the floor → add a link: when Biscuit is near Bone → Bone plays pickup → command bar: biscuit go to bone → he walks over, the link fires, the bone vanishes. Or run the whole thing ready-made: /characterstudio?world=park-play.
Troubleshooting
| Symptom | Fix |
|---|---|
| Preset greyed out | Missing rig role — the Rig checklist shows which; assign it or switch archetype chips. |
| Verb does nothing | Unresolvable role → no-op by design. Check partId spelling and the role list. |
| Macro won't play in Rig mode | Verb-step macros (walkTo, consume…) need Stage mode — they queue on an entity. |
| Director idle | No AI? The built-in offline director still answers the command bar; autopilot needs AI — the local engine (☰ → Settings → Advanced) or a provider (☰ → Settings → AI provider). |
?world= didn't load | Stage must be entered (Stage mode) before import; a bad bundle name 404s → check chars/manifest.json ids. |
| Entity won't move | walkTo needs the entity mounted on the floor — user marker and props without scenes can be targets but not walkers. |