Macros & Automation

How to build chat and script macros for Foundry VTT Version 14, run them from the hotbar, and trigger automation with Hooks and Region behaviors.

The Magic of Automation

Imagine a tireless assistant who remembers every rule, does the sums, and handles the bookkeeping while you focus on storytelling. That's what macros and automation bring to Foundry VTT.

Think of macros as spells you write once and cast whenever you need them. Want to roll stealth for every hidden goblin? One click. Want the lights to drop and a message to appear when the villain arrives? One click. Want a trap to spring when a token steps on it? A Region behavior can do that without any click at all. You don't have to be a programmer: many useful macros are a few lines long, and chat macros need no code.

The Automation Ecosystem

flowchart TD A[Automation in Foundry] --> B[Chat Commands] A --> C[Chat Macros] A --> D[Script Macros] A --> E[Active Effects] A --> F[Region Behaviors] A --> G[Modules] B --> B1["/roll, /gmroll, /w<br/>/emote, /macro"] C --> C1["Saved messages<br/>and rolls"] D --> D1["JavaScript using<br/>game, canvas, ui, Hooks"] E --> E1["Bonuses and conditions<br/>that expire on their own"] F --> F1["Execute Macro, Execute Script<br/>Teleport Token and more"] G --> G1["Monk's Active Tile Triggers<br/>Sequencer, system add-ons"]

Your First Macro: Baby Steps to Power

Foundry has two kinds of macro:

To make one, open the Macros tab of the sidebar and create a new macro, or click an empty slot on the macro hotbar at the bottom of the screen. Give it a name, choose Chat or Script, pick an image, type the command, and save. Drag macros onto the hotbar: it has 10 slots per page and 5 pages, and the number keys 1 to 9 and 0 run the macros on the current page. You can also run any macro by typing /macro followed by its name in chat. Many game systems also let you drag an item or ability from a character sheet onto the hotbar to make a macro for it.

Here is the same macro as text you can copy. It rolls initiative, using your game system's formula, for the selected tokens that are already in the current combat:

// Roll initiative for the selected tokens in the current combat
const combat = game.combat;
if (!combat) return ui.notifications.warn("Start an encounter first.");
const ids = canvas.tokens.controlled
  .map(t => t.combatant?.id)
  .filter(id => id);
if (!ids.length) return ui.notifications.warn("Select tokens in combat.");
await combat.rollInitiative(ids);
ui.notifications.info(`Rolled for ${ids.length} combatant(s).`);

Script macros run inside an async function, so you can use await and an early return at the top level.

Essential Macros Every GM Needs

Here's a macro starter kit: simple but powerful tools for common tasks.

flowchart TD A[GM Macro Toolkit] --> B[Combat Helpers] A --> C[Scene Management] A --> D[Communication] A --> E[Token Control] B --> B1["Roll initiative for selected<br/>next turn, end combat<br/>heal the party"] C --> C1["Lighting presets<br/>darkness level, fog reset<br/>scene transitions"] D --> D1["GM description<br/>whisper a player<br/>sound cues"] E --> E1["Toggle visibility<br/>apply conditions<br/>token cleanup"]

Chat Commands: Your Quick Access Toolbar

Everything here works typed straight into chat, and anything you can type in chat can be saved as a chat macro.

Roll Formula Cheat Sheet

These formulas work in chat, in chat macros, and in new Roll(...) inside scripts. Try them first in the Dice Formula Tester.

FormulaWhat it does
2d20kh / 2d20klRoll two d20s, keep the highest / lowest
4d6kh3 or 4d6dlRoll four d6, keep the best three (drop the lowest)
5d10x10 / 6d10xo10Explode on a 10 / explode only once
1d10r1 / 1d20rr<3Reroll a 1 once / keep rerolling results under 3
10d20cs>=15Count successes: dice showing 15 or more
4d10min2 / 4d10max8Treat lower results as 2 / higher results as 8
4dfFour Fate dice (each -1, 0 or +1)
1d100Percentile roll
1d20 + @abilities.cha.modPull a value from the character (paths depend on the game system; this one is dnd5e)

Building Complex Macros: From Simple to Spectacular

Let's grow from one-line macros to ones that ask questions and make decisions.

Example: Weather Control Macro

