Renderer and effects

A renderer owns one canvas and one context: WebGL2 for GLSL effects, WebGPU for WGSL ones. Effects are definitions it instantiates on demand; any number of them share the same context.

Renderer

import { createRenderer } from 'webgl-ui'
const renderer = createRenderer()
MemberDescription
canvasThe canvas, aria-hidden, attached only while an animation runs.
prepare(effect)Starts initializing an effect (idempotent); resolves true when it can render. Elements call it at creation.
isReady(effect)Whether the effect can render now.
play(host, options)Runs one animation on host; resolves true when it finishes, false if cancelled. A new play cancels the previous one.
cancel(owner?)Stops the active animation (only if it belongs to owner, when given).
dispose()Frees the context and every effect.

A lost WebGL context re-initializes every effect when it comes back; in between, elements are native.

Your own GLSL effect

A fragment shader in GLSL ES 3.00. It may declare any of these uniforms and must write premultiplied alpha. See the cookbook recipe for a complete one.

UniformTypeValue
uProgressfloat0 → 1 while showing, 1 → 0 while hiding (eased).
uTimefloatSeconds, continuous across reversals.
uResolutionvec2Canvas size in physical pixels.
uColorvec3Cover or backdrop color, sRGB 0..1.
uAlphafloatBackdrop alpha (dialog); 1 elsewhere.
uPointervec2Normalized pointer position (reserved).
import { defineGlslEffect } from 'webgl-ui'
const fade = defineGlslEffect('fade', `#version 300 es
precision highp float;
uniform float uProgress;
uniform vec3 uColor;
out vec4 outColor;
void main() {
  float a = 1.0 - uProgress;
  outColor = vec4(uColor * a, a);
}`)

A dialog backdrop effect must end, at uProgress = 1, on a uniform uColor with alpha uAlpha: that is what CSS shows after the hand-over.

Rules for effect authors

WGSL effects (WebGPU)

The core also draws with WebGPU, with no dependency. A WGSL effect is one function returning premultiplied color; the standard inputs are in the uniform fx. Give it a GLSL twin as fallback: where WebGPU is missing (no navigator.gpu, no adapter) the fallback runs on WebGL2 instead, and with neither the elements are native.

import { createRenderer, createDetailsFx, defineWgslEffect, inkEffect, inkGpuEffect } from 'webgl-ui'

const fade = defineWgslEffect('fade', `
fn effect(uv: vec2f, frag: vec2f) -> vec4f {   // uv: 0..1, origin top left
  let a = 1.0 - smoothstep(fx.progress - 0.1, fx.progress + 0.1, 1.0 - uv.y);
  return vec4f(fx.color.xyz * a, a);
}`, { fallback: inkEffect })

// A canvas holds one kind of context: WGSL effects get a renderer of their own.
const gpuRenderer = createRenderer()
createDetailsFx(details, { renderer: gpuRenderer, effect: inkGpuEffect }) // the ink, in WGSL
InputValue
fx.progress, fx.timeAs uProgress, uTime.
fx.resolution, fx.pointerAs uResolution, uPointer.
fx.color.xyz, fx.alphaAs uColor, uAlpha.
uv, frag0..1 with the origin at the top left (the WebGPU convention); fragment position in pixels.

Built in: inkGpuEffect and inkVeilGpuEffect, the ink and the veil in WGSL with their GLSL versions as fallbacks. Image sampling and params are GLSL-only for now. Live: WebGPU playground page.

TSL effects (three.js)

The optional package packages/tsl builds effects as TSL node graphs; three.js is loaded lazily and is never a core dependency. A canvas holds one kind of context, so TSL effects need a renderer of their own.

import { createRenderer, createDetailsFx } from 'webgl-ui'
import { defineTslEffect } from 'webgl-ui-tsl'

const fade = defineTslEffect('fade', ({ progress, color, uv }, { vec4, smoothstep }) => {
  const a = smoothstep(progress.sub(0.1), progress.add(0.1), uv.y).oneMinus()
  return vec4(color.mul(a), a) // premultiplied; uv origin at the top left
})

const tslRenderer = createRenderer()
createDetailsFx(details, { renderer: tslRenderer, effect: fade })

Live: TSL playground page.