MacroHero

Welcome to the official user guide for MacroHero β€” the all-in-one macro and automation extension for Owlbear Rodeo. Build visual controls, automate your table and connect your favorite extensions.


Getting Started

  1. Install MacroHero: Add the extension manifest URL https://macrohero.onrender.com/manifest.json using Owlbear Rodeo's "Install from manifest" option.
  2. Enable MacroHero in your Owlbear Rodeo session.
  3. Open MacroHero from the sidebar to access the main interface.

Visual Editor

  1. Click the Configuration (βš™οΈ) button in the header to open the visual editor modal.
  2. The visual editor is a GUI wrapper around the underlying JSON configuration β€” every change you make updates the JSON and vice‑versa. There are no separate editor "tabs" or modes: the visual editor is the single interface for changing your configuration.
  3. Use the editor to add pages, define variables, and place items (buttons, values, inputs, counters, stacks, rows, dividers, etc.).
    • Add Page: give it a label and open it to add variables and layout items.
    • Add Variable: set a key and an expression. Optionally set numeric min/max.
    • Add Item: pick a type (value, input, checkbox, counter, button, text, title). Configure its label, link it to a var (when applicable), and add commands arrays to buttons.
  4. Commands: buttons accept an array of command strings β€” they execute sequentially when clicked. Commands are written in pure JavaScript and run in an async context. Use await when calling integration functions (e.g., await OwlTrackers.addValue(token, 'HP', 10)), reference variables by name, use operators, conditionals, and any standard JavaScript syntax.
  5. Counter items respect the variable's min/max and accept an optional step setting (default 1).
  6. Save / Cancel: Use the modal controls to save changes (persisted per-room) or cancel to discard edits. Changes are written to room metadata/local storage as appropriate.
  7. Validation & Debug: the editor validates syntax where possible; use temporary value items to surface intermediate variables and test expressions before using them in commands.

Visual Editor Modal Walkthrough

Advanced Modal Features

Token Visualization Tool

The configuration modal includes a Token Viewer tab that displays all tokens and items currently in your Owlbear Rodeo scene. This tool helps you:

Usage: Open the config modal (βš™οΈ) and click the Token Viewer tab. Use the search box to find tokens by name, or expand layers to browse all items. Click "Copy ID" next to any token to use it in your configuration.

Debug Mode

MacroHero includes a comprehensive Debug Mode system that lets you enable detailed console logging for specific modules. This is invaluable for troubleshooting expressions, commands, and integration calls.

Usage: Open the config modal (βš™οΈ), click the Debug Mode tab, and check the boxes for modules you want to debug. Open your browser's developer console (F12) to see the logs. Useful for diagnosing why expressions return unexpected values or why commands fail.

Items Reference

Each page's layout array is built from items. Below is the complete reference for every available item type and its properties.

Layout & Display

Data Binding

Actions

Layout Containers


Configuration Guide & Syntax

MacroHero is configured using a JSON structure that defines global variables, pages, UI layout, and commands. You can edit this configuration via the visual editor (βš™οΈ) or by importing/exporting JSON. Below is a step-by-step guide with copy-pasteable examples for every feature.

The global Section

The global section sets up your extension's title, window size, and variables available everywhere. Here’s a real-world example:

{
    "global": {
      "title": "Macro Hero",
      "width": 800,
      "height": 600,
      "variables": {
        "playerName": {
          "expression": "Local.value('playerName', 'Unknown')"
        }
      }
    }
  }

Defining Variables & Expressions

Variables are defined in the variables section of either global or a page. Each variable uses an expression string written in pure JavaScript. You can reference other variables directly by name, use standard JavaScript math operators, call integration functions, and use any JavaScript syntax (ternary operators, string concatenation, etc.).

{
    "variables": {
      "hp": { "expression": "OwlTrackers.getValue(token, 'HP')" },
      "isBandaged": { "expression": "ConditionMarkers.hasCondition(token, 'Bandaged')" },
      "bonus": { "expression": "5" },
      "total": { "expression": "hp + bonus" }
    }
  }

