wgsl-play
v0.0.5
Published
Web component for rendering WESL/WGSL fragment shaders.
Readme
wgsl-play
Web component for rendering WESL/WGSL fragment shaders.
Usage
<script type="module">import "wgsl-play";</script>
<wgsl-play src="./shader.wesl"></wgsl-play>That's it. The component auto-fetches dependencies and starts animating.
Shader API
Write a fragment shader with entry point fs_main. WESL extensions are supported (imports, conditional compilation).
Standard uniforms are provided at binding 0:
import test::Uniforms;
@group(0) @binding(0) var<uniform> u: Uniforms;
@fragment fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
let uv = pos.xy / u.resolution;
return vec4f(uv, sin(u.time) * 0.5 + 0.5, 1.0);
}| Uniform | Type | Description |
|---------|------|-------------|
| resolution | vec2f | Canvas dimensions in pixels |
| time | f32 | Elapsed time in seconds |
| mouse | vec2f | Mouse position (normalized 0-1) |
Inline source
You can include shader code inline if you'd prefer. Use a <script type="text/wgsl"> (or <script type="text/wesl">) tag.
<wgsl-play>
<script type="text/wesl">
import test::Uniforms;
@group(0) @binding(0) var<uniform> u: Uniforms;
@fragment fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
let uv = pos.xy / u.resolution;
return vec4f(uv, sin(u.time) * 0.5 + 0.5, 1.0);
}
</script>
</wgsl-play>Programmatic control
const player = document.querySelector("wgsl-play");
player.source = shaderCode;
player.pause();
player.rewind();
player.play();Importing shaders (Vite)
import shader from './examples/noise.wesl?raw';
const player = document.querySelector("wgsl-play");
player.source = shader;The ?raw suffix imports the file as a string. This keeps shaders alongside your source files with HMR support.
API
Attributes
src- URL to .wesl/.wgsl fileshader-root- Root path for internal imports (default:/shaders)
Properties
source: string- Get/set shader sourceproject: WeslProject- Set full project config (weslSrc, libs, conditions, constants)isPlaying: boolean- Playback state (readonly)time: number- Animation time in seconds (readonly)hasError: boolean- Compilation error state (readonly)errorMessage: string | null- Error message (readonly)
Methods
play()- Start/resume animationpause()- Pause animationrewind()- Reset to t=0showError(message)- Display error (empty string clears)
Events
compile-error-{ message: string }init-error-{ message: string }(WebGPU init failed)playback-change-{ isPlaying: boolean }
Styling
wgsl-play {
width: 512px;
height: 512px;
}
wgsl-play::part(canvas) {
image-rendering: pixelated;
}Multi-file Shaders
For apps with multiple shader files, use shader-root.
public/
shaders/
utils.wesl # import package::utils
effects/
main.wesl # import super::common
common.wesl<wgsl-play src="/shaders/effects/main.wesl" shader-root="/shaders"></wgsl-play>Local shader modules referenced via package:: or super::
will be fetched from the web server.
// effects/main.wesl
import package::utils::noise;
import super::common::tint;
@fragment fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
return tint(noise(pos.xy));
}Using with wesl-plugin
For more control, use the wesl-plugin to assemble shaders and libraries at build time and provide them wgsl-play in JavaScript or TypeScript.
- provides full support for Hot Module Reloading during development
- allows specifying fixed library dependency versions
Runtime linking (?link)
With Runtime linking, WGSL is constructed from WGSL/WESL at runtime. Use runtime linking to enable virtual modules, conditional transpilation, and injecting shader constants from JavaScript.
// vite.config.ts
import { linkBuildExtension } from "wesl-plugin";
import viteWesl from "wesl-plugin/vite";
export default {
plugins: [viteWesl({ extensions: [linkBuildExtension] })]
};
// app.ts
import shaderConfig from "./shader.wesl?link";
// wgsl-play links internally, allowing runtime conditions/constants
player.project = {
...shaderConfig,
conditions: { MOBILE: isMobileGPU },
constants: { num_lights: 4 }
};Exports
// Default - auto-registers element
import "wgsl-play";
// Element class only (manual registration)
import { WgslPlay } from "wgsl-play/element";
// Configuration
import { defaults } from "wgsl-play";
defaults({ shaderRoot: "/custom/shaders" });