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

Effects

An effect is a timed record on an actor that the actor's backend paints, with typed params a scene can tween.

An effect changes how an actor looks without changing the actor. Applying one records it on the timeline next to the actor, with its own params, and the backend that paints the actor reads the record and draws the treatment. The actor’s own state is never touched, so removing the effect leaves nothing to undo.

The card’s renderer draws a plain panel. The glow comes from the DOM backend’s style stage, which reads card.effects on each frame and adds a CSS filter around the card. play fades the glow in, a tween on spread widens it, and detach ends it.

Define

defineEffect(kind, { name, params, ...contribution }) declares an effect once. The kind comes from the backend package and fixes three things: which backend paints the effect, which stage paints it, and what the contribution is. Params are signals, and the contribution is typed from them, so no separate params type exists to keep in sync.

import { const describeEffect: (source: AnyEffectDefinition | EffectBinding) => EffectDescription
What an editor needs to label and offer an effect: its kind's name, what it applies to, and per param reflection's `kind`, the declared `type`, `range`, `options` and `unit`, and the JSON `default`. `params: {}` for an effect that declares none. A binding must carry its definition; resolve a bare wire id through the render runtime (`runtime.describeEffect(id)`).
describeEffect
} from "@motionactor/core";
import {
const glow: EffectDefinition<"dom", "glow", {
    readonly color: TypedFieldSignal<Color, string | Color>;
    readonly spread: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly color: TypedFieldSignal<Color, string | Color>;
    readonly spread: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}>>, false, "actor">
glow
} from "./effect-glow.scene";
const glow: EffectDefinition<"dom", "glow", {
    readonly color: TypedFieldSignal<Color, string | Color>;
    readonly spread: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly color: TypedFieldSignal<Color, string | Color>;
    readonly spread: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}>>, false, "actor">
glow
.EffectDefinitionFields<"dom", "glow", { readonly color: TypedFieldSignal<Color, string | Color>; readonly spread: ActorFieldSignal<number>; readonly progress: ActorFieldSignal<...>; }, EffectKind<...>, DomStyleRender<...>>.name: "glow"
Unique among its backend's effects: with the backend's name, the wire identity.
name
; // "glow"
function describeEffect(source: AnyEffectDefinition | EffectBinding): EffectDescription
What an editor needs to label and offer an effect: its kind's name, what it applies to, and per param reflection's `kind`, the declared `type`, `range`, `options` and `unit`, and the JSON `default`. `params: {}` for an effect that declares none. A binding must carry its definition; resolve a bare wire id through the render runtime (`runtime.describeEffect(id)`).
describeEffect
(
const glow: EffectDefinition<"dom", "glow", {
    readonly color: TypedFieldSignal<Color, string | Color>;
    readonly spread: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly color: TypedFieldSignal<Color, string | Color>;
    readonly spread: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}>>, false, "actor">
glow
).EffectDescription.params: Record<string, EffectParamDescription>params.EffectParamDescription
What an editor needs to offer one param: reflection's kind and type facts, and the JSON default.
spread
;
// { kind: "number", default: 16 }

describeEffect is what an editor reads to build a control for each param: its kind, its declared type, its range and its default.

Kind Import Contribution Painted by
domStyle @motionactor/dom style(params, { box }) → { filter, translateX, rotate, scale, opacity, clipPath, overlays, … } The DOM style stage, inline
glShader @motionactor/dom A WebGL shader with uniforms(params, frame) The DOM canvas stage, offscreen
canvasPaint @motionactor/dom paint(context2d, ctx) The DOM canvas stage, offscreen
pixiFilter @motionactor/pixi A Pixi filter class and update(filter, params) The Pixi filter stage
threePass @motionactor/three A postprocessing pass and update(pass, params) The Three.js host’s composer
threeMaterial @motionactor/three material: { apply, update, remove } The Three.js host

Several domStyle effects on one actor fold together: filters append, translations and rotations add.

Overlays

A domStyle effect draws over its actor with overlays. They’re data built with overlay.*, so the same effect paints under React, Vue and Svelte:

