API
Splash exposes an API for module and macro authors. Reach it at:
const splash = game.modules.get('splash').api;The API is assigned during Foundry’s init hook, and the splash.init hook fires immediately after. Register custom types from a listener on splash.init so they exist before any splash is built.
Hooks.once('splash.init', () => { const api = game.modules.get('splash').api; // register your sprites, actions, effects, animations, triggers here});Every first-party sprite, action, effect, animation, and trigger registers through this same surface, so the registration shape below is exactly what the built-ins use.
Registering types
Section titled “Registering types”Each register call takes a type string (the discriminator stored on the object’s data), a human name shown in the editor’s pickers, and a builder.
Sprites
Section titled “Sprites”api.registerSprite('my-sprite', 'My Sprite', (sprite, state, context) => { // return a PIXI.DisplayObject (or a Promise of one)});| Argument | What it is |
|---|---|
sprite |
The sprite’s data, including your custom fields. |
state |
The state the sprite is being instantiated into. |
context |
Per-sprite wiring back into the runtime. context.onAction(action) runs an action through the owning splash instance; call it from a click handler to keep behavior instance-scoped. |
Actions
Section titled “Actions”api.registerAction('my-action', 'My Action', (action) => { // run a side effect});The processor receives the action’s data. Actions registered this way run through the runtime’s external executor, which is how the built-in macro action fires a Foundry macro. The value and state actions (set-value, increment-value, change-state, close) are handled inside each running splash and are registered only so the editor can list them.
Effects
Section titled “Effects”api.registerEffect('my-effect', 'My Effect', (app, effect) => { // return a PIXI.Filter (or a Promise of one)});The builder gets the PIXI Application and the effect’s data, and returns a filter applied to the sprite.
Animations
Section titled “Animations”api.registerAnimation('my-anim', 'My Animation', (animation, sprite, app) => { // mutate sprite.filters or tween; runs for the animation's duration});The builder gets the animation’s data, the target PIXI.DisplayObject, and the PIXI Application. Use animation.duration and animation.delay to schedule cleanup, the way the built-in dissolve and glitch animations do.
Triggers
Section titled “Triggers”A trigger is a source in the world (a region, a door) wired to launch a splash. Register one with a type, a label, and the rest of the trigger definition.
api.registerTrigger('my-trigger', 'My Trigger', { icon: 'fa-solid fa-bolt', createBinding: async (splashUuid) => { // interactively wire a new binding; resolve true on success return true; }, listBindings: () => [ // every live binding of this type, world-wide ], removeBinding: async (binding) => { // tear the binding down },});A TriggerBinding carries an id (stable, used for removal), type, splashUuid, a human summary for the triggers list, and an optional sceneId. See Triggers for how bindings surface in the UI.
Inspecting the registry
Section titled “Inspecting the registry”| Getter | Returns |
|---|---|
api.registeredAnimations |
{ type, name }[] |
api.registeredEffects |
{ type, name }[] |
api.registeredActions |
{ type, name }[] |
api.registeredTriggers |
the registered TriggerDefinition[] |
api.getTrigger(type) |
one trigger definition, or undefined |
api.bindingsForSplash(uuid) |
every live binding pointing at that splash |
Launching splashes
Section titled “Launching splashes”These methods take a splash journal page UUID and respect view and trigger permissions.
| Method | What it does |
|---|---|
api.launch(uuid, opts) |
Open the splash using its stored presentation. A handout opens a windowed app; scene, hud, and full open a fullscreen overlay. |
api.show(uuid, opts) |
Show the splash at a given layer. |
api.openHandout(uuid) |
Open the handout window directly. |
api.close(opts) |
Close the active splash locally. |
launch and show accept { global, targetUser }. global is GM-only and shows the splash table-wide, persisting until closed. targetUser shows it transiently to one player. With neither, the splash opens locally for you. show also takes layer (full by default). close accepts { global }, GM-only, to kill the splash for the whole table.
await api.launch('JournalEntry.abc.JournalEntryPage.def', { global: true });See Layers & Modes for what each presentation looks like, and Synced Mode for how global splashes share state.
Inline-macro scripts
Section titled “Inline-macro scripts”A button’s script action runs a snippet of JavaScript inside the running splash. The source executes as an async function body with three arguments in scope.
| Name | What it is |
|---|---|
scope |
The firing sprite’s node in the splash’s sprite tree. The node carries id, name, type, parent, children, and child.get(name) to find one of its children by name (reach a sibling through scope.parent.child.get(name)). scope.get(property) and scope.set(property, value) read and write a sprite property, and scope.text is a live accessor for its text. Writes are ephemeral synced overrides, not saved to the document. |
context |
The firing sprite’s data bag (scope.context), a plain object you can stash values on. |
api |
A small surface for driving the splash from the script (below). |
The script api is scoped to this splash instance, not the module-level API:
| Member | What it does |
|---|---|
api.changeState(load, unload) |
Load and unload states by id, the same as a change-state action. |
api.setValue(key, value) |
Set a named runtime value. |
api.close() |
Request the splash close. |
api.unlockDoor(uuid) |
Unlock a door. Defaults to the door that triggered the splash; routed through the GM when a player can’t write walls. |
api.trigger |
What launched this splash, e.g. { door } for a door trigger. |
// Reveal a hidden state when the dialed letters spell the word.if (scope.child.get('lock').text === 'OPEN') { api.changeState(['vault-open'], ['vault-locked']); api.unlockDoor();}scope.set and scope.text write synced overrides, so a dialed letter or revealed value stays consistent across the table in synced mode. See Actions & Interactivity for wiring buttons to scripts.