App triggers
Drive the host app — Voxta Talk UI, VAM scenes, Voxy avatar, custom apps — from a scenario script.
App triggers are how a scenario reaches outward and tells the host app to do something: change the chat view, swap avatar images, play music, fire a sound effect, change a VAM scene state.
You call them from a script:
import { chat } from "@voxta";
export function trigger(e) {
chat.appTrigger("TriggerName", ...params);
}The available triggers depend on which app is running the chat. The trigger names below are for Voxta Talk (the default web UI). For VAM, see VAM app triggers.
Asset helpers
Most modern triggers take an Asset object instead of a path string. Get an asset from a character's or scenario's asset collection:
| Function | What it does |
|---|---|
assets.get(name) | Get a specific asset by exact filename. Throws if it doesn't exist. |
assets.oneOf(pattern) | Random pick from assets whose path matches the (case-insensitive) regular expression. Throws if none match. |
assets.oneOrNoneOf(pattern) | Same as oneOf but returns null/undefined instead of throwing. |
The oneOf / oneOrNoneOf argument is a regular expression matched against the asset path, not a literal prefix. A bare word like "emote_" still works (it matches any path containing it), but you can also anchor and use full regex syntax.
// Specific
const happy = e.character.assets.get("happy_face.webm");
// Random from a set (matches any path containing "emote_")
const emote = e.character.assets.oneOf("emote_");
// Anchored regex: a random PNG under portraits/
const portrait = e.character.assets.oneOf("^portraits/.*\\.png$");
// Optional random
const effect = chat.scenario.assets.oneOrNoneOf("special_effect_");Path-based access still works for legacy triggers — just pass a string path relative to the character or scenario Assets folder.
Voxta Talk triggers
Emote
Pop an emoji bubble above the character.
chat.appTrigger("Emote", "💡", "yellow");
chat.appTrigger("Emote", "❤️");| Param | Type | Notes |
|---|---|---|
emoji | string | A single emoji. |
color | string? | Color name ("red") or hex ("#FF0000"). |
SelectView
Switch the chat interface mode.
chat.appTrigger("SelectView", "portrait");| View | What it is |
|---|---|
talk | Voice-centric, minimal UI. |
portrait | Avatar-focused. |
chat | Full view with chat history. |
stage | Visual-novel stage with backgrounds, buttons and HUD. See the Stage Editor. |
assistant | Compact assistant layout. |
debug | Inspector view. |
SetAvatar
Swap a character's avatar — a 2D image, a looping video, or a .vrm model.
chat.appTrigger("SetAvatar", e.character.assets.get("mood.png"), e.character.id, "untilEndOfSpeech");| Param | Type | Notes |
|---|---|---|
asset | Asset | The image, video or model to display. |
targetCharacterId | string? | Whose avatar to change. Defaults to the asset's owner. |
until | AvatarExpiration? | How long the override sticks. |
A video avatar loops and is muted unless you pass a volume, which needs the object form:
chat.appTrigger("SetAvatar", {
path: e.character.assets.get("idle_loop.webm"),
characterId: e.character.id,
volume: 60,
});A string path still works instead of an asset, for legacy scenarios.
SetGeneratedAvatar
Pin a fixed portrait on a character. It outranks the emote and expression avatars set by SetAvatar, so a
generated portrait stays put while expressions come and go. Pass an empty value to clear it.
chat.appTrigger("SetGeneratedAvatar", someUrnOrUrl, e.character.id);SetAvatarFromScenario
Avatar from the scenario's asset folder instead of a character's.
chat.appTrigger("SetAvatarFromScenario", "intro.png", e.character.id);Audio
Music, ambient loops, sound effects and voice lines have their own page: Audio.
SetBackground
Swap the background image or video. Voxta Talk supports multiple background layers — useful for stacking a background image with overlay effects.
Basic — single background:
chat.appTrigger("SetBackground", chat.scenario.assets.get("main_room_bg.jpg"));With explicit layer (positional):
// Layer 1 = base background, higher layers = overlays
chat.appTrigger("SetBackground", chat.scenario.assets.get("room_bg.jpg"), 1);
chat.appTrigger("SetBackground", chat.scenario.assets.get("rain_overlay.webm"), 2);With explicit layer (object form):
chat.appTrigger("SetBackground", {
path: chat.scenario.assets.get("rain_overlay.webm").toUrn(),
layer: 2
});Clear a layer:
// Omit the asset to clear that layer
chat.appTrigger("SetBackground", null, 2);| Param | Type | Notes |
|---|---|---|
asset or options | Asset | string | { path, layer } | The image/video, a URN string, or an options object. |
layer | number? | Layer index. Defaults to 1. Higher values stack on top. |
There's also a typed helper that wraps this call:
chat.setBackground(chat.scenario.assets.get("room_bg.jpg"));
chat.setBackground(chat.scenario.assets.get("rain.webm"), 2);
chat.setBackground({ path: someUrn, layer: 3 });Layer rendering zones (z-index)
The layer number maps to one of three visual zones — pick the zone that matches the kind of asset you're placing:
| Layers | Zone | What renders | Use for |
|---|---|---|---|
| 1–3 | Background | Behind characters and UI | Standard scenery, wall textures, distant objects. Layer 1 is the default for legacy single-background calls. |
| 4–9 | Mid-ground | In front of characters, behind the Chat UI | Fog, rain, foreground props (tables, desks) the characters stand behind — but that shouldn't block the user from typing. |
| 10+ | Foreground | On top of everything, including the Chat UI | Full-screen maps, cutscenes, heavy weather effects, HUD overlays, vignetting. |
All layers from 4 upwards are rendered with pointer-events: none. This means even if a foreground layer covers the whole screen (e.g. a map at layer 10), the user can still click buttons and input fields underneath transparent sections of the image.
Clearing layers
Pass null (or omit the asset) to clear a specific layer:
chat.setBackground(null, 4); // remove the desk
chat.setBackground(null, 10); // remove the rain overlayCommon recipes
// Standard background scene
chat.setBackground(chat.scenario.assets.get("bg_forest.jpg"), 1);
// A desk in front of the character but behind the chat box
chat.setBackground(chat.scenario.assets.get("prop_desk.png"), 4);
// A rain effect on top of everything (clickable through transparent areas)
chat.setBackground(chat.scenario.assets.get("effect_rain.webm"), 10);SetBackgroundFromScenario
Set the background using a string path relative to the scenario's asset folder. Single-layer only — for layered backgrounds, use SetBackground with an asset object.
chat.appTrigger("SetBackgroundFromScenario", "lobby_bg.jpg");SetAvatarAnimation
Play an animation on a character's VRM — an FBX clip, or a generated SMPL .json.
// Loop it as a state:
chat.appTrigger("SetAvatarAnimation", e.character.assets.get("VRM/Idle.fbx"), e.character.id);
// Play it once, then return to idle:
chat.appTrigger("SetAvatarAnimation", e.character.assets.get("animations/wave.fbx"), e.character.id, false);| Param | Type | Notes |
|---|---|---|
animationAsset | Asset | Must be a URN — string paths not accepted. |
characterId | string? | Whose avatar to animate. Defaults to the sender. Narrators cannot be targeted. |
loop | boolean? | Default true — hold it as a state. false plays it once. |
Pass a null/empty asset to remove the animation override.
Postures
Build postures the way a mocap timeline would: looping states connected by one-shot transitions. Set a new looping state right after a transition and it is deferred, crossfading in when the transition ends.
The posture itself is a chat variable, avatars_<characterId>. Set it and the idle / talking / thinking states resolve to VRM/<folder>/<Expression>_<State>.fbx. Delete it to return to the default standing set.
export function trigger(e) {
const cid = e.character.id;
chat.variables["avatars_" + cid] = "Sitting";
chat.appTrigger("SetAvatarAnimation", e.character.assets.get("VRM/transitions/StandToSit.fbx"), cid, false);
chat.appTrigger("SetAvatarAnimation", e.character.assets.get("VRM/Sitting/Neutral_Idle.fbx"), cid);
}SetAvatarBlendshape
Drive a single blendshape on a character's avatar — useful for facial expression rigs that expose blendshape parameters.
chat.appTrigger("SetAvatarBlendshape", "smile", 0.8, e.character.id);
chat.appTrigger("SetAvatarBlendshape", "smile", 0); // reset| Param | Type | Notes |
|---|---|---|
blendshapeName | string | The blendshape identifier on the avatar rig. |
value | number | Typically 0.0–1.0. |
characterId | string? | Whose avatar to modify. Defaults to the sender. |
Avatar expiration values
Used in SetAvatar / SetAvatarFromScenario:
| Value | Behavior |
|---|---|
untilNextMessage | Reverts after the next chat message is processed. |
untilEndOfSpeech | Reverts when the current TTS line finishes. |
| undefined (omit) | Persists for the session until explicitly changed. |
Queued vs immediate triggers
By default chat.appTrigger(...) fires immediately — while the reply is still being written, well before it is spoken. To fire after the character finishes speaking, use chat.queue.appTrigger(...):
chat.queue.appTrigger("PlaySound", chat.scenario.assets.get("door_close.wav"));The queued trigger waits in the message queue and fires when the character's speech completes.
Queued means after the whole speech, not at a point in it. For a sound that belongs mid-line, use the "insert" audio method instead.
Other queued operations
chat.queue is a sub-module with other queue-aware operations beyond just app triggers:
| Method | What it does |
|---|---|
chat.queue.appTrigger(name, ...args) | App trigger queued to fire after speech. |
chat.queue.roleMessage(role, text) | Send a message as the character associated with role, queued. |
chat.queue.roleEnabled(role, enabled) | Toggle a role's participation, queued. |
chat.queue.setFlag(value) / chat.queue.setFlags(...) | Set scenario flags, queued. |
Each maps to the same operation as the non-queued version on chat.*, but waits for the current speech turn to finish before being applied.
HUD & stage triggers
A second family of triggers builds game UI on the stage — SetHud, OpenPanel, Splash, Notice, Choice, ScreenEffect, FloatingText, Impact, CharacterEffect. They have their own page: HUD & stage effects.
VAM triggers
When the chat is hosted by the VAM plugin, app triggers can drive any VAM atom storable — Timeline animations, light intensities, material params, etc. The trigger signature is different (atom + storable + param + value):
chat.appTrigger('Action', 'Person', 'plugin#2_VamTimeline.AtomPlugin', 'Play wave_hello');See VAM → App triggers for the full VAM-specific reference.