Skip to content
MotionActor
Esc
↑↓navigate↵open⌘Jpreview
On this page

Effects

Typed timeline records the renderer reads as overlays, with normalized progress resolved per frame.

An effect is not a mutation. It is a record on the timeline that says “this actor is under this effect, at this progress, at this frame” — and a renderer decides what that looks like.

Define

An effect belongs to one backend. It declares its params once; render is typed from them and from the backend’s render contract, so there is no separate params type to keep in step.

import { color, colorType, defineEffect, signal, unitInterval } from "@motionactor/core";
import { dom } from "@motionactor/renderers";

export const glow = defineEffect(dom, {
  name: "glow",
  params: {
    color: signal.of(colorType, color("#22d3ee")),
    spread: signal(18),
    progress: signal.of(unitInterval, 0),
  },
  render({ state, context, params }) {
    // params: { color: Color; spread: number; progress: number }
    state.overlays.push(/* a box-shadow overlay */);
  },
});

Its wire identity is { backend: "dom", name: "glow" }. A DOM glow and a Pixi glow are two values with two identities. Two effects with the same name on one backend throw when the backend is created.

Install

A backend lists its effects next to its actor renderers, as installed code:

createBackend(dom, { renderers: [textBlockRenderer], effects: [glow, blur] });

Apply

Calling the effect with some of its params makes what ctx.apply takes; undeclared params keep their declared defaults.

const card = ctx.spawn(Card);
const fx = ctx.apply(card, glow({ spread: 24 }));

yield* fx.play({ duration: 0.4, easing: Easing.inOutCubic });
yield* fx.spread.tween(48, 0.5); // params are signals on the handle
fx.detach();

Describe

describeEffect(glow) returns the param metadata an editor needs (kind, type, range, default), read from the declaration.

At resolution time

compiled.at(frame) exposes the active effects on each ResolvedActor as actor.effects: ActiveEffect[]. Renderers read this array to apply overlays.

The card above is drawn from card.effects alone: ctx.apply records the glow, play drives its progress, a param tween widens it, and detach ends the record — the card’s own state never changes.

const state = compiled.at(frame);
const card = state.findActor(Card);

for (const fx of card.effects) {
  fx.effectType; // "glow"
  fx.progress; // 0–1, normalized for this frame
  fx.params; // resolved params
}

Was this page helpful?