// Weather Control: pick the sky, set the scene's darkness, tell the table
const choice = await foundry.applications.api.DialogV2.wait({
  window: { title: "Weather Control" },
  content: "<p>What is the sky doing?</p>",
  buttons: [
    { action: "clear", label: "Clear skies", default: true },
    { action: "dusk", label: "Dusk" },
    { action: "storm", label: "Thunderstorm" }
  ]
});
const darkness = { clear: 0, dusk: 0.5, storm: 0.8 }[choice];
if (darkness === undefined) return;   // dialog closed without a choice
await canvas.scene.update({ "environment.darknessLevel": darkness });
await ChatMessage.create({ content: `<p>The weather turns: ${choice}.</p>` });
// Rain or snow particles: set them in the scene's configuration, or use a module

Active Effects: Living Buffs and Debuffs

Active Effects are like enchantments that know their own rules. V14 rebuilt them as Active Effects V2:

flowchart LR A[Active Effect] --> B[Changes] A --> C[Duration] A --> D[Token Display] A --> E[Sources] B --> B1["AC, ability scores, speed<br/>paths depend on the system"] C --> C1["Rounds, turns or time<br/>expires at start or end of a turn,<br/>a round, or the combat"] D --> D1["Status icon: never, conditional or always<br/>can change the token, such as its image"] E --> E1["Items, spells, status icons<br/>effect compendiums, drag onto tokens"]

Example: Bless Spell Effect (dnd5e)

Practical Macro Library

Copy these into script macros. Test them on a spare world before game night.

The "Oh No!" Emergency Kit

1. Heal All PCs (dnd5e)

// Emergency healing for all player-owned actors.
// The HP path below is for the dnd5e system; other systems use different paths.
const pcs = game.actors.filter(a => a.hasPlayerOwner);
for (const pc of pcs) {
    const hp = pc.system.attributes?.hp;
    if (hp) await pc.update({ "system.attributes.hp.value": hp.max });
}
ui.notifications.info("All PCs healed to full!");
                

2. Dramatic Lighting Change

// Sudden darkness falls!
await canvas.scene.update({ "environment.darknessLevel": 0.9 });
await ChatMessage.create({
    content: "<h2>Darkness Falls!</h2><p>The lights suddenly dim...</p>",
    speaker: { alias: "GM" }
});
                

3. Roll Stealth for All Enemies (dnd5e)

// Mass stealth check, whispered to you.
// @skills.ste.total is a dnd5e path; other systems name skills differently.
const hostiles = canvas.tokens.placeables.filter(t =>
    t.actor && t.document.disposition === CONST.TOKEN_DISPOSITIONS.HOSTILE
);
for (const token of hostiles) {
    const roll = new Roll("1d20 + @skills.ste.total", token.actor.getRollData());
    await roll.evaluate();
    await roll.toMessage({
        speaker: { alias: token.name },
        flavor: "Stealth Check",
        whisper: [game.user.id]
    });
}
                

4. Toggle Visibility of Selected Tokens

// Hide or reveal the selected tokens in one click
const tokens = canvas.tokens.controlled;
if (!tokens.length) return ui.notifications.warn("Select tokens first!");
for (const t of tokens) {
    await t.document.update({ hidden: !t.document.hidden });
}
                

Want to move tokens somewhere when they step on a spot? You don't need a macro: give a Region the Teleport Token behavior (see below).

Hooks: Macros That React

A macro normally runs when you click it. Hooks let code run when something happens in the game. Hooks.on(name, fn) runs fn every time the event fires; Hooks.once runs it only the first time. Modules use the lifecycle hooks init, setup and ready, and canvasReady fires when a scene finishes drawing.

// Announce each new round in chat.
// A hook registered by a macro lasts until the page is reloaded.
Hooks.on("updateCombat", (combat, changed) => {
  if (!game.user.isGM || !("round" in changed)) return;
  ChatMessage.create({ content: `<h3>Round ${combat.round} begins!</h3>` });
});
ui.notifications.info("Round announcer is on until you reload.");

Where hooks belong: run from a macro, a hook disappears when anyone reloads, and running the macro twice registers it twice. For something you want every session, put it in a small module of your own, or use a Region behavior or Monk's Active Tile Triggers instead.

Region Behaviors: Automation on the Map

Scene Regions (V12 and later) are areas you draw with the Regions tool. Each Region can carry behaviors that fire on events, such as a token entering or leaving the area, or a turn starting while a token is inside. Two behaviors run your own code:

