wgsl-play 0.0.37 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,11 +29,94 @@ import env::u;
29
29
  }
30
30
  ```
31
31
 
32
- | Uniform | Type | Description |
33
- |---------|------|-------------|
34
- | `resolution` | `vec2f` | Canvas dimensions in pixels |
32
+ When no `@uniforms` struct is declared, a default is provided with `resolution` and `time`.
33
+
34
+ ### Custom Uniforms
35
+
36
+ Declare a struct with `@uniforms` to add your own fields with UI controls:
37
+
38
+ ```wgsl
39
+ import env::u;
40
+
41
+ @uniforms struct Params {
42
+ @auto resolution: vec2f,
43
+ @auto time: f32,
44
+ @range(1.0, 20.0, 5.0, 6.0) frequency: f32,
45
+ @color(0.2, 0.5, 1.0) tint: vec3f,
46
+ @toggle(0) invert: u32,
47
+ }
48
+
49
+ @fragment fn main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
50
+ let wave = sin(pos.x * u.frequency + u.time);
51
+ var color = wave * u.tint;
52
+ if u.invert == 1u { color = 1.0 - color; }
53
+ return vec4f(color, 1.0);
54
+ }
55
+ ```
56
+
57
+ ### `@auto` -- Runtime Fields
58
+
59
+ The player fills these automatically each frame. The field name determines
60
+ which value is bound (or use `@auto(name)` when the field name differs):
61
+
62
+ | Name | Type | Description |
63
+ |------|------|-------------|
64
+ | `resolution` | `vec2f` | Canvas size in pixels |
35
65
  | `time` | `f32` | Elapsed time in seconds |
36
- | `mouse` | `vec2f` | Mouse position (normalized 0-1) |
66
+ | `delta_time` | `f32` | Delta time since last frame |
67
+ | `frame` | `u32` | Frame count |
68
+ | `mouse_pos` | `vec2f` | Pointer position in pixels |
69
+ | `mouse_delta` | `vec2f` | Pointer movement since last frame |
70
+ | `mouse_button` | `i32` | Active button: 0=none, 1=left, 2=middle, 3=right |
71
+
72
+ ### UI Annotations
73
+
74
+ These generate interactive controls in the player.
75
+
76
+ #### `@range(min, max [, step [, initial]])`
77
+
78
+ Slider for `f32` or `i32`. Step defaults to `0.01` for `f32`, `1` for `i32`.
79
+ Initial defaults to `min`.
80
+
81
+ ```wgsl
82
+ @range(1.0, 20.0) frequency: f32,
83
+ @range(1.0, 20.0, 5.0) frequency: f32, // step=5
84
+ @range(1.0, 20.0, 0.5, 5.0) frequency: f32, // step=0.5, initial=5
85
+ ```
86
+
87
+ #### `@color(r, g, b)`
88
+
89
+ Color picker for `vec3f`:
90
+
91
+ ```wgsl
92
+ @color(0.2, 0.5, 1.0) tint: vec3f,
93
+ ```
94
+
95
+ #### `@toggle([initial])`
96
+
97
+ Boolean toggle for `u32` (0 or 1). WGSL forbids `bool` in uniform buffers.
98
+
99
+ ```wgsl
100
+ @toggle invert: u32, // default=0
101
+ @toggle(1) invert: u32, // default=1
102
+ ```
103
+
104
+ ### Plain Fields
105
+
106
+ Fields without annotations are zero-initialized and settable from JavaScript
107
+ via `setUniform()`. This works before or after compilation.
108
+
109
+ ```wgsl
110
+ @uniforms struct Params {
111
+ @auto resolution: vec2f,
112
+ brightness: f32, // no annotation — set from JS
113
+ }
114
+ ```
115
+
116
+ ```javascript
117
+ const player = document.querySelector("wgsl-play");
118
+ player.setUniform("brightness", 0.8);
119
+ ```
37
120
 
38
121
  ### Inline source
39
122
 
@@ -56,7 +139,7 @@ You can include shader code inline if you'd prefer. Use a `<script type="text/wg
56
139
 
57
140
  ```typescript
58
141
  const player = document.querySelector("wgsl-play");
59
- player.source = shaderCode;
142
+ player.shader = shaderCode;
60
143
  player.pause();
61
144
  player.rewind();
62
145
  player.play();
@@ -68,7 +151,7 @@ player.play();
68
151
  import shader from './examples/noise.wesl?raw';
69
152
 
70
153
  const player = document.querySelector("wgsl-play");
71
- player.source = shader;
154
+ player.shader = shader;
72
155
  ```
73
156
 
74
157
  The `?raw` suffix imports the file as a string. This keeps shaders alongside your source files with HMR support.
@@ -80,12 +163,20 @@ The `?raw` suffix imports the file as a string. This keeps shaders alongside you
80
163
  - `shader-root` - Root path for internal imports (default: `/shaders`)
81
164
  - `autoplay` - Start animating on load (default: `true`). Set `autoplay="false"` to start paused
