Effects for the modern web.
30 WebGL2 filter primitives — blur, bloom, glow, color grade, sharpen, halftone, tilt-shift, scanlines, lens distortion, oil paint, wave, swirl, motion blur, VHS, datamosh, ASCII, dither, gradient map and more. One Runner, one source, one render call. Multi-pass effects auto-allocate HDR ping-pong targets.
- 30 effects
- ~9 KB gzipped (full)
- 0 runtime deps
- tree-shakable import what you ship
$ pnpm add @vysmo/effects View on GitHub Request an effect
Tell us what you have in mind — a look, a reference, a vibe — and we’ll consider it for the next batch.
Three patterns to start with
Drop in any of the eight built-in effects, chain bloom over a photograph, or define your own with the same data-first authoring pattern transitions use.
Single-pass blur
Most effects are a one-liner. The Runner caches the compiled program per effect, so re-rendering the same effect with new params skips compilation.
import { Runner, blur } from "@vysmo/effects";
const runner = new Runner({ canvas });
const image = document.querySelector("img")!;
await image.decode();
runner.render(blur, {
source: image,
params: { radius: 12 },
}); Multi-pass bloom (HDR)
Bloom and glow ship four-pass pipelines under the hood —
bright-pass, separable Gaussian, composite. They opt into
RGBA16F ping-pong targets when the GPU supports it.
import { Runner, bloom } from "@vysmo/effects";
const runner = new Runner({ canvas });
const image = document.querySelector("img")!;
await image.decode();
// Bloom auto-allocates ping-pong RGBA16F targets internally
// so highlights survive the bright-pass / blur / composite chain.
runner.render(bloom, {
source: image,
params: { intensity: 1.2, threshold: 0.7 },
}); Author your own
defineEffect infers the param types from
defaults. Write a fragment that exports vec4 effect(vec2 uv) and the runner takes care of
uniforms, FBO allocation, and pass dispatch.
import { defineEffect, Runner } from "@vysmo/effects";
// Author your own effect — same defineX pattern as transitions.
const tint = defineEffect({
name: "tint",
defaults: { strength: 0.5, color: [1, 0.4, 0.8] as const },
glsl: `
uniform float uStrength;
uniform vec3 uColor;
vec4 effect(vec2 uv) {
vec4 src = getSource(uv);
return vec4(mix(src.rgb, uColor, uStrength), src.a);
}`,
});
new Runner({ canvas }).render(tint, { source: image });