HUD & stage effects
Game UI on the stage — a player HUD, windows, popups, screen effects and combat feedback.
A family of app triggers that turn the stage view into a game screen: a stat HUD, spawnable windows, splash banners, modal choices, and screen effects.
All of it is shown to the player only — none of it reaches the AI. If the character should know something, say it with a context or a note.
The HUD
SetHud replaces the HUD with a list of typed sections. Keep it in a chat variable so it persists, and re-push it on resume — the on-screen HUD is not restored after a refresh.
import { chat } from "@voxta";
function renderHud() {
const hud = chat.get("hud", null);
if (hud) chat.appTrigger("SetHud", hud);
}
chat.addEventListener("start", () => {
chat.set("hud", {
title: "Adventurer",
sections: [
{ type: "badge", label: "Location", items: [{ label: "Moonlit Tavern", icon: "📍" }] },
{ type: "bars", label: "Stats", icon: "📊", items: [
{ label: "Health", value: 72, max: 100, icon: "❤️", color: "#e5484d" },
]},
],
});
renderHud();
});
chat.addEventListener("resume", renderHud);Each section is a tab, except badge sections which render in a persistent footer.
| Section type | Renders |
|---|---|
bars | Value/max fill. Add min below zero for a diverging gauge. |
chips | Status pills. qty shows ×N. |
grid | Inventory slots. Supports price. |
checklist | Objectives, with done. |
badge | A single footer line. |
Every item takes label, icon (emoji or short text), color, tooltip. Set showWhenCollapsed: true on a section to keep it visible when the player collapses the HUD.
ShowHud, HideHud and ToggleHud take no arguments.
If the scenario inherits the Voxta UI parent, import { hud } from "../base/hud" gives you set, push, setBar, damage, heal, addItem, removeItem, complete, setBadge instead of the manual get/set/push round-trip.
Item actions
Give an item an actions list and it gets a context menu. Picking one fires app:hud_action with { action, item, section }.
{ label: "Health Potion", icon: "🧪", qty: 3, actions: [
{ id: "drink", label: "Drink", icon: "🧪" },
{ id: "drop", label: "Drop", icon: "🗑️" },
]}
chat.addEventListener("app:hud_action", (e) => {
const { action, item } = e.arguments;
// ...
});Windows
A window is a movable surface separate from the HUD — a shop, a loot pile, a container. Several can be open at once. Windows are transient: re-open them when needed rather than restoring on resume.
chat.appTrigger("OpenPanel", {
id: "shop:blacksmith",
title: "Blacksmith",
anchor: "center",
sections: [
{ type: "grid", label: "Wares", items: [
{ label: "Iron Sword", icon: "⚔️", price: 120, actions: [{ id: "buy", label: "Buy", icon: "💰" }] },
]},
],
footer: [{ label: "Your gold: 250", icon: "💰" }],
});
chat.appTrigger("ClosePanel", "shop:blacksmith");Windows reuse the HUD section types. Item actions fire app:panel_action with { action, item, section, panel }. Opening with an existing id replaces that window.
Popups
Splash
A transient banner that fades in, holds, and fades out. Several queue up and play in order.
chat.appTrigger("Splash", {
text: "Quest Complete",
subtitle: "The Fallen Key",
icon: "🗝️",
sound: "audio/quest_complete.mp3", // optional scenario audio asset
duration: 4, // seconds, 1-30
});Notice
Dims the scene until the player presses the button. The press fires app:notice_dismissed with the notice's id.
chat.appTrigger("Notice", {
id: "letter",
title: "A Sealed Letter",
text: "The wax bears no crest.",
icon: "✉️",
button: "Continue",
});
chat.addEventListener("app:notice_dismissed", (e) => { /* e.arguments.id */ });Choice
A modal decision. Picking an option fires app:choice with the choice's id and the picked value. Yes/no is just a two-option choice.
chat.appTrigger("Choice", {
id: "rebel_offer",
title: "The Rebel Offer",
text: "Do you take the coin?",
choices: [
{ value: "accept", label: "Take it", icon: "🪙" },
{ value: "decline", label: "Refuse", icon: "✋" },
],
});
chat.addEventListener("app:choice", (e) => {
if (e.arguments.choice === "accept") chat.setFlag("joined_rebels");
});Screen effects
Cinematic feedback over the whole stage. Transient effects play once; sticky ones stay until changed.
chat.appTrigger("ScreenEffect", { type: "flash", color: "#e5484d", edges: true, intensity: 0.6 });
chat.appTrigger("ScreenEffect", { type: "shake", intensity: 0.6 });
chat.appTrigger("ScreenEffect", { type: "glow", color: "#3ad19b", intensity: 0.5 });
chat.appTrigger("ScreenEffect", { type: "blur", intensity: 0.6 });
chat.appTrigger("ScreenEffect", { type: "fade", color: "#000000", direction: "through" });| Type | Effect |
|---|---|
flash | Hard colored bloom. edges: true blooms from the screen edges (damage). |
glow | Soft warm bloom from the center (heal). |
shake | Decaying camera shake. |
blur | Quick defocus pulse (exertion). |
fade | Ease to or from a color. direction: out holds on it, in clears from it, through dips and returns. |
vignette | Sticky colored edge vignette. Supports pulse. |
tint | Sticky full-screen tint. Supports pulse. |
daze | Sticky woozy blur. |
Sticky effects clear with intensity: 0, or all at once with ClearScreenEffects.
Combat feedback
// A number that pops and drifts up. x/y are stage percentages, default over the character.
chat.appTrigger("FloatingText", { text: "-20", color: "#e5484d", size: "large" });
chat.appTrigger("FloatingText", { text: "+20", icon: "❤️", color: "#3ad19b" });
// An expanding ring burst at a point.
chat.appTrigger("Impact", { x: 50, y: 42, color: "#e5484d", intensity: 0.8 });size is a number in rem, or small / medium / large / crit.
Character effects
Effects on a character sprite, as opposed to the whole screen. Target with name or characterId; omit both to hit everyone on screen.
chat.appTrigger("CharacterEffect", { type: "hit", color: "#ff3b3b" }); // damage: flash + recoil
chat.appTrigger("CharacterEffect", { type: "flash", color: "#ffffff" });
chat.appTrigger("CharacterEffect", { type: "glow", color: "#3ad19b" }); // heal / buff
chat.appTrigger("CharacterEffect", { type: "shake" }); // fear / cold / stun
chat.appTrigger("CharacterEffect", { type: "bounce" }); // happy / jump
chat.appTrigger("CharacterEffect", { type: "tint", color: "#46a758", intensity: 0.5 }); // stickyHitCharacter is shorthand for CharacterEffect with type: "hit".
Split the two perspectives: the character on screen taking a hit is a CharacterEffect; the player taking a hit is a ScreenEffect.