82
165
  - `transparent` - Use premultiplied alpha for transparent backgrounds (default: opaque)
166
+ - `from` - Element ID of a source provider (e.g., wgsl-edit) to connect to
167
+ - `no-controls` - Hide playback controls (play/pause, rewind, fullscreen)
168
+ - `no-settings` - Hide the uniform controls panel
169
+ - `width` / `height` - Fixed canvas resolution in pixels, independent of display size. When set, the canvas is not resized by the CSS layout
170
+ - `pixel-ratio` - Scale factor from CSS pixels to canvas pixels (default: `devicePixelRatio`). Set `pixel-ratio="1"` for 1:1 CSS pixels (no HiDPI scaling)
171
+ - `resizable` - Show a drag handle to let users resize the element interactively
83
172
  - `fetch-libs` - Auto-fetch missing libraries from npm (default: `true`). Set `fetch-libs="false"` to disable
173
+ - `fetch-sources` - Auto-fetch local .wesl source files via HTTP (default: `true`). Set `fetch-sources="false"` to disable
84
174
 
85
175
  ### Properties
86
- - `source: string` - Get/set shader source
176
+ - `shader: string` - Get/set shader source (single-file convenience)
87
177
  - `conditions: Record<string, boolean>` - Get/set conditions for conditional compilation (`@if`/`@elif`/`@else`)
88
- - `project: WeslProject` - Set full project config (weslSrc, libs, conditions, constants)
178
+ - `project: WeslProject` - Get/set full project config (weslSrc, libs, conditions, constants)
179
+ - `pixelRatio: number` - Get/set canvas-to-CSS pixel ratio (default: `devicePixelRatio`)
89
180
  - `isPlaying: boolean` - Playback state (readonly)
90
181
  - `time: number` - Animation time in seconds (readonly)
91
182
  - `hasError: boolean` - Compilation error state (readonly)
@@ -95,12 +186,29 @@ The `?raw` suffix imports the file as a string. This keeps shaders alongside you
95
186
  - `play()` - Start/resume animation
96
187
  - `pause()` - Pause animation
97
188
  - `rewind()` - Reset to t=0
189
+ - `setUniform(name, value)` - Set a uniform value programmatically
98
190
  - `showError(message)` - Display error (empty string clears)
99
191
 
100
192
  ### Events
101
193
  - `compile-error` - `{ message: string }`
102
194
  - `init-error` - `{ message: string }` (WebGPU init failed)
103
195
  - `playback-change` - `{ isPlaying: boolean }`
196
+ - `uniforms-layout` - `{ detail: AnnotatedLayout }` (fired after each compile)
197
+
198
+ ## Canvas Sizing
199
+
200
+ By default the canvas resolution tracks CSS size at `devicePixelRatio`.
201
+ Use `pixel-ratio` or `width`/`height` to decouple:
202
+
203
+ ```html
204
+ <!-- 1:1 CSS pixels (blocky on HiDPI, great for pixel art) -->
205
+ <wgsl-play pixel-ratio="1" style="width:512px; height:512px"></wgsl-play>
206
+
207
+ <!-- Fixed 64x64 canvas, stretched to whatever CSS size -->
208
+ <wgsl-play width="64" height="64" style="width:512px; height:512px"></wgsl-play>
209
+ ```
210
+
211
+ For crisp upscaling of low-res canvases, add `image-rendering: pixelated`:
104
212
 
105
213
  ## Styling
106
214
 
@@ -147,7 +255,7 @@ import super::common::tint;
147
255
 
148
256
  ## Using with wesl-plugin
149
257
 
