4 Examples
Flaig edited this page 2026-10-15 17:21:51 +02:00

Examples

Copy these into a Script macro. Each filter page also ends with an example for that filter.

These examples demonstrate the API and pass parameter validation. Their exact appearance and long-running stability have not all been checked in Foundry. Some effects intentionally darken, replace or hide the original image. Use a local preview to judge an effect on your target before saving it. The filter pages record individual visual checks.

Fire, Electric and Fog have confirmed GPU precision defects in their inherited shaders. Those require the respective highp shader fixes; changing example values alone does not reliably fix black or transparent output.

Toggle an effect

Run once to add, again to remove.

const id = "ward";
for (const token of canvas.tokens.controlled) {
  if (leanFX.hasFilterId(token, id)) await leanFX.deleteFilters(token, id);
  else await leanFX.addFilters(token, [{
    filterType: "glow", filterId: id, color: 0x66ccff, outerStrength: 4, distance: 12,
    animated: {outerStrength: {animType: "syncCosOscillation", loopDuration: 3000, val1: 2, val2: 5}}
  }]);
}

Targeted tokens

for (const token of game.user.targets) {
  await leanFX.addUpdateFilters(token, [{
    filterType: "outline", filterId: "marked", color: 0xff2222, thickness: 2, quality: 0.5,
    animated: {thickness: {animType: "syncCosOscillation", loopDuration: 800, val1: 1, val2: 3}}
  }]);
}

A one-shot hit flash

A red flash that fades in and out over 600 ms, then removes itself.

const token = canvas.tokens.controlled[0];
await leanFX.addUpdateFilters(token, [{
  filterType: "adjustment", filterId: "hit",
  onComplete: "delete",
  animated: {
    red: {animType: "halfCosOscillation", loopDuration: 300, loops: 2, val1: 1, val2: 3},
    green: {animType: "halfCosOscillation", loopDuration: 300, loops: 2, val1: 1, val2: 0.4},
    blue: {animType: "halfCosOscillation", loopDuration: 300, loops: 2, val1: 1, val2: 0.4}
  }
}]);

With two loops, a half-wave goes to val2 and back to val1, so the token ends unchanged.

A burning aura with a shield

Two filters on one token. zOrder decides which applies first.

await leanFX.addUpdateFiltersOnSelected([
  {filterType: "xglow", filterId: "fire-aura", auraType: 2, color: 0x903010, thickness: 9.8, scale: 4,
    auraIntensity: 2, subAuraIntensity: 1.5, threshold: 0.4, discard: true,
    animated: {time: {animType: "move", speed: 0.0027}}},
  {filterType: "field", filterId: "shield", shieldType: 2, gridPadding: 2, color: 0xcc00cc, blend: 3,
    lightAlpha: 0.5, lightSize: 0.5, animated: {time: {animType: "move", speed: 0.0015}}}
]);

Water on a Region

Select the Region on the Regions layer first. The effect follows its geometry and stays visible when you return to Token Controls. This example draws above tiles and drawings, below tokens at the same elevation. Leave editor Visibility layer-only; see Region rendering.

const region = canvas.regions.controlled[0];
await leanFX.addUpdateFilters(region, [{
  filterType: "flood", filterId: "water", color: 0x0020bb, billowy: 0.43, tintIntensity: 0.72, glint: 0.31, scale: 70,
  animated: {time: {animType: "move", speed: 0.0006}}
}], {placement: "aboveTiles"});

Fog on a Tile

const tile = canvas.tiles.controlled[0];
await leanFX.addUpdateFilters(tile, [{
  filterType: "fog", filterId: "fog", color: 0x000000, density: 0.65,
  animated: {time: {animType: "move", speed: 2.2}}
}]);

Hovering

A slow up-and-down float, for flying tokens.

await leanFX.addUpdateFiltersOnSelected([{
  filterType: "transform", filterId: "hover", padding: 30,
  animated: {translationY: {animType: "sinOscillation", loopDuration: 3000, val1: -0.04, val2: 0.04}}
}]);

Try before saving

Shows an effect only to you for five seconds.

const token = canvas.tokens.controlled[0];
const preview = leanFX.preview(token, [{
  filterType: "electric", filterId: "try", color: 0x99ccff, intensity: 3,
  animated: {time: {animType: "move", speed: 0.002}}
}]);
setTimeout(() => preview.clear(), 5000);

Change one parameter

Updates merge, so only color changes; the animation keeps running.

await leanFX.updateFiltersOnSelected([{filterType: "glow", filterId: "ward", color: 0xffaa00}]);

Remove all effects from the scene

for (const layer of [canvas.tokens, canvas.tiles, canvas.drawings, canvas.regions]) {
  for (const placeable of layer.placeables) {
    if (leanFX.getFilters(placeable).length) await leanFX.deleteFilters(placeable);
  }
}

Check why an effect does not show

const token = canvas.tokens.controlled[0];
console.log(leanFX.getFilters(token));   // what is saved
console.log(leanFX.inspect(token));      // what is drawn, and errors (bad image path, shader failure, ...)

Use leanFX from a module

Hooks.once("ready", () => {
  const fx = game.modules.get("leanFX")?.api;
  if (!fx) return;   // leanFX not active
  Hooks.on("targetToken", (user, token, targeted) => {
    if (user !== game.user || !token.document.isOwner) return;
    const spec = {filterType: "outline", filterId: "my-module-target", color: 0xffcc00, thickness: 2};
    if (targeted) fx.addUpdateFilters(token, [spec]);
    else fx.deleteFilters(token, spec.filterId);
  });
});

Prefix your filter IDs with your module's name so they don't collide with the user's own effects.