VFX-JS adds WebGL effects to the elements of your website.
Attach a shader to an <img>, a <video> or a block of text, or run the
live DOM through a shader when a page changes.
- Element effects. Glitch, bloom, pixelate, halftone and more on images, videos and text. Stack them, or write your own in GLSL.
- Page transitions (new). Glitch, pixel sort, datamosh and more between pages or tabs. VFX-JS captures the live DOM, so it works on your real markup, and the element is plain DOM again when the run ends. See the transitions site.
- Any framework. Plain JS, plus bindings for React, Vue, Svelte and Astro, with router support for Next.js, React Router, Nuxt, Vue Router, SvelteKit and Astro.
npm i @vfx-js/coreimport { VFX } from "@vfx-js/core";
const img = document.querySelector("#img");
const vfx = new VFX();
vfx.add(img, { shader: "glitch", overflow: 100 });Or compose effects from @vfx-js/effects:
import { VFX } from "@vfx-js/core";
import { BloomEffect, PixelateEffect } from "@vfx-js/effects";
const vfx = new VFX();
vfx.add(img, {
effect: [new PixelateEffect({ size: 10 }), new BloomEffect({ intensity: 5 })],
});npm i @vfx-js/core @vfx-js/transitionsimport { VFX } from "@vfx-js/core";
import { Swipe } from "@vfx-js/transitions";
// <div id="panel"> holds one <section> per tab, all but one hidden.
const vfx = new VFX();
const panel = document.querySelector("#panel");
const t = vfx.addTransition(panel, { transition: Swipe, duration: 0.6 });
for (const tab of document.querySelectorAll("[data-panel]")) {
tab.addEventListener("click", () => t.show(tab.dataset.panel));
}For page navigation, wrap your router outlet in <VFXTransition>.
Getting Started has
the setup for each framework.
| Package | Description |
|---|---|
@vfx-js/core |
The VFX class: element effects and transitions |
@vfx-js/effects |
Ready-made effects |
@vfx-js/transitions |
Transition presets |
@vfx-js/react |
React components and hooks, with Next.js and React Router support |
@vfx-js/vue |
Vue components and directives, with Nuxt and Vue Router support |
@vfx-js/svelte |
Svelte components and attachments, with SvelteKit support |
@vfx-js/astro |
Astro components |
react-vfx |
Old name of @vfx-js/react, kept for compatibility |
- Website: usage, shader presets, effects and examples
- Transitions site: setup, presets, custom transitions and a playground
- React docs
- Effect API: write your own effects
- Multipass shaders
- Transition API
- HTML-in-Canvas: effects on any HTML element
VFX-JS needs WebGL. Without it, VFX.init() returns null, so you can
leave the page as plain HTML. When the user prefers reduced motion, VFX-JS renders
nothing, and transitions cut to the new content.
Effects on <img> and <video> work the same in every browser.
For text, other HTML and transitions, VFX-JS has to capture the DOM:
- With HTML-in-Canvas, the browser draws the element itself, so the capture matches the page. The API is still experimental and is only in Chrome Canary, or Chrome behind a flag.
- Elsewhere, VFX-JS clones the element and draws it as an SVG image. This
is a workaround and can't match the page exactly. Web fonts,
::before/::afterand<canvas>content, among others, don't make it into the capture.
See Limitations for the full list.
Install the VFX-JS skill for Claude Code, Codex, Cursor and other agents:
npx skills add fand/vfx-jsOr point your agent to llms.txt / llms-full.txt.
Requires Node.js 24 and pnpm.
git clone https://github.com/fand/vfx-js.git
cd vfx-js
pnpm install
pnpm devpnpm dev starts the docs and transitions sites and watches the libraries
they use. pnpm build, pnpm lint and pnpm test run across all
packages. See CONTRIBUTING.md before you send a pull
request.
MIT
