Your system asks for reduced motion, so VFX-JS draws nothing. Demos on this site show plain HTML.

Class

VFX

vfx-js/src/vfx.ts:78 ↗
import { VFX } from "@vfx-js/core";

The main interface of VFX-JS.

Constructors

new VFX()

new VFX(options?: VFXOpts = {}): VFX

Creates VFX instance and start playing immediately. When the user prefers reduced motion, it renders nothing: elements stay plain HTML and transitions cut to the new content.

Throws

When WebGL is not available in the current environment.

Accessors

canvas

get canvas(): HTMLCanvasElement

The output canvas VFX renders into.

maxTextureSize

get maxTextureSize(): number

time

get time(): number

Current virtual animation time in seconds (the value behind the time uniform).

timeScale

get timeScale(): number

Playback rate of the animation clock.

1 is realtime, 0 pauses (frames still render), and negative values run time backwards. Can be changed at any time.

Note: deltaTime follows this rate, so it goes negative while rewinding. Time-only effects handle that fine; effects that integrate motion over deltaTime (particles, fluid, datamosh) are not designed to run backwards and may behave unexpectedly.

set timeScale(scale: number): void

Methods

add

add(element: HTMLElement, opts: VFXProps): Promise<void>

Register an element to track the position and render visual effects in the area.

addHTML

addHTML(element: HTMLElement, opts: VFXProps): Promise<void>

Register an element using html-in-canvas API. Wraps the element in a <canvas layoutsubtree content="drawable"> and captures via drawElementImage. Falls back to add() if html-in-canvas is not supported.

addTransition

addTransition<T extends Transition | TransitionClass>(
    element: HTMLElement,
    opts?: AddTransitionOpts<T> = {},
): TransitionHandle

Animate an element from its old content to its new content with a shader. The element is wrapped only while a run is in flight, unless keepWrapped is set.

const t = vfx.addTransition(main, { duration: 0.6 });
await t.run(() => renderNextPage());

capture

capture(source: HTMLElement, opts?: VFXCaptureOpts = {}): Promise<VFXCapture>

Capture an element as a texture without drawing it. Any effect of this VFX instance can read the texture. Resolves after the first capture is in the texture.

<img>, <video> and <canvas> are uploaded as they are. Other elements are captured with html-in-canvas when the browser supports it (like VFX.addHTML), or else with one DOM snapshot (like VFX.add). Call update() on the snapshot after the element changes.

const page = await vfx.capture(article, { hide: true });
await vfx.add(minimap, {
    effect: myEffect,
    uniforms: { page: page.texture },
});
// Later:
page.dispose();

The texture works on the effect path (effect) only, and only in this VFX instance.

Rejects when the element already has a capture in this instance, when addHTML or a transition wraps it, when add has it (unless it is an <img>, <video> or <canvas> captured without hide), or when the first capture fails. add rejects a captured element under the same rule. When the user prefers reduced motion, it resolves to a handle that leaves the element alone and samples as transparent.

destroy

destroy(): void

Destroy VFX and stop rendering.

play

play(): void

Start rendering VFX.

remove

remove(element: HTMLElement): void

Remove the element from VFX and stop rendering the shader.

render

render(): void

Render the whole scene once, manually. This is useful when you want to control the rendering timings manually by combining with autoplay: false.

setTime

setTime(time: number): void

Pin the animation clock to an absolute time in seconds.

The next render() uses the value verbatim, so setTime(t); render() produces a deterministic frame — handy for scrubbing a timeline or capturing stable snapshots (e.g. visual regression tests):

const vfx = new VFX({ autoplay: false });
await vfx.add(img, { effect });
vfx.setTime(1.5);
vfx.render();

Note: this drives deltaTime too, so a jump produces one large deltaTime on the next frame. Time-only effects are unaffected, but effects that integrate motion over deltaTime (particles, fluid, datamosh) may jump — pause first (timeScale = 0) when scrubbing them.

stop

stop(): void

Stop rendering VFX. You can restart rendering by calling VFX.play() later.

update

update(element: HTMLElement): Promise<void>

Update the texture for the given element.

For an HTMLImageElement, reloads its currently selected image URL — call this after changing img.src or when responsive image selection changes. Videos refresh automatically (no-op). Otherwise re-snapshots the element's DOM subtree. For html-in-canvas elements, resolves once the new capture is in the texture, or after a short timeout.

Useful for elements whose contents change (input, textarea, or an <img> whose selected source swaps).

updateEffects

updateEffects(
    element: HTMLElement,
    effect: Effect | readonly Effect[],
): Promise<void>

Replace the effect chain on an already-registered effect-path element in-place. Effects whose reference is unchanged keep their init state and GPU resources; only added/removed effects run init / dispose. The element's source texture is preserved.

Useful for live UIs that reorder or toggle effects without paying the cost of vfx.remove + vfx.add (which reloads the source).

init static

init(options?: VFXOpts): VFX | null

Create a VFX instance if WebGL is available, or return null.