import { 
function defineEffect<const TName extends string, const P extends EffectParamDeclarations, TKind extends AnyEffectKind, TTarget = unknown, const TTime extends boolean = false, TNative = unknown, const TApplies extends EffectApplication | undefined = undefined>(kind: TKind, spec: EffectSpec<TName, P, TTarget, TTime> & {
    readonly params: NoPlaybackParams;
    readonly applies?: TApplies;
} & EffectNativeSite<TNative> & EffectKindSpec<EffectKindTypesOf<TKind>, EffectRenderParams<P, TTime>, TTarget, TNative>): EffectDefinition<EffectKindBackendOf<TKind>, TName, P, TKind, TTarget, EffectKindRuntime<EffectKindTypesOf<TKind>, EffectRenderParams<P, TTime>, TTarget, TNative>, TTime, EffectDefinitionApplies<TApplies, TTarget, TKind>>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
} from "@motionactor/core";
import {
const domStyle: EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that styles its actor's element with CSS: export const blur = defineEffect(domStyle, { name: "blur", params: { strength: signal(8), progress: signal.of(unitInterval, 0) }, style: ({ strength, progress }) => ({ filter: `blur(${strength * progress}px)` }), }); Framework-free: each binding's DOM backend pairs it with a stage that paints `domStyleLayers` in its own elements.
domStyle
,
const overlay: {
    div: (props?: Readonly<{
        style?: StyleRecord;
        data?: Readonly<Record<string, string>>;
        mount?: OverlayMount;
    }> | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    svg: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    g: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    rect: (props: OverlaySvgProps) => OverlayElement;
    circle: (props: OverlaySvgProps) => OverlayElement;
    line: (props: OverlaySvgProps) => OverlayElement;
    path: (props: OverlaySvgProps) => OverlayElement;
}
The overlay builders: overlays: [overlay.svg({ attrs: { viewBox } }, [overlay.rect({ attrs: { strokeWidth: 2 } })])]
overlay
} from "@motionactor/dom";
export const
const ring: EffectDefinition<"dom", "ring", {
    readonly width: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly width: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}>>, false, "actor">
ring
=
defineEffect<"ring", {
    readonly width: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, false, unknown, undefined>(kind: EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, spec: EffectSpec<"ring", {
    readonly width: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}, unknown, false> & {
    ...;
} & DomStyleSpec<...>): EffectDefinition<...>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
(
const domStyle: EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that styles its actor's element with CSS: export const blur = defineEffect(domStyle, { name: "blur", params: { strength: signal(8), progress: signal.of(unitInterval, 0) }, style: ({ strength, progress }) => ({ filter: `blur(${strength * progress}px)` }), }); Framework-free: each binding's DOM backend pairs it with a stage that paints `domStyleLayers` in its own elements.
domStyle
, {
EffectSpec<"ring", { readonly width: ActorFieldSignal<number>; readonly progress: ActorFieldSignal<number>; }, unknown, false>.name: "ring"
Unique among the backend's effects. With the backend's name, the effect's wire identity.
name
: "ring",
params: {
    readonly width: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
} & NoPlaybackParams
Every param the kind's contribution reads, declared once as `signal(default)` or `signal.of(type, default)`.
params
: { width: ActorFieldSignal<number>width: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(2), progress: ActorFieldSignal<number>progress: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(0) },
style: DomStyleRender<EffectParamValues<{
    readonly width: ActorFieldSignal<number>;
    readonly progress: ActorFieldSignal<number>;
}>>
The effect's contribution to its actor's element, pure in its params.
style
: ({ width: numberwidth, progress: numberprogress }, { box: EffectStageBox | undefinedbox }) => ({
overlays?: readonly OverlayElement[] | undefined
Painted over the actor's content, inside its box, by whichever binding paints the actor.
overlays
: [
const overlay: {
    div: (props?: Readonly<{
        style?: StyleRecord;
        data?: Readonly<Record<string, string>>;
        mount?: OverlayMount;
    }> | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    svg: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    g: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    rect: (props: OverlaySvgProps) => OverlayElement;
    circle: (props: OverlaySvgProps) => OverlayElement;
    line: (props: OverlaySvgProps) => OverlayElement;
    path: (props: OverlaySvgProps) => OverlayElement;
}
The overlay builders: overlays: [overlay.svg({ attrs: { viewBox } }, [overlay.rect({ attrs: { strokeWidth: 2 } })])]
overlay
.svg: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElementsvg(
{ style?: StyleRecord | undefinedstyle: { StandardLonghandProperties<string | number, string & {}>.position?: Property.Position | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015. **Syntax**: `static | relative | absolute | sticky | fixed` **Initial value**: `static` | Chrome | Firefox | Safari | Edge | IE | | :----: | :-----: | :----: | :----: | :---: | | **1** | **1** | **1** | **12** | **4** |
@seehttps://developer.mozilla.org/docs/Web/CSS/Reference/Properties/position
position
: "absolute", StandardShorthandProperties<string | number, string & {}>.inset?: Property.Inset<string | number> | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2021. **Syntax**: `<'top'>{1,4}` | Chrome | Firefox | Safari | Edge | IE | | :----: | :-----: | :------: | :----: | :-: | | **87** | **66** | **14.1** | **87** | No |
@seehttps://developer.mozilla.org/docs/Web/CSS/Reference/Properties/inset
inset
: 0 },
attrs?: Readonly<{
    viewBox?: string;
    preserveAspectRatio?: string;
    transform?: string;
    x?: number;
    y?: number;
    width?: number;
    height?: number;
    rx?: number;
    ry?: number;
    cx?: number;
    cy?: number;
    r?: number;
    x1?: number;
    y1?: number;
    x2?: number;
    y2?: number;
    d?: string;
    fill?: string;
    opacity?: number;
    stroke?: string;
    strokeWidth?: number;
    strokeLinecap?: "butt" | "round" | "square";
    strokeLinejoin?: "miter" | "round" | "bevel";
    strokeDasharray?: string;
    strokeDashoffset?: number;
    strokeOpacity?: number;
}> | undefined
attrs
: { viewBox?: string | undefinedviewBox: `0 0 ${box: EffectStageBox | undefinedbox?.width: number | undefinedwidth ?? 0} ${box: EffectStageBox | undefinedbox?.height: number | undefinedheight ?? 0}` } },
[
const overlay: {
    div: (props?: Readonly<{
        style?: StyleRecord;
        data?: Readonly<Record<string, string>>;
        mount?: OverlayMount;
    }> | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    svg: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    g: (props?: OverlaySvgProps | undefined, children?: readonly OverlayElement[]) => OverlayElement;
    rect: (props: OverlaySvgProps) => OverlayElement;
    circle: (props: OverlaySvgProps) => OverlayElement;
    line: (props: OverlaySvgProps) => OverlayElement;
    path: (props: OverlaySvgProps) => OverlayElement;
}
The overlay builders: overlays: [overlay.svg({ attrs: { viewBox } }, [overlay.rect({ attrs: { strokeWidth: 2 } })])]
overlay
.rect: (props: OverlaySvgProps) => OverlayElementrect({
attrs?: Readonly<{
    viewBox?: string;
    preserveAspectRatio?: string;
    transform?: string;
    x?: number;
    y?: number;
    width?: number;
    height?: number;
    rx?: number;
    ry?: number;
    cx?: number;
    cy?: number;
    r?: number;
    x1?: number;
    y1?: number;
    x2?: number;
    y2?: number;
    d?: string;
    fill?: string;
    opacity?: number;
    stroke?: string;
    strokeWidth?: number;
    strokeLinecap?: "butt" | "round" | "square";
    strokeLinejoin?: "miter" | "round" | "bevel";
    strokeDasharray?: string;
    strokeDashoffset?: number;
    strokeOpacity?: number;
}> | undefined
attrs
: { x?: number | undefinedx: 1, y?: number | undefinedy: 1, width?: number | undefinedwidth: (box: EffectStageBox | undefinedbox?.width: number | undefinedwidth ?? 2) - 2, height?: number | undefinedheight: (box: EffectStageBox | undefinedbox?.height: number | undefinedheight ?? 2) - 2, fill?: string | undefinedfill: "none", stroke?: string | undefinedstroke: "#f86e42", strokeWidth?: number | undefinedstrokeWidth: width: numberwidth, opacity?: number | undefinedopacity: progress: numberprogress } })],
), ], }), });

A painter that draws imperatively, such as a WebGL shader, attaches to an overlay element with overlayMount(id, props, painter). The painter starts once and receives each later frame’s props while the id holds. It is disposed when the element goes. Declare the painter at module level: a mount with a different painter restarts.

Install

The scene above installs glow beside its definition, in two statements. The ActorEffects entry, keyed "<backend>:<name>", types the name. registerEffects([glow]) makes it callable. Importing the module installs the effect. A type-only import runs no registration, so the call throws, naming the effect.

  • A later registration at the same backend and name replaces the earlier one. A hot reload or a second module loader re-registers without error.
  • Two ActorEffects entries for one key with different definitions are a type error.
  • registerEffects throws for an actor effect named for.
  • An effect that cannot apply to an actor, a layer effect or one that needs target data, registers too. It resolves by wire id but stays off fx; apply it to a target with ctx.apply.

A module imported only for its registration is a side effect. Under "sideEffects": false a bundler drops it, and the call throws. A package that ships effect modules, or wraps copied ones, lists them and the entries that import them:

{ "sideEffects": ["./src/**/effect.ts", "./dist/index.mjs", "./dist/index.cjs"] }

Call by name

fx is a getter on Actor, so every actor, child field and slot occupant has the same menu: each installed effect whose name one backend defines. Params come first, playback second.

const glowing = card.fx.glow(); // handle, default params
const wide = card.fx.glow({ spread: 24 }); // handle
yield* card.fx.glow({ duration: 0.6 }); // played, default params
yield* card.fx.glow({ spread: 24 }, { duration: 0.6, easing: Easing.outCubic }); // played
  • Without playback, the effect applies at the cursor and the call returns its handle.
  • With playback, it applies when the generator first runs, then plays to its end and holds there. Under ctx.delay that is when the delay ends.
  • One object with duration is playback, so no effect param may be named duration or easing.
  • An effect with a required param has no () or (playback) form.

On the handle:

  • handle.play({ duration, easing? }) tweens a param named progress from 0 to 1. An effect without a progress param just waits out the duration.
  • Every param is a signal, so handle.spread.tween(48, 0.5) and handle.color.set(...) work as they do on an actor. A param given as a derived has no signal on the handle.
  • handle.detach() ends the record at the cursor. Frames before it still show the effect.

One name on two backends

A name two installed backends define leaves the menu: card.fx.glow is a type error, and at runtime the call throws, naming both. fx.for(backend) selects one:

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
,
function defineEffect<const TName extends string, const P extends EffectParamDeclarations, TKind extends AnyEffectKind, TTarget = unknown, const TTime extends boolean = false, TNative = unknown, const TApplies extends EffectApplication | undefined = undefined>(kind: TKind, spec: EffectSpec<TName, P, TTarget, TTime> & {
    readonly params: NoPlaybackParams;
    readonly applies?: TApplies;
} & EffectNativeSite<TNative> & EffectKindSpec<EffectKindTypesOf<TKind>, EffectRenderParams<P, TTime>, TTarget, TNative>): EffectDefinition<EffectKindBackendOf<TKind>, TName, P, TKind, TTarget, EffectKindRuntime<EffectKindTypesOf<TKind>, EffectRenderParams<P, TTime>, TTarget, TNative>, TTime, EffectDefinitionApplies<TApplies, TTarget, TKind>>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor, function registerEffects(definitions: readonly AnyEffectDefinition[]): void
Install effects by wire id. Call it in the effect's own module, after its `defineEffect(...)` (and beside its `ActorEffects` augmentation when it applies to an actor), so importing the effect installs it. Every effect registers. One that can apply to an actor is also on every actor's `fx`; one that cannot (a layer application, or one needing target data) stays off `fx` and applies with `ctx.apply` on a target. A later registration at a wire id replaces the earlier one. An actor effect named `for` throws: `fx.for` is taken.
registerEffects
,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
} from "@motionactor/core";
import { const dom: BackendRef<"dom">
The DOM backend's identity, at its own framework-free entry (`@motionactor/renderers/dom-ref`). It lives here and not in `@motionactor/dom`: DOM renderers are written in packages the DOM host depends on (`@motionactor/primitives`, `@motionactor/media`), and the contract already treats DOM specially (only DOM renderers bind `refs`). It is not on the floor's root, so no binding entry re-exports a `dom` that is not the backend: `@motionactor/dom` re-exports it as the ref, and its `/react`, `/vue` and `/svelte` entries export their backends as `dom`.
dom
,
const domStyle: EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that styles its actor's element with CSS: export const blur = defineEffect(domStyle, { name: "blur", params: { strength: signal(8), progress: signal.of(unitInterval, 0) }, style: ({ strength, progress }) => ({ filter: `blur(${strength * progress}px)` }), }); Framework-free: each binding's DOM backend pairs it with a stage that paints `domStyleLayers` in its own elements.
domStyle
} from "@motionactor/dom";
import { const pixi: BackendRef<"pixi">
The Pixi backend's identity, and the only place its name is spelled. This entry pulls in no React, no Pixi and no host, so a scene file, an effect or a renderer package can name the backend without mounting it.
pixi
,
const pixiFilter: EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that is one Pixi filter on its actor (or a registered target container). The short form is a pixi-filters class whose options and props are the params, by name: export const glow = defineEffect(pixiFilter, { name: "glow", params: { distance: signal(10), outerStrength: signal(4), color: signal<ColorSource>(0xffffff) }, filter: GlowFilter, }); The full form, `filter: { create, update, destroy? }`, translates params the filter does not take by name.
pixiFilter
} from "@motionactor/pixi";
import { class BlurFilter
The BlurFilter applies a Gaussian blur to an object. The strength of the blur can be set for the x-axis and y-axis separately.
@example```ts import { BlurFilter } from 'pixi.js'; // Create with default settings const filter = new BlurFilter(); // Create with custom settings const filter = new BlurFilter({ strength: 8, // Overall blur strength quality: 4, // Blur quality (higher = better but slower) kernelSize: 5 // Size of blur kernel matrix }); // Apply to a display object sprite.filters = [filter]; // Update properties filter.strength = 10; // Set both X and Y blur filter.strengthX = 5; // Set only horizontal blur filter.strengthY = 15; // Set only vertical blur filter.quality = 2; // Adjust quality // Enable edge pixel clamping filter.repeatEdgePixels = true; ```@remarks- Higher quality values produce better blur but impact performance - Strength controls blur intensity independently for X and Y - Can be optimized using quality and kernelSize settings - Supports edge pixel clamping for special effects@see{@link BlurFilterPass} For single-direction blur@see{@link FilterOptions} For base filter options@categoryfilters@standard@noInheritDoc
BlurFilter
} from "pixi.js";
const
const domGlow: EffectDefinition<"dom", "glow", {
    readonly spread: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly spread: ActorFieldSignal<number>;
}>>, false, "actor">
domGlow
=
defineEffect<"glow", {
    readonly spread: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, false, unknown, undefined>(kind: EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, spec: EffectSpec<"glow", {
    readonly spread: ActorFieldSignal<number>;
}, unknown, false> & {
    readonly params: NoPlaybackParams;
    readonly applies?: undefined;
} & DomStyleSpec<EffectParamValues<{
    readonly spread: ActorFieldSignal<number>;
}>>): EffectDefinition<...>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
(
const domStyle: EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that styles its actor's element with CSS: export const blur = defineEffect(domStyle, { name: "blur", params: { strength: signal(8), progress: signal.of(unitInterval, 0) }, style: ({ strength, progress }) => ({ filter: `blur(${strength * progress}px)` }), }); Framework-free: each binding's DOM backend pairs it with a stage that paints `domStyleLayers` in its own elements.
domStyle
, {
EffectSpec<"glow", { readonly spread: ActorFieldSignal<number>; }, unknown, false>.name: "glow"
Unique among the backend's effects. With the backend's name, the effect's wire identity.
name
: "glow",
params: {
    readonly spread: ActorFieldSignal<number>;
} & NoPlaybackParams
Every param the kind's contribution reads, declared once as `signal(default)` or `signal.of(type, default)`.
params
: { spread: ActorFieldSignal<number>spread: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(16) },
style: DomStyleRender<EffectParamValues<{
    readonly spread: ActorFieldSignal<number>;
}>>
The effect's contribution to its actor's element, pure in its params.
style
: ({ spread: numberspread }) => ({ filter?: string | readonly string[] | undefined
CSS filter functions, appended after the folded hue/contrast/brightness.
filter
: `drop-shadow(0 0 ${spread: numberspread}px #f86e42)` }),
}); const
const pixiGlow: EffectDefinition<"pixi", "glow", {
    readonly strength: ActorFieldSignal<number>;
}, EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, unknown, PixiFilterLifecycle<EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>>, false, "actor">
pixiGlow
=
defineEffect<"glow", {
    readonly strength: ActorFieldSignal<number>;
}, EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, unknown, false, BlurFilter, undefined>(kind: EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, spec: EffectSpec<"glow", {
    readonly strength: ActorFieldSignal<number>;
}, unknown, false> & ({
    readonly params: NoPlaybackParams;
    readonly applies?: undefined;
} & PixiFilterSpec<...>)): EffectDefinition<...>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
(
const pixiFilter: EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that is one Pixi filter on its actor (or a registered target container). The short form is a pixi-filters class whose options and props are the params, by name: export const glow = defineEffect(pixiFilter, { name: "glow", params: { distance: signal(10), outerStrength: signal(4), color: signal<ColorSource>(0xffffff) }, filter: GlowFilter, }); The full form, `filter: { create, update, destroy? }`, translates params the filter does not take by name.
pixiFilter
, {
EffectSpec<"glow", { readonly strength: ActorFieldSignal<number>; }, unknown, false>.name: "glow"
Unique among the backend's effects. With the backend's name, the effect's wire identity.
name
: "glow",
params: {
    readonly strength: ActorFieldSignal<number>;
} & NoPlaybackParams
Every param the kind's contribution reads, declared once as `signal(default)` or `signal.of(type, default)`.
params
: { strength: ActorFieldSignal<number>strength: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(8) },
filter: PixiFilterClass<EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>, Filter & BlurFilter>
filter
: class BlurFilter
The BlurFilter applies a Gaussian blur to an object. The strength of the blur can be set for the x-axis and y-axis separately.
@example```ts import { BlurFilter } from 'pixi.js'; // Create with default settings const filter = new BlurFilter(); // Create with custom settings const filter = new BlurFilter({ strength: 8, // Overall blur strength quality: 4, // Blur quality (higher = better but slower) kernelSize: 5 // Size of blur kernel matrix }); // Apply to a display object sprite.filters = [filter]; // Update properties filter.strength = 10; // Set both X and Y blur filter.strengthX = 5; // Set only horizontal blur filter.strengthY = 15; // Set only vertical blur filter.quality = 2; // Adjust quality // Enable edge pixel clamping filter.repeatEdgePixels = true; ```@remarks- Higher quality values produce better blur but impact performance - Strength controls blur intensity independently for X and Y - Can be optimized using quality and kernelSize settings - Supports edge pixel clamping for special effects@see{@link BlurFilterPass} For single-direction blur@see{@link FilterOptions} For base filter options@categoryfilters@standard@noInheritDoc
BlurFilter
,
update: (filter: Filter & BlurFilter, params: EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>) => void
update
: (filter: Filter & BlurFilterfilter, { strength: numberstrength }) => {
filter: Filter & BlurFilterfilter.BlurFilter.strength: number
Sets the strength of both the blurX and blurY properties simultaneously. Controls the overall intensity of the Gaussian blur effect.
@example```ts // Set equal blur strength for both axes filter.strength = 8; // Will throw error if X and Y are different filter.strengthX = 4; filter.strengthY = 8; filter.strength; // Error: BlurFilter's strengthX and strengthY are different ```@default8@throws{Error} If strengthX and strengthY are different values
strength
= strength: numberstrength;
}, }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof
const domGlow: EffectDefinition<"dom", "glow", {
    readonly spread: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly spread: ActorFieldSignal<number>;
}>>, false, "actor">
domGlow
;
"pixi:glow": typeof
const pixiGlow: EffectDefinition<"pixi", "glow", {
    readonly strength: ActorFieldSignal<number>;
}, EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, unknown, PixiFilterLifecycle<EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>>, false, "actor">
pixiGlow
;
} } function registerEffects(definitions: readonly AnyEffectDefinition[]): void
Install effects by wire id. Call it in the effect's own module, after its `defineEffect(...)` (and beside its `ActorEffects` augmentation when it applies to an actor), so importing the effect installs it. Every effect registers. One that can apply to an actor is also on every actor's `fx`; one that cannot (a layer application, or one needing target data) stays off `fx` and applies with `ctx.apply` on a target. A later registration at a wire id replaces the earlier one. An actor effect named `for` throws: `fx.for` is taken.
registerEffects
([
const domGlow: EffectDefinition<"dom", "glow", {
    readonly spread: ActorFieldSignal<number>;
}, EffectKind<"dom", "style", DomStyleTypes, {
    readonly kind: "inline";
}, "actor">, unknown, DomStyleRender<EffectParamValues<{
    readonly spread: ActorFieldSignal<number>;
}>>, false, "actor">
domGlow
,
const pixiGlow: EffectDefinition<"pixi", "glow", {
    readonly strength: ActorFieldSignal<number>;
}, EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, unknown, PixiFilterLifecycle<EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>>, false, "actor">
pixiGlow
]);
export const const compiled: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>compiled = createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const card: LayoutActor<ActorEventMap>card = ctx: SceneContextctx.
CompileContext.spawn<LayoutActor<ActorEventMap>>(definition: ActorClass<LayoutActor<ActorEventMap>>, overrides?: {
    measuredBox?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 20 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): LayoutActor<...> (+1 overload)
spawn
(class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor);
// @ts-expect-error: dom and pixi both define glow const card: LayoutActor<ActorEventMap>card.Actor<ActorEventMap>.fx: ActorFx
Installed effects by name: `yield* this.fx.burnDissolve({ noiseScale: 7 }, { duration: 0.6 })` plays one; `this.fx.glow({ spread: 24 })` keeps its handle. A name two installed backends define is selected with `this.fx.for(dom).glow(...)`. Applies only once spawned; the layer's backend is checked when bound.
fx
.glow();
yield* const card: LayoutActor<ActorEventMap>card.Actor<ActorEventMap>.fx: ActorFx
Installed effects by name: `yield* this.fx.burnDissolve({ noiseScale: 7 }, { duration: 0.6 })` plays one; `this.fx.glow({ spread: 24 })` keeps its handle. A name two installed backends define is selected with `this.fx.for(dom).glow(...)`. Applies only once spawned; the layer's backend is checked when bound.
fx
.for<"dom">(backend: BackendRef<"dom">): FxFor<"dom">
The effects `backend` defines, clashing names included: `card.fx.for(dom).glow(...)`.
for
(const dom: BackendRef<"dom">
The DOM backend's identity, at its own framework-free entry (`@motionactor/renderers/dom-ref`). It lives here and not in `@motionactor/dom`: DOM renderers are written in packages the DOM host depends on (`@motionactor/primitives`, `@motionactor/media`), and the contract already treats DOM specially (only DOM renderers bind `refs`). It is not on the floor's root, so no binding entry re-exports a `dom` that is not the backend: `@motionactor/dom` re-exports it as the ref, and its `/react`, `/vue` and `/svelte` entries export their backends as `dom`.
dom
).
glow: (params: EffectFactoryInput<{
    readonly spread: ActorFieldSignal<number>;
}, false>, playback: EffectPlaybackOptions) => Generator<Frame, void, unknown> (+3 overloads)
Apply when iterated and play it once.
glow
({ spread?: EffectParamInitial<ActorFieldSignal<number>> | undefinedspread: 24 }, { EffectPlaybackOptions.duration: numberduration: 0.4 });
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

Checked when it paints

TypeScript does not know which backend paints an actor: layer(dom).spawn(Card) has the same menu as any actor. In every build, an effect for another backend throws before it paints, naming the effect, the actor and both backends. On an actor in a declared layer, the call throws as the scene compiles. Selecting with for(backend) does not skip the check.

Effect "glow" on card "card" is for backend "pixi", but the default layer is painted by "dom"

An offscreen effect (glShader, canvasPaint) applies to a child a view binds only when its target contract clips, clips: true, so it paints inside the child’s rect and nowhere else. The binding refuses any other with a BoundElementError, in every build, including one that starts later. Bindings shows one.

Explicit targets

ctx.apply(target, effect(params?)) is the primitive under fx, and returns the same handle. A scene uses it for what is not an actor, such as a target factory’s text range or mask, and for a definition picked at runtime, such as one from an editor palette:

const highlight = ctx.apply(article.targets.range("lead"), textHighlight());
yield* highlight.play({ duration: 0.4 });

What a frame reports

actor.effects lists the effects active at the frame, in the order they were applied:

import { 
const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    card: Card;
}>
scene
} from "./effect-glow.scene";
using
using compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
=
const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    card: Card;
}>
scene
.
SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build
({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1280, SceneViewport.height: numberheight: 720 }, fps: numberfps: 30 });
const [const fx: ActiveEffectfx] =
using compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    card: Card;
}>
at
(45).
FrameState<{ ...; }>.scene: {
    card: ResolvedActorOfInstance<Card> | undefined;
}
scene
.card: ResolvedActorOfInstance<Card> | undefinedcard!.effects: readonly ActiveEffect[]effects;
const fx: ActiveEffectfx?.ActiveEffect.effectType: stringeffectType; // "glow" const fx: ActiveEffectfx?.ActiveEffect.params: Record<string, unknown>params.unknownspread; // 48 const fx: ActiveEffectfx?.ActiveEffect.params: Record<string, unknown>params.unknownprogress; // 1 const fx: ActiveEffectfx?.ActiveEffect.progress: numberprogress; // 0.857, how far through the application frame 45 is
using compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    card: Card;
}>
at
(60).
FrameState<{ ...; }>.scene: {
    card: ResolvedActorOfInstance<Card> | undefined;
}
scene
.card: ResolvedActorOfInstance<Card> | undefinedcard?.effects: readonly ActiveEffect[] | undefinedeffects; // [], detached at frame 51