Creating & Organizing UI Items

Each page's layout array defines the UI. See the Items Reference for the full list of types and properties. Quick reference:

Writing Scripts & Using Integrations

The onclick, onrightclick, and onupdate fields accept a JavaScript script as an array of strings β€” each string is a line, joined and executed in an async context. Variables are in scope by name; use await for async integration calls.

{
    "type": "button",
    "label": "Heal if Bandaged",
    "onclick": ["await (isBandaged ? OwlTrackers.addValue(token, 'HP', 10) : OwlTrackers.addValue(token, 'HP', -5))"]
  }

Supported integrations include: GoogleSheets, Local, OwlTrackers, StatBubbles, ConditionMarkers, ColoredRings, PrettySordid, JustDices, DicePlus, Weather, Embers, Auras, Aurora, Announcement. See the API reference for all functions.

JSON Structure for Pages, Layouts, and Commands

{
    "global": { ... },
    "pages": [
      {
        "label": "Page Name",
        "variables": { ... },
        "layout": [ ... ]
      }
    ]
  }

Saving & Persistence

MacroHero stores different kinds of data in different places to balance persistence, size limits, and privacy:

Full Example: Copy-Pasteable Page

{
    "label": "Health Controls",
    "variables": {
      "token": { "expression": "'token-id-here'" },
      "hp": { "expression": "OwlTrackers.getValue(token, 'HP')" },
      "maxHp": { "expression": "OwlTrackers.getMax(token, 'HP')" },
      "revives": { "expression": "3", "min": 0, "max": 10 }
    },
    "layout": [
      { "type": "title", "text": "HP Controls" },
      { "type": "value", "var": "hp", "label": "Current HP" },
      { "type": "value", "var": "maxHp", "label": "Max HP" },
      { "type": "button", "label": "Heal 10", "onclick": ["await OwlTrackers.addValue(token, 'HP', 10)"] },
      { "type": "button", "label": "Damage 5", "onclick": ["await OwlTrackers.addValue(token, 'HP', -5)"] },
      { "type": "button", "label": "Heal to Max", "onclick": ["await OwlTrackers.setValue(token, 'HP', maxHp)"] },
      { "type": "title", "text": "Revives" },
      { "type": "value", "var": "revives", "label": "Revives Left" },
      { "type": "counter", "var": "revives", "label": "Use Revive", "step": 1 }
    ]
  }

Notes: the revives variable includes min/max so the counter will stay within 0–10, and step is set to 1 (optional).

Paste this into your configuration (via the visual editor or JSON import) and adjust variable names as needed.

Integrations

MacroHero provides integrations with popular Owlbear Rodeo extensions and external services. All integration functions are async and should be used with await in commands when necessary.

See the detailed API reference below for all available functions.

Integrations API Reference

This section documents all public functions exposed by MacroHero's built-in integrations. Use these functions in variable expressions and in commands. Examples show how to call them from configuration JSON (expressions) or commands.

Local

GoogleSheets

JustDices

OwlTrackers

StatBubbles

ConditionMarkers

ColoredRings

PrettySordid

Embers

Embers provides a fluent API for creating and chaining spell visual effects (projectiles, area effects, cone effects, and gameplay actions) using the Embers extension for Owlbear Rodeo.

EmbersSequence Fluent Methods
Convenience Helpers
Embers Examples

Single Projectile Effect

await Embers.sequence()
  .projectile('generic.weapon_attacks.ranged.laser_shot.orange', caster, target)
  .cast()

Projectile Followed by AOE (Sequential)

await Embers.sequence()
  .projectile('generic.weapon_attacks.ranged.laser_shot.orange', caster, target, {spellName: 'Fireball'})
  .then()
  .aoe('generic.impact.fire', target, {size: 5})
  .cast()

Multiple Projectiles (Simultaneous)

