Manifest + WGSL
A plugin is a folder: manifest.json plus one or more WGSL files. Typed params, ordered passes, and nothing a host has to guess.
A WispFX plugin is a JSON manifest plus WGSL shaders, and it renders identically in native wgpu hosts and in the browser through WebGPU. Write an effect or a transition once,
run it in any host that speaks the standard, and test it headlessly with the wispfx CLI.
A plugin is a folder: manifest.json plus one or more WGSL files. Typed params, ordered passes, and nothing a host has to guess.
An effect takes one input, source. A transition takes from and to plus a progress the host drives from 0 to 1 across the cut.
The host prepends a prelude that declares every binding and the vertex stage. A plugin writes fs_main and never its own samplers or textures.
A plugin declares MIDI 2.0 controllers and notes it reacts to. The host feeds them from an audio follower, a hardware device or OSC, and the shader reads plain 0..1 floats. A grade can follow the bass.
Linear premultiplied rgba16float in and out, WGSL both naga and the browser accept, no extensions. A native host and a browser host draw the same frame.
manifest.json declares what the plugin is, its params and its passes. shader.wgsl is the fragment code. The host prepends the prelude and compiles.
{
"wisp": "0.1",
"id": "org.wispfx.cross-dissolve",
"name": "Cross Dissolve",
"kind": "transition",
"category": "dissolve",
"version": "0.1.0",
"license": "MIT OR Apache-2.0",
"params": [
{ "id": "ease", "label": "Ease", "type": "float",
"default": 0.0, "min": 0.0, "max": 1.0, "animatable": true }
],
"passes": [
{ "shader": "shader.wgsl", "entry": "fs_main",
"inputs": ["from", "to"], "output": "out" }
]
}struct Params {
ease: f32,
}
@group(1) @binding(0) var<uniform> params: Params;
@fragment
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
let a = textureSampleLevel(wisp_input0, wisp_sampler, uv, 0.0);
let b = textureSampleLevel(wisp_input1, wisp_sampler, uv, 0.0);
let p = clamp(wisp_globals.progress, 0.0, 1.0);
let t = mix(p, smoothstep(0.0, 1.0, p), params.ease);
return mix(a, b, t); // premultiplied: a plain mix is correct
}wispfx check and wispfx render