fx.progress measures the application itself, from the call to detach, and is 1 for an effect that is never detached. The progress param is the one play drives, so a style can fade in once and hold.

Pixi and Three.js

The same pattern works on the canvas backends. A kind whose contribution names a class infers the type of the native object it creates, so update gets a typed filter or pass:

import { 
function defineEffect<const TName extends string, const P extends EffectParamDeclarations, TKind extends AnyEffectKind, TTarget = unknown, const TTime extends boolean = false, TNative = unknown, const TApplies extends EffectApplication | undefined = undefined>(kind: TKind, spec: EffectSpec<TName, P, TTarget, TTime> & {
    readonly params: NoPlaybackParams;
    readonly applies?: TApplies;
} & EffectNativeSite<TNative> & EffectKindSpec<EffectKindTypesOf<TKind>, EffectRenderParams<P, TTime>, TTarget, TNative>): EffectDefinition<EffectKindBackendOf<TKind>, TName, P, TKind, TTarget, EffectKindRuntime<EffectKindTypesOf<TKind>, EffectRenderParams<P, TTime>, TTarget, TNative>, TTime, EffectDefinitionApplies<TApplies, TTarget, TKind>>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
} from "@motionactor/core";
import {
const pixiFilter: EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that is one Pixi filter on its actor (or a registered target container). The short form is a pixi-filters class whose options and props are the params, by name: export const glow = defineEffect(pixiFilter, { name: "glow", params: { distance: signal(10), outerStrength: signal(4), color: signal<ColorSource>(0xffffff) }, filter: GlowFilter, }); The full form, `filter: { create, update, destroy? }`, translates params the filter does not take by name.
pixiFilter
} from "@motionactor/pixi";
import {
const threePass: EffectKind<"three", "pass", ThreePassTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that is a postprocessing `Effect` in the layer's composer. No stage: the three host (`ThreeSceneEffectsWrapper`) collects these into its `EffectComposer`. export const noise = defineEffect(threePass, { name: "noise", params: { premultiply: signal(false), opacity: signal(0.4) }, pass: NoiseEffect, options: { blendFunction: BlendFunction.SCREEN }, update: (effect, { premultiply, opacity }) => { … }, // effect: NoiseEffect }); or, for a pass that needs the scene or selection to build, `pass: { scope, create, update, destroy }`.
threePass
} from "@motionactor/three";
import { class BlurFilter
The BlurFilter applies a Gaussian blur to an object. The strength of the blur can be set for the x-axis and y-axis separately.
@example```ts import { BlurFilter } from 'pixi.js'; // Create with default settings const filter = new BlurFilter(); // Create with custom settings const filter = new BlurFilter({ strength: 8, // Overall blur strength quality: 4, // Blur quality (higher = better but slower) kernelSize: 5 // Size of blur kernel matrix }); // Apply to a display object sprite.filters = [filter]; // Update properties filter.strength = 10; // Set both X and Y blur filter.strengthX = 5; // Set only horizontal blur filter.strengthY = 15; // Set only vertical blur filter.quality = 2; // Adjust quality // Enable edge pixel clamping filter.repeatEdgePixels = true; ```@remarks- Higher quality values produce better blur but impact performance - Strength controls blur intensity independently for X and Y - Can be optimized using quality and kernelSize settings - Supports edge pixel clamping for special effects@see{@link BlurFilterPass} For single-direction blur@see{@link FilterOptions} For base filter options@categoryfilters@standard@noInheritDoc
BlurFilter
} from "pixi.js";
import { class BloomEffect
A bloom effect.
BloomEffect
} from "postprocessing";
export const
const blur: EffectDefinition<"pixi", "blur", {
    readonly strength: ActorFieldSignal<number>;
}, EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, unknown, PixiFilterLifecycle<EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>>, false, "actor">
blur
=
defineEffect<"blur", {
    readonly strength: ActorFieldSignal<number>;
}, EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, unknown, false, BlurFilter, undefined>(kind: EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">, spec: EffectSpec<"blur", {
    readonly strength: ActorFieldSignal<number>;
}, unknown, false> & ({
    readonly params: NoPlaybackParams;
    readonly applies?: undefined;
} & PixiFilterSpec<...>)): EffectDefinition<...>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
(
const pixiFilter: EffectKind<"pixi", "filter", PixiFilterTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that is one Pixi filter on its actor (or a registered target container). The short form is a pixi-filters class whose options and props are the params, by name: export const glow = defineEffect(pixiFilter, { name: "glow", params: { distance: signal(10), outerStrength: signal(4), color: signal<ColorSource>(0xffffff) }, filter: GlowFilter, }); The full form, `filter: { create, update, destroy? }`, translates params the filter does not take by name.
pixiFilter
, {
EffectSpec<"blur", { readonly strength: ActorFieldSignal<number>; }, unknown, false>.name: "blur"
Unique among the backend's effects. With the backend's name, the effect's wire identity.
name
: "blur",
params: {
    readonly strength: ActorFieldSignal<number>;
} & NoPlaybackParams
Every param the kind's contribution reads, declared once as `signal(default)` or `signal.of(type, default)`.
params
: { strength: ActorFieldSignal<number>strength: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(8) },
filter: PixiFilterClass<EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>, Filter & BlurFilter>
filter
: class BlurFilter
The BlurFilter applies a Gaussian blur to an object. The strength of the blur can be set for the x-axis and y-axis separately.
@example```ts import { BlurFilter } from 'pixi.js'; // Create with default settings const filter = new BlurFilter(); // Create with custom settings const filter = new BlurFilter({ strength: 8, // Overall blur strength quality: 4, // Blur quality (higher = better but slower) kernelSize: 5 // Size of blur kernel matrix }); // Apply to a display object sprite.filters = [filter]; // Update properties filter.strength = 10; // Set both X and Y blur filter.strengthX = 5; // Set only horizontal blur filter.strengthY = 15; // Set only vertical blur filter.quality = 2; // Adjust quality // Enable edge pixel clamping filter.repeatEdgePixels = true; ```@remarks- Higher quality values produce better blur but impact performance - Strength controls blur intensity independently for X and Y - Can be optimized using quality and kernelSize settings - Supports edge pixel clamping for special effects@see{@link BlurFilterPass} For single-direction blur@see{@link FilterOptions} For base filter options@categoryfilters@standard@noInheritDoc
BlurFilter
,
update: (filter: Filter & BlurFilter, params: EffectParamValues<{
    readonly strength: ActorFieldSignal<number>;
}>) => void
update
: (filter: Filter & BlurFilterfilter, { strength: numberstrength }) => {
filter: Filter & BlurFilterfilter.BlurFilter.strength: number
Sets the strength of both the blurX and blurY properties simultaneously. Controls the overall intensity of the Gaussian blur effect.
@example```ts // Set equal blur strength for both axes filter.strength = 8; // Will throw error if X and Y are different filter.strengthX = 4; filter.strengthY = 8; filter.strength; // Error: BlurFilter's strengthX and strengthY are different ```@default8@throws{Error} If strengthX and strengthY are different values
strength
= strength: numberstrength;
}, }); export const
const bloom: EffectDefinition<"three", "bloom", {
    readonly intensity: ActorFieldSignal<number>;
}, EffectKind<"three", "pass", ThreePassTypes, {
    readonly kind: "inline";
}, "actor">, unknown, ThreePassLifecycle<EffectParamValues<{
    readonly intensity: ActorFieldSignal<number>;
}>, Effect>, false, "actor">
bloom
=
defineEffect<"bloom", {
    readonly intensity: ActorFieldSignal<number>;
}, EffectKind<"three", "pass", ThreePassTypes, {
    readonly kind: "inline";
}, "actor">, unknown, false, BloomEffect, undefined>(kind: EffectKind<"three", "pass", ThreePassTypes, {
    readonly kind: "inline";
}, "actor">, spec: EffectSpec<"bloom", {
    readonly intensity: ActorFieldSignal<number>;
}, unknown, false> & ({
    readonly params: NoPlaybackParams;
    readonly applies?: undefined;
} & ThreePassSpec<...>)): EffectDefinition<...>
Define an effect: one value of one kind, on the kind's backend. The spec is the core fields plus the kind's own, typed from `params`: export const glow = defineEffect(domStyle, { name: "glow", params: { color: signal.of(colorType, color("#22d3ee")), spread: signal(18) }, style: ({ color, spread }) => ({ filter: `drop-shadow(0 0 ${spread}px ${color})` }), }); declare module "@motionactor/core" { interface ActorEffects { "dom:glow": typeof glow } } registerEffects([glow]); card.fx.glow({ spread: 24 });
defineEffect
(
const threePass: EffectKind<"three", "pass", ThreePassTypes, {
    readonly kind: "inline";
}, "actor">
The kind of effect that is a postprocessing `Effect` in the layer's composer. No stage: the three host (`ThreeSceneEffectsWrapper`) collects these into its `EffectComposer`. export const noise = defineEffect(threePass, { name: "noise", params: { premultiply: signal(false), opacity: signal(0.4) }, pass: NoiseEffect, options: { blendFunction: BlendFunction.SCREEN }, update: (effect, { premultiply, opacity }) => { … }, // effect: NoiseEffect }); or, for a pass that needs the scene or selection to build, `pass: { scope, create, update, destroy }`.
threePass
, {
name: "bloom"name: "bloom",
params: {
    intensity: ActorFieldSignal<number>;
}
params
: { intensity: ActorFieldSignal<number>intensity: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(1.5) },
pass: typeof BloomEffectpass: class BloomEffect
A bloom effect.
BloomEffect
,
ThreePassFromClass<EffectParamValues<{ readonly intensity: ActorFieldSignal<number>; }>, Effect & BloomEffect>.update: (effect: Effect & BloomEffect, params: EffectParamValues<{
    readonly intensity: ActorFieldSignal<number>;
}>, context: ThreeComposerEffectContext) => void
`effect` is the instance `pass` builds. A property, not a method, so a wrong annotation is an error.
update
: (pass: Effect & BloomEffectpass, { intensity: numberintensity }) => {
pass: Effect & BloomEffectpass.BloomEffect.intensity: number
The bloom intensity.
@type{Number}
intensity
= intensity: numberintensity;
}, });