await Embers.sequence()
  .projectile('effectId', caster, target1)
  .projectile('effectId', caster, target2)
  .projectile('effectId', caster, target3)
  .cast()

Cone Effect with Delay

await Embers.sequence()
  .cone('generic.weapon_attacks.melee.single.generic_slash', caster, target, {size: 6, spellName: 'Cone Attack'})
  .named('Sweep')
  .cast()

Complex Chain with Delays

await Embers.sequence()
  .projectile('missile', caster, target, {spellName: 'Chain Lightning'})
  .delay(200)
  .then()
  .aoe('impact', target, {size: 4})
  .delay(300)
  .then()
  .cone('secondary', caster, target2, {size: 3})
  .cast()

Using Convenience Helpers

// Shortcut for simple projectiles
await Embers.castProjectile('effectId', caster, target, {spellName: 'Arrow'})

// Shortcut for AOE
await Embers.castAOE('effectId', target, {size: 5})

// Shortcut for cone
await Embers.castCone('effectId', caster, target, {size: 6})
How Delays Work

When you call .delay(ms), the delay is applied to the next effect, not the previous one. Delays stack β€” multiple consecutive .delay() calls add together.

// This delays the aoe by 300ms (projectile executes, then 300ms passes, then aoe fires)
Embers.sequence()
  .projectile('effect1', caster, target)
  .delay(300)
  .aoe('effect2', target)
  .cast()
How Sequential Execution Works (.then())

The .then() method creates a nested sequence where following effects execute after all previous effects complete. Without .then(), effects execute simultaneously. You can chain multiple .then() calls to create a complex sequence.

// Effects execute in order: projectile β†’ (waits for completion) β†’ aoe β†’ (waits) β†’ cone
await Embers.sequence()
  .projectile('missile', caster, target)
  .then()
  .aoe('impact', target, {size: 5})
  .then()
  .cone('secondary', caster, target2)
  .cast()
Notes on Embers

Weather

Control weather effects on maps. Works with the official Weather extension. For advanced features (ENERGYSTORM, WATER, CURRENT weather types and tint color support), use the weather-extended fork.

Auras

Manage auras on tokens and items using the Auras extension (formerly Emanation). Auras are visual effects that appear around tokens, including glows, bubbles, ranges, images, and custom effects.

Auras Examples

Add a Red Glow Aura

await Auras.addAura(token, {
  style: 'Glow',
  color: '#FF0000',
  size: 5
})

Add a Blue Range Aura with Opacity

await Auras.addAura(token, {
  style: 'Range',
  color: '#0000FF',
  size: 8,
  opacity: 0.6
})

Add an Aura with Blend Mode (Glow + PLUS mode simulates light)

await Auras.addAura(token, {
  style: 'Glow',
  color: '#FFFF00',
  size: 4,
  blendMode: 'PLUS'
})

Add a Custom Shader Aura

await Auras.addAura(token, {
  style: 'Custom',
  size: 5,
  sksl: 'your_shader_code_here'
})

Check and Remove Auras

const hasAuras = await Auras.hasAura(token)
if (hasAuras) {
  const aurasList = await Auras.getAuras(token)
  console.log(`Token has ${aurasList.length} aura(s)`)
  await Auras.removeAura(token)
}
Auras Notes & Limitations

Scene & Token Helpers

Advanced utility functions for scene and token manipulation. These are helper modules exposed directly in the expression/command context.

sceneHelpers
tokenHelpers

Metadata Modules (Advanced)

Low-level OBR SDK access for expert users. These modules provide direct metadata manipulation.

These expose raw OBR SDK operations. Use with caution. Refer to source code for function signatures.

Aurora

Control ambient lighting and time-of-day effects on maps using the Aurora extension.

DicePlus

Advanced dice rolling with full dice notation support: exploding dice (!), keep highest (kh), drop lowest (dl), and more.

Announcement

Show and manage scene-wide announcement banners visible to all players.

Example: Full Page with Weather

