@vysmo/effects Library Canvas

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
Don’t see what you need?

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 });