Effects by wire id

The record carries the definition of an effect the scene imported. An app that names effects without their definitions, such as an editor palette or a process that receives { backend: "dom", name: "glow" } over IPC, imports their modules. A render runtime resolves every registered effect of its own backends:

import "./glow";
import { function createRenderRuntime<THost = unknown, TNode = unknown>({ backends, renderers, default: requestedDefault, }: CreateRenderRuntimeInput<THost, TNode>): RenderRuntime<THost, TNode>
Compose backends (hosts, primitives, stages) and the renderers painted on them. Each renderer routes to the backend it names, so one list can hold DOM, three and redraw renderers side by side. Effects by wire id resolve from the one effect registry (`registerEffects`), limited to these backends: importing an effect installs it. Backends are keyed by name, so a ref from a second copy of a backend package in one bundle still finds its backend; two backends with one name are an error.
createRenderRuntime
} from "@motionactor/renderers";
import { const dom: StagedBackend<React.FC<ReactBackendHostProps>, "dom", React.ReactNode>
The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`, `createRenderRuntime({ backends: [dom] })`.
dom
} from "@motionactor/dom/react";
export const const runtime: RenderRuntime<React.FC<ReactBackendHostProps>, React.ReactNode>runtime = createRenderRuntime<React.FC<ReactBackendHostProps>, React.ReactNode>({ backends, renderers, default: requestedDefault, }: CreateRenderRuntimeInput<React.FC<ReactBackendHostProps>, React.ReactNode>): RenderRuntime<React.FC<ReactBackendHostProps>, React.ReactNode>
Compose backends (hosts, primitives, stages) and the renderers painted on them. Each renderer routes to the backend it names, so one list can hold DOM, three and redraw renderers side by side. Effects by wire id resolve from the one effect registry (`registerEffects`), limited to these backends: importing an effect installs it. Backends are keyed by name, so a ref from a second copy of a backend package in one bundle still finds its backend; two backends with one name are an error.
createRenderRuntime
({ backends: readonly AnyBackend<React.FC<ReactBackendHostProps>, React.ReactNode>[]backends: [const dom: StagedBackend<React.FC<ReactBackendHostProps>, "dom", React.ReactNode>
The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`, `createRenderRuntime({ backends: [dom] })`.
dom
] });
const runtime: RenderRuntime<React.FC<ReactBackendHostProps>, React.ReactNode>runtime.function allAvailableEffects(): EffectWireId[]
Every registered effect of this runtime's backends, as wire ids, backend by backend.
allAvailableEffects
(); // [{ backend: "dom", name: "glow" }]
const runtime: RenderRuntime<React.FC<ReactBackendHostProps>, React.ReactNode>runtime.function resolveEffectBinding(id: EffectWireId): EffectBinding | undefined
A binding for the effect a wire id names, carrying its definition: what an editor puts on an `ActiveEffect` it mints. `undefined` if none.
resolveEffectBinding
({ EffectWireId<string, string>.backend: stringbackend: "dom", EffectWireId<string, string>.name: stringname: "glow" });
const runtime: RenderRuntime<React.FC<ReactBackendHostProps>, React.ReactNode>runtime.function describeEffect(effect: AnyEffectDefinition | EffectWireId): EffectDescription | undefined
What an editor needs to offer an effect's params, derived from its declarations (`describeEffect` in core). `undefined` for a wire id `resolveEffect` does not resolve.
describeEffect
({ backend: stringbackend: "dom", name: stringname: "glow" });

A runtime without the dom backend resolves none of them.

Was this page helpful?