150
- For more control, use the [wesl-plugin](https://github.com/wgsl-tooling-wg/wesl-js/tree/main/tools/packages/wesl-plugin) to
258
+ For more control, use the [wesl-plugin](https://github.com/wgsl-tooling-wg/wesl-js/tree/main/packages/wesl-plugin) to
151
259
  assemble shaders and libraries at build time and provide
152
260
  them wgsl-play in JavaScript or TypeScript.
153
261
  - provides full support for Hot Module Reloading during development
@@ -1,4 +1,4 @@
1
- import { Conditions, LinkParams } from "wesl";
1
+ import { Conditions, WeslProject } from "wesl";
2
2
 
3
3
  //#region src/Config.d.ts
4
4
  /** Configuration for wgsl-play. */
@@ -14,8 +14,6 @@ declare function getConfig(overrides?: Partial<WgslPlayConfig>): WgslPlayConfig;
14
14
  declare function resetConfig(): void;
15
15
  //#endregion
16
16
  //#region src/WgslPlay.d.ts
17
- /** Project configuration for multi-file shaders (subset of wesl link() API). */
18
- type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName">;
19
17
  /** One source location within a compile error. */
20
18
  interface CompileErrorLocation {
21
19
  file?: string;
@@ -37,15 +35,17 @@ declare class WgslPlay extends HTMLElement {
37
35
  private canvas;
38
36
  private errorOverlay;
39
37
  private controls;
38
+ private settings;
40
39
  private resizeObserver;
41
40
  private stopRenderLoop?;
42
41
  private renderState?;
42
+ private pendingUniforms;
43
43
  private playback;
44
44
  private _weslSrc;
45
45
  private _rootModuleName;
46
46
  private _libs?;
47
47
  private _linkOptions;
48
- private _fromFullProject;
48
+ private _fetchSources;
49
49
  private _initPromise?;
50
50
  private _sourceEl;
51
51
  private _sourceListener;
@@ -55,6 +55,8 @@ declare class WgslPlay extends HTMLElement {
55
55
  private _theme;
56
56
  private _mediaQuery;
57
57
  private _onFullscreenChange;
58
+ private _pointerCleanup?;
59
+ private _resizeCleanup?;
58
60
  /** Get config overrides from element attributes. */
59
61
  private getConfigOverrides;
60
62
  constructor();
@@ -62,22 +64,26 @@ declare class WgslPlay extends HTMLElement {
62
64
  disconnectedCallback(): void;
63
65
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
64
66
  /** Current shader source code (main module). */
65
- get source(): string;
66
- /** Set shader source directly. */
67
- set source(value: string);
67
+ get shader(): string;
68
+ /** Set shader source directly (single-file convenience). */
69
+ set shader(value: string);
68
70
  /** Conditions for conditional compilation (@if/@elif/@else). */
69
71
  get conditions(): Conditions;
70
72
  set conditions(value: Conditions);
71
73
  /** Set project configuration (mirrors wesl link() API). */
72
74
  set project(value: WeslProject);
73
- /** Set sources from a full project with weslSrc. */
74
- private setProjectSources;
75
75
  /** Whether to auto-fetch missing library packages from npm (default: true). */
76
76
  get fetchLibs(): boolean;
77
77
  set fetchLibs(value: boolean);
78
+ /** Whether to fetch local .wesl source files via HTTP (default: true). */
79
+ get fetchSources(): boolean;
80
+ set fetchSources(value: boolean);
78
81
  /** Whether autoplay is enabled (default: true). Set autoplay="false" to start paused. */
79
82
  get autoplay(): boolean;
80
83
  set autoplay(value: boolean | string);
84
+ /** Scale factor from CSS pixels to canvas pixels (default: devicePixelRatio). */
85
+ get pixelRatio(): number;
86
+ set pixelRatio(value: number);
81
87
  /** Whether the shader is currently playing. */
82
88
  get isPlaying(): boolean;
83
89
  /** Current animation time in seconds. */
@@ -97,8 +103,19 @@ declare class WgslPlay extends HTMLElement {
97
103
  rewind(): void;
98
104
  /** Display error message in overlay. Pass empty string to clear. */
99
105
  showError(message: string): void;
106
+ /** Set a uniform value by name. Works before or after compilation. */
107
+ setUniform(name: string, value: number | number[]): void;
108
+ private flushPendingUniforms;
109
+ /** Current uniform control values (readable). */
110
+ get uniforms(): Record<string, number | number[]>;
100
111
  /** Toggle fullscreen on this element. */
101
112
  toggleFullscreen(): void;
113
+ /** Track pointer events on canvas for mouse_pos @auto fields. */
114
+ private setupMouseTracking;
115
+ /** Drag-to-resize via a custom handle (works on touch + mouse). */
116
+ private setupResizeHandle;
117
+ /** Recompute canvas resolution from attributes or CSS size. */
118
+ private updateCanvasSize;
102
119
  private updateTheme;
103
120
  /** Set up WebGPU and load initial shader. Returns true if successful. */
104
121
  private initialize;
@@ -106,16 +123,20 @@ declare class WgslPlay extends HTMLElement {
106
123
  /** Load from source element, src URL, script child, or inline textContent. */
107
124
  private loadInitialContent;
108
125
  /** Connect to a source provider element (e.g., wgsl-edit). */
109
- private connectToSource;
126
+ private connectFrom;
110
127
  /** Fetch shader from URL, then trigger a build. */
111
128
  private loadFromUrl;
112
129
  /** Mark build as needed. Coalesces rapid requests into a single build. */
113
130
  private requestBuild;
114
131
  /** Run builds until no longer dirty. Only one instance runs at a time. */
115
132
  private runBuild;
133
+ /** Fetch deps if needed and create the render pipeline. */
134
+ private buildPipeline;
135
+ /** Apply a successful build: flush uniforms, update controls, render. */
136
+ private applyBuild;
116
137
  private handleCompileError;
117
138
  /** Extract source locations from a WESL parse error or GPU compilation error. */
118
139
  private extractLocations;
119
140
  }
120
141
  //#endregion
121
- export { WgslPlayConfig as a, resetConfig as c, WgslPlay as i, CompileErrorLocation as n, defaults as o, WeslProject as r, getConfig as s, CompileErrorDetail as t };
142
+ export { defaults as a, WgslPlayConfig as i, CompileErrorLocation as n, getConfig as o, WgslPlay as r, resetConfig as s, CompileErrorDetail as t };