Many jobs need no code: Teleport Token moves a token to another Region (even on another scene), Display Scrolling Text floats a message over the map, Pause Game stops play, Adjust Darkness Level darkens an area, Modify Movement Cost makes difficult terrain, and Toggle Behavior switches other behaviors on or off. V14 adds Change Level and Define Surface for Scene Levels.

Example: A Pit Trap with Execute Script

  1. Select the Regions tool and draw a rectangle over the trap's squares.
  2. Open the Region's configuration, add an Execute Script behavior, and choose the event for a token entering the Region.
  3. Paste a script like this one and save:
// Execute Script behavior. The script receives the event that fired it;
// for token events, event.data.token is the token that triggered it.
const token = event.data.token;
if (!token) return;
const roll = await new Roll("2d6").evaluate();
await roll.toMessage({ flavor: `${token.name} falls into a pit!` });

Test it with a player account as well as your own, because players and the GM can see different things.

Monk's Active Tile Triggers

Before Regions existed, the module Monk's Active Tile Triggers was the usual way to build traps and teleporters. It is verified for V14 and still worth a look if you want a long menu of ready-made actions (open doors, play sounds, change lighting, move tokens, run macros) triggered by tiles, without writing code. For simple teleporters and triggers, core Regions now cover a lot of what it used to do, with one fewer module to keep updated.

Module Automation: Supercharge Your Game

Modules add more automation. These are verified for V14 as of this writing; check each package page before you update.

Your game system may have its own automation add-ons too. For dnd5e, for example, Midi-QOL (verified for Version 14 in October 2026, usually paired with Dynamic Active Effects) automates attack, damage and save workflows, and Token Action HUD gives each token a clickable action bar. Automated Animations can then play an effect for each attack or spell. Check that any system add-on supports both your Foundry version and your system version before you install it.

Try It in Foundry

Starter Set

  1. Welcome Message: A chat macro that posts a dramatic welcome when the session starts
  2. Secret Check: A chat macro with /gmroll 1d20 and a flavor label
  3. Torch Toggle: A script macro that gives the selected token light, or removes it
  4. Initiative Cleaner: Remove all NPCs from the combat tracker
  5. Inspiration Giver: Award inspiration with a fanfare message in chat

Scene Automation

  1. Day/Night Cycle: A macro that switches the darkness level between day and night
  2. Weather Picker: Extend the weather macro above with more choices
  3. Pit Trap: A Region with Execute Script that rolls damage when a token enters
  4. Secret Passage: Two Regions with Teleport Token that link a hidden door to the next room
  5. Round Announcer: The updateCombat hook from this page, with your own message

Bigger Projects

  1. Smart Healing: Heal the most injured PC first and announce it in chat
  2. Condition Manager: Keep a compendium of Active Effects with durations and drag them onto tokens
  3. Loot Roller: A macro that draws from a Roll Table and posts the result
  4. Scene Narrator: Post a description from a journal page when a token enters a Region

Debugging Your Macros

When macros go wrong (and they will!), here's how to fix them:

Common Issues and Solutions

Debug Helper Macro

// Debug helper: shows useful info in the console
console.log("=== DEBUG INFO ===");
console.log("Selected Tokens:", canvas.tokens.controlled);
console.log("Targeted Tokens:", game.user.targets);
console.log("Active Scene:", canvas.scene?.name);
console.log("Current User:", game.user.name);
console.log("Combat Active:", game.combat?.active);
ui.notifications.info("Check the console for debug info (F12)");
            

Ideas from the Table

The One-Button Boss Fight

One macro can turn a throne room into a battle arena: it reveals hidden walls of guards, drops the darkness level, starts the boss music from a playlist, and posts a dramatic line to chat. Say "You shouldn't have insulted the king" and press the button.

The Living Street

Give each shop a Region. A macro that you run at nightfall hides the shopkeeper tokens, turns on the street lamps, and reveals the guard patrol. Your players will start planning heists around it.

The Personalized Hotbar

Give each player their own macros. The barbarian gets a "RAGE!" button that applies the rage effect and posts a battle cry; the wizard gets a chat macro that whispers her spell list reminder. Players love their own buttons.

Macro Best Practices

Writing Good Macros

Organizing Your Macros

Where to Go Next

Foundry can now do the busywork for you: chat and script macros on the hotbar, hooks that react to the game, and Regions that spring traps on their own. Every click saved is more time for epic moments.