Here is a full, copy-pasteable page example that demonstrates variables, expressions, counters with min/max, inputs, Weather integration, and commands that call integrations (GoogleSheets, OwlTrackers, JustDices).

{
  "label": "Character Demo",
  "variables": {
    "token": { "expression": "'your-token-id-here'" },
    "hp": { "expression": "OwlTrackers.getValue(token, 'HP')" },
    "maxHp": { "expression": "OwlTrackers.getMax(token, 'HP')" },
    "name": { "expression": "GoogleSheets.getValue('Roster', 'A2')" },
    "mapId": { "expression": "sceneHelpers.getMapIdFromToken(token)" },
    "revives": { "expression": "3", "min": 0, "max": 5 }
  },
  "layout": [
    { "type": "title", "text": "Character Demo" },
    { "type": "value", "var": "name", "label": "Name" },
    { "type": "row", "children": [
      { "type": "value", "var": "hp", "label": "HP" },
      { "type": "value", "var": "maxHp", "label": "Max" }
    ]},
    { "type": "row", "children": [
      { "type": "button", "label": "Heal 10", "onclick": ["await OwlTrackers.addValue(token, 'HP', 10)"] },
      { "type": "button", "label": "Damage 5", "onclick": ["await OwlTrackers.addValue(token, 'HP', -5)"] }
    ]},
    { "type": "divider" },
    { "type": "title", "text": "Weather Controls" },
    { "type": "row", "children": [
      { "type": "button", "label": "Snow", "onclick": ["await Weather.setWeather(mapId, {type: 'SNOW', speed: 2, density: 3})"] },
      { "type": "button", "label": "Rain", "onclick": ["await Weather.setWeather(mapId, {type: 'RAIN', speed: 3, density: 4})"] },
      { "type": "button", "label": "Remove", "onclick": ["await Weather.removeWeather(mapId)"] }
    ]},
    { "type": "divider" },
    { "type": "value", "var": "revives", "label": "Revives" },
    { "type": "counter", "var": "revives", "label": "Use Revive", "step": 1 },
    { "type": "input", "var": "note", "label": "Note", "placeholder": "Notes..." },
    { "type": "button", "label": "Quick Attack", "onclick": ["await JustDices.roll('1d20 + ' + (hp % 5))"] }
  ]
}

Paste this page JSON into the editor (or import it). Replace 'your-token-id-here' with a real token id. The revives counter respects min/max, step sets the increment, and weather buttons demonstrate the Weather integration.

Stack Item & Multi-Line Script Example

Use stack to group items vertically. Here's a minimal example with a multi-line button script:

{
  "label": "Stack + MultiCmd",
  "variables": {
    "token": { "expression": "'your-token-id-here'" },
    "revives": { "expression": "2", "min": 0, "max": 5 }
  },
  "layout": [
    { "type": "title", "text": "Stack & Multi-Command" },
    { "type": "stack", "children": [
      { "type": "value", "var": "revives", "label": "Revives Left" },
      { "type": "input", "var": "note", "label": "Note" },
      { "type": "button", "label": "Use Revive & Roll",
        "onclick": [
          "revives = revives - 1",
          "await OwlTrackers.addValue(token, 'HP', 10)",
          "await JustDices.roll('1d6')"
        ]
      }
    ]}
  ]
}

When the button is clicked it decrements revives, heals the token's HP by 10, then rolls a d6 and returns the result.

Advanced Usage

Advanced usage focuses on combining variables, expressions and integrations to build responsive and robust UI pages.

Expressions & Debugging

Async & Commands

Tips & Best Practices

Troubleshooting & Common Issues

Quick fixes

Google Sheets

Common mistakes

Contact

Need help, found a bug, or want to suggest an improvement? Choose whichever channel works best for you.

Owlbear Rodeo Discord DM or ping me in the community server. @citron3400
Dedicated Discord Join my server for direct help and project discussions. Join server GitHub Issues Report a reproducible bug or request a feature. Open an issue