shaderpad 1.0.0-beta.18 → 1.0.0-beta.19

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
@@ -1,6 +1,8 @@
1
- # ShaderPad
1
+ ![ShaderPad logo](./ShaderPad.png)
2
2
 
3
- ShaderPad is a lightweight, dependency-free library to reduce boilerplate when writing fragment shaders. It provides a simple interface to initialize shaders, manage uniforms, maintain an animation loop, and handle user interactions such as mouse movements and window resizing.
3
+ **Minimal setup. Tiny footprint.**
4
+
5
+ ShaderPad is a lightweight, dependency-free library that reduces boilerplate when working with fragment shaders. It provides a simple interface for setup, texture and uniform management, and user interactions such as mouse movements and window resizing. It comes “batteries included” for common needs, with optional plugins — from PNG export to face/pose tracking — for more advanced use cases. Simple, performant, and portable, ShaderPad lets you focus on the fun part: writing GLSL.
4
6
 
5
7
  ## Installation
6
8
 
@@ -21,7 +23,7 @@ precision highp float;
21
23
  in vec2 v_uv;
22
24
  uniform float u_time;
23
25
  uniform vec2 u_resolution;
24
- uniform vec4 u_cursor; // [cursorX, cursorY, scrollX, scrollY]
26
+ uniform vec2 u_cursor;
25
27
 
26
28
  // Custom variables.
27
29
  uniform vec3 u_cursorColor;
@@ -34,7 +36,7 @@ void main() {
34
36
  float dotDist = length(dotGrid);
35
37
  float dot = step(dotDist, 5.);
36
38
 
37
- float cursorDist = distance(uv, u_cursor.xy * u_resolution);
39
+ float cursorDist = distance(uv, u_cursor * u_resolution);
38
40
  float cursor = step(cursorDist, 25. + sin(u_time * 5.) * 5.);
39
41
 
40
42
  vec3 color = mix(vec3(0., 0., 1.), vec3(1.), dot);
@@ -71,6 +73,173 @@ shader.play(time => {
71
73
 
72
74
  See the [`examples/` directory](./examples/) for more.
73
75
 
76
+ ## Usage
77
+
78
+ ### Uniforms
79
+
80
+ #### `initializeUniform(name, type, value, options?)`
81
+
82
+ Initialize a uniform variable. The uniform must be declared in your fragment shader.
83
+
84
+ ```typescript
85
+ // Initialize a float uniform.
86
+ shader.initializeUniform('u_speed', 'float', 1.5);
87
+
88
+ // Initialize a vec3 uniform.
89
+ shader.initializeUniform('u_color', 'float', [1.0, 0.5, 0.0]);
90
+ ```
91
+
92
+ **Parameters:**
93
+
94
+ - `name` (string): The name of the uniform as declared in your shader
95
+ - `type` ('float' | 'int'): The uniform type
96
+ - `value` (number | number[] | (number | number[])[]): Initial value(s)
97
+ - `options` (optional): `{ arrayLength?: number }` - Required for uniform arrays
98
+
99
+ #### `updateUniforms(updates, options?)`
100
+
101
+ Update one or more uniform values.
102
+
103
+ ```typescript
104
+ shader.updateUniforms({
105
+ u_speed: 2.0,
106
+ u_color: [1.0, 0.0, 0.0],
107
+ });
108
+ ```
109
+
110
+ **Parameters:**
111
+
112
+ - `updates` (Record<string, number | number[] | (number | number[])[]>): Object mapping uniform names to their new values
113
+ - `options` (optional): `{ startIndex?: number }` - Starting index for partial array updates
114
+
115
+ #### Uniform arrays
116
+
117
+ ShaderPad supports uniform arrays. Initialize them by passing an `arrayLength` option:
118
+
119
+ ```typescript
120
+ // Initialize a vec2 array with 5 elements.
121
+ shader.initializeUniform(
122
+ 'u_positions',
123
+ 'float',
124
+ [
125
+ [0, 0],
126
+ [1, 1],
127
+ [2, 2],
128
+ [3, 3],
129
+ [4, 4],
130
+ ],
131
+ {
132
+ arrayLength: 5,
133
+ }
134
+ );
135
+
136
+ // Update all elements.
137
+ shader.updateUniforms({
138
+ u_positions: [
139
+ [0.1, 0.2],
140
+ [1.1, 1.2],
141
+ [2.1, 2.2],
142
+ [3.1, 3.2],
143
+ [4.1, 4.2],
144
+ ],
145
+ });
146
+
147
+ // Update a subset starting at index 2.
148
+ shader.updateUniforms(
149
+ {
150
+ u_positions: [
151
+ [2.5, 2.5],
152
+ [3.5, 3.5],
153
+ ],
154
+ },
155
+ { startIndex: 2 }
156
+ );
157
+ ```
158
+
159
+ #### Included uniforms
160
+
161
+ | Uniform | Type | Description |
162
+ | ---------------------- | -------------- | --------------------------------------------------------------------------------- |
163
+ | `u_time` | float | The current time in seconds. |
164
+ | `u_frame` | int | The current frame number. |
165
+ | `u_resolution` | float[2] | The canvas element's dimensions. |
166
+ | `u_cursor` | float[2] | Cursor position (x, y). |
167
+ | `u_click` | float[3] | Click position (x, y) and left click state (z). |
168
+ | `u_history` | sampler2DArray | Buffer texture of prior frames. Available if `history` option is set. |
169
+ | `u_historyFrameOffset` | int | Frame offset for accessing history texture. Available if `history` option is set. |
170
+
171
+ #### Included varyings
172
+
173
+ | Varying | Type | Description |
174
+ | ------- | -------- | ----------------------------------- |
175
+ | `v_uv` | float[2] | The UV coordinates of the fragment. |
176
+
177
+ ### Textures
178
+
179
+ #### `initializeTexture(name, source, options?)`
180
+
181
+ Initialize a texture from an image, video, or canvas element.
182
+
183
+ ```typescript
184
+ // Initialize a texture from an image.
185
+ const img = new Image();
186
+ img.src = 'texture.png';
187
+ img.onload = () => {
188
+ shader.initializeTexture('u_texture', img);
189
+ };
190
+
191
+ // Initialize a texture with history (stores previous frames).
192
+ shader.initializeTexture('u_webcam', videoElement, { history: 30 });
193
+ ```
194
+
195
+ **Parameters:**
196
+
197
+ - `name` (string): The name of the texture uniform as declared in your shader
198
+ - `source` (HTMLImageElement | HTMLVideoElement | HTMLCanvasElement): The texture source
199
+ - `options` (optional): `{ history?: number }` - Number of previous frames to store
200
+
201
+ #### `updateTextures(updates)`
202
+
203
+ Update one or more textures. Useful for updating video textures each frame.
204
+
205
+ ```typescript
206
+ shader.updateTextures({
207
+ u_webcam: videoElement,
208
+ u_overlay: overlayCanvas,
209
+ });
210
+ ```
211
+
212
+ **Parameters:**
213
+
214
+ - `updates` (Record<string, TextureSource>): Object mapping texture names to their new sources
215
+
216
+ ### Lifecycle methods
217
+
218
+ #### `play(callback?)`
219
+
220
+ Start the render loop. The callback is invoked each frame with the current time and frame number.
221
+
222
+ ```typescript
223
+ shader.play();
224
+ // Can optionally take a callback to invoke each frame.
225
+ shader.play((time, frame) => {
226
+ shader.updateTextures({ u_webcam: videoElement });
227
+ });
228
+ ```
229
+
230
+ #### `pause()`, `reset()`, `destroy()`
231
+
232
+ ```typescript
233
+ shader.pause(); // Pause the render loop.
234
+ shader.reset(); // Reset frame counter and clear history buffers.
235
+ shader.destroy(); // Clean up resources.
236
+ ```
237
+
238
+ ### Properties
239
+
240
+ - `canvas` (HTMLCanvasElement): The canvas element used for rendering
241
+ - `onResize?: (width: number, height: number) => void`: Callback fired when the canvas is resized
242
+
74
243
  ## Options
75
244
 
76
245
  ShaderPad’s constructor accepts an optional `options` object.
@@ -88,51 +257,219 @@ shader.onResize = (width, height) => {
88
257
  };
89
258
  ```
90
259
 
91
- ### plugins
260
+ ### history
92
261
 
93
- ShaderPad supports plugins to add additional functionality.
262
+ The `history` option enables framebuffer history, allowing you to access previous frames in your shader. Pass a number to specify how many frames to keep.
94
263
 
95
- #### history
264
+ ```typescript
265
+ // Store the last 10 frames of shader output.
266
+ const shader = new ShaderPad(fragmentShaderSrc, { history: 10 });
96
267
 
97
- The `history` plugin can capture both the framebuffer output and texture history. The plugin argument controls how many frames to keep for the framebuffer (defaults to `1`).
268
+ // In your shader, access history using the u_history uniform:
269
+ // uniform highp sampler2DArray u_history;
270
+ // It’s stored as a ringbuffer, so you can access specific frames with the provided offset uniform:
271
+ // uniform int u_historyFrameOffset;
272
+ ```
273
+
274
+ You can also enable history for individual textures:
98
275
 
99
276
  ```typescript
100
- import ShaderPad, { history, WithHistory } from 'shaderpad';
277
+ // Store the last 30 webcam frames.
278
+ shader.initializeTexture('u_webcam', videoElement, { history: 30 });
279
+ // In your shader, access history using the u_webcam and u_webcamFrameOffset uniforms:
280
+ // uniform highp sampler2DArray u_webcam;
281
+ // uniform int u_webcamFrameOffset;
282
+ ```
101
283
 
102
- // Store the last 10 frames of shader output.
103
- const shaderWithOutputHistory = new ShaderPad(fragmentShaderSrc, { plugins: [history(10)] });
284
+ It’s recommended to use the `helpers` plugin to simplify access to history textures:
104
285
 
105
- // Store the last 30 webcam frames.
106
- const shader = new ShaderPad(fragmentShaderSrc, { plugins: [history()] }) as WithHistory<ShaderPad>;
107
- shader.initializeTexture('u_webcam', videoElement, 30);
286
+ ```glsl
287
+ int nFramesAgo = 2; // Get the color 2 frames ago.
288
+ float zIndex = historyZ(u_webcam, u_webcamFrameOffset, nFramesAgo);
289
+ vec4 historyColor = texture(u_webcam, vec3(v_uv, zIndex));
290
+ ```
291
+
292
+ ### plugins
293
+
294
+ ShaderPad supports plugins to add additional functionality. Plugins are imported from separate paths to keep bundle sizes small.
295
+
296
+ #### helpers
297
+
298
+ The `helpers` plugin provides convenience functions and constants. See [helpers.glsl](./src/plugins/helpers.glsl) for the implementation.
299
+
300
+ ```typescript
301
+ import ShaderPad from 'shaderpad';
302
+ import { helpers } from 'shaderpad/plugins/helpers';
303
+
304
+ const shader = new ShaderPad(fragmentShaderSrc, {
305
+ plugins: [helpers()],
306
+ });
108
307
  ```
109
308
 
110
309
  #### save
111
310
 
112
- The `save` plugin adds a `.save()` method to the shader that saves the current frame to a PNG file.
311
+ The `save` plugin adds a `.save()` method to the shader that saves the current frame to a PNG file. It works on desktop and mobile.
113
312
 
114
313
  ```typescript
115
- import ShaderPad, { save, WithSave } from 'shaderpad';
314
+ import ShaderPad from 'shaderpad';
315
+ import { save, WithSave } from 'shaderpad/plugins/save';
316
+
116
317
  const shader = new ShaderPad(fragmentShaderSrc, { plugins: [save()] }) as WithSave<ShaderPad>;
117
318
  shader.save('my-frame');
118
319
  ```
119
320
 
120
- ## Included uniforms
321
+ #### face
121
322
 
122
- | Uniform | Type | Description |
123
- | -------------- | -------------- | ------------------------------------------------------------------------------- |
124
- | `u_time` | float | The current time in seconds. |
125
- | `u_frame` | int | The current frame number. |
126
- | `u_resolution` | float[2] | The canvas element's dimensions. |
127
- | `u_cursor` | float[2] | Cursor position (x, y). |
128
- | `u_click` | float[3] | Click position (x, y) and left click state (z). |
129
- | `u_history` | sampler2DArray | Buffer texture of prior frames. Only available if the `history` plugin is used. |
323
+ The `face` plugin uses [MediaPipe](https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker) to detect faces in video or image textures.
130
324
 
131
- ## Included varyings
325
+ ```typescript
326
+ import ShaderPad from 'shaderpad';
327
+ import { face } from 'shaderpad/plugins/face';
328
+
329
+ const shader = new ShaderPad(fragmentShaderSrc, {
330
+ plugins: [
331
+ face({
332
+ textureName: 'u_webcam',
333
+ options: { maxFaces: 3 },
334
+ }),
335
+ ],
336
+ });
337
+ ```
132
338
 
133
- | Varying | Type | Description |
134
- | ------- | -------- | ----------------------------------- |
135
- | `v_uv` | float[2] | The UV coordinates of the fragment. |
339
+ **Uniforms:**
340
+
341
+ | Uniform | Type | Description |
342
+ | -------------- | -------------- | ---------------------------------------------- |
343
+ | `u_maxFaces` | int | Maximum number of faces to detect |
344
+ | `u_nFaces` | int | Current number of detected faces |
345
+ | `u_faceCenter` | vec2[maxFaces] | Face center positions |
346
+ | `u_leftEye` | vec2[maxFaces] | Left eye positions |
347
+ | `u_rightEye` | vec2[maxFaces] | Right eye positions |
348
+ | `u_noseTip` | vec2[maxFaces] | Nose tip positions |
349
+ | `u_faceMask` | sampler2D | Face mask texture (R: mouth, G: face, B: eyes) |
350
+
351
+ **Helper functions:** `getFace(vec2 pos)`, `getEye(vec2 pos)`, `getMouth(vec2 pos)`
352
+
353
+ **Note:** The face plugin requires `@mediapipe/tasks-vision` as a peer dependency.
354
+
355
+ #### pose
356
+
357
+ The `pose` plugin uses [MediaPipe Pose Landmarker](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker) to expose a flat array of 2D landmarks. Each pose contributes 33 landmarks, enumerated below.
358
+
359
+ ```typescript
360
+ import ShaderPad from 'shaderpad';
361
+ import { pose } from 'shaderpad/plugins/pose';
362
+
363
+ const shader = new ShaderPad(fragmentShaderSrc, {
364
+ plugins: [pose({ textureName: 'u_video', options: { maxPoses: 3 } })],
365
+ });
366
+ ```
367
+
368
+ **Uniforms:**
369
+
370
+ | Uniform | Type | Description |
371
+ | ----------------- | ------------------- | ---------------------------------------- |
372
+ | `u_maxPoses` | int | Maximum number of poses to track |
373
+ | `u_nPoses` | int | Current number of detected poses |
374
+ | `u_poseLandmarks` | vec2[maxPoses * 33] | Landmark positions in UV space |
375
+ | `u_poseMask` | sampler2D | Pose mask texture (G: body, B: skeleton) |
376
+
377
+ **Helper functions:** `poseLandmark(int poseIndex, int landmarkIndex)`, `getBody(vec2 pos)`, `getSkeleton(vec2 pos)`
378
+
379
+ Use `poseLandmark(int poseIndex, int landmarkIndex)` in GLSL to retrieve a specific point. Landmark indices are:
380
+
381
+ | Index | Landmark | Index | Landmark |
382
+ | ----- | ----------------- | ----- | ---------------- |
383
+ | 0 | nose | 17 | left pinky |
384
+ | 1 | left eye (inner) | 18 | right pinky |
385
+ | 2 | left eye | 19 | left index |
386
+ | 3 | left eye (outer) | 20 | right index |
387
+ | 4 | right eye (inner) | 21 | left thumb |
388
+ | 5 | right eye | 22 | right thumb |
389
+ | 6 | right eye (outer) | 23 | left hip |
390
+ | 7 | left ear | 24 | right hip |
391
+ | 8 | right ear | 25 | left knee |
392
+ | 9 | mouth (left) | 26 | right knee |
393
+ | 10 | mouth (right) | 27 | left ankle |
394
+ | 11 | left shoulder | 28 | right ankle |
395
+ | 12 | right shoulder | 29 | left heel |
396
+ | 13 | left elbow | 30 | right heel |
397
+ | 14 | right elbow | 31 | left foot index |
398
+ | 15 | left wrist | 32 | right foot index |
399
+ | 16 | right wrist | | |
400
+
401
+ [Source](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#pose_landmarker_model)
402
+
403
+ A minimal fragment shader loop looks like:
404
+
405
+ ```glsl
406
+ int POSE_LANDMARK_LEFT_HIP = 23;
407
+ int POSE_LANDMARK_RIGHT_HIP = 24;
408
+ for (int i = 0; i < u_maxPoses; ++i) {
409
+ if (i >= u_nPoses) break;
410
+ vec2 leftHip = poseLandmark(i, POSE_LANDMARK_LEFT_HIP);
411
+ vec2 rightHip = poseLandmark(i, POSE_LANDMARK_RIGHT_HIP);
412
+ // …
413
+ }
414
+ ```
415
+
416
+ #### hands
417
+
418
+ The `hands` plugin uses [MediaPipe Hand Landmarker](https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker) to expose a flat array of 2D landmarks. Each hand contributes 22 landmarks, enumerated below.
419
+
420
+ ```typescript
421
+ import ShaderPad from 'shaderpad';
422
+ import { hands } from 'shaderpad/plugins/hands';
423
+
424
+ const shader = new ShaderPad(fragmentShaderSrc, {
425
+ plugins: [hands({ textureName: 'u_video', options: { maxHands: 2 } })],
426
+ });
427
+ ```
428
+
429
+ **Uniforms:**
430
+
431
+ | Uniform | Type | Description |
432
+ | ----------------- | ------------------- | -------------------------------- |
433
+ | `u_maxHands` | int | Maximum number of hands to track |
434
+ | `u_nHands` | int | Current number of detected hands |
435
+ | `u_handLandmarks` | vec2[maxHands * 22] | Landmark positions in UV space |
436
+
437
+ **Helper functions:** `handLandmark(int handIndex, int landmarkIndex)`
438
+
439
+ Use `handLandmark(int handIndex, int landmarkIndex)` in GLSL to retrieve a specific point. Landmark indices are:
440
+
441
+ | Index | Landmark | Index | Landmark |
442
+ | ----- | ----------------- | ----- | ----------------- |
443
+ | 0 | WRIST | 11 | MIDDLE_FINGER_DIP |
444
+ | 1 | THUMB_CMC | 12 | MIDDLE_FINGER_TIP |
445
+ | 2 | THUMB_MCP | 13 | RING_FINGER_MCP |
446
+ | 3 | THUMB_IP | 14 | RING_FINGER_PIP |
447
+ | 4 | THUMB_TIP | 15 | RING_FINGER_DIP |
448
+ | 5 | INDEX_FINGER_MCP | 16 | RING_FINGER_TIP |
449
+ | 6 | INDEX_FINGER_PIP | 17 | PINKY_MCP |
450
+ | 7 | INDEX_FINGER_DIP | 18 | PINKY_PIP |
451
+ | 8 | INDEX_FINGER_TIP | 19 | PINKY_DIP |
452
+ | 9 | MIDDLE_FINGER_MCP | 20 | PINKY_TIP |
453
+ | 10 | MIDDLE_FINGER_PIP | 21 | HAND_CENTER |
454
+
455
+ [Source](https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker#models)
456
+
457
+ A minimal fragment shader loop looks like:
458
+
459
+ ```glsl
460
+ int HAND_LANDMARK_WRIST = 0;
461
+ int HAND_LANDMARK_THUMB_TIP = 4;
462
+ int HAND_LANDMARK_INDEX_TIP = 8;
463
+ for (int i = 0; i < u_maxHands; ++i) {
464
+ if (i >= u_nHands) break;
465
+ vec2 wrist = handLandmark(i, HAND_LANDMARK_WRIST);
466
+ vec2 thumbTip = handLandmark(i, HAND_LANDMARK_THUMB_TIP);
467
+ vec2 indexTip = handLandmark(i, HAND_LANDMARK_INDEX_TIP);
468
+ // …
469
+ }
470
+ ```
471
+
472
+ **Note:** The hands plugin requires `@mediapipe/tasks-vision` as a peer dependency.
136
473
 
137
474
  ## Contributing
138
475
 
@@ -149,7 +486,7 @@ npm install
149
486
  npm run dev
150
487
  ```
151
488
 
152
- This will launch a local server. Open the provided URL (usually `http://localhost:5173`) in your browser to view and interact with the examples. Use the select box to view a different example.
489
+ This will launch a local server. Open the provided URL (usually `http://localhost:5173`) in your browser to view and interact with the examples. Use the select box to view different examples.
153
490
 
154
491
  ### Adding an example
155
492
 
package/dist/index.d.mts CHANGED
@@ -1,50 +1,41 @@
1
- type WithHistory<T extends ShaderPad> = T & {
2
- initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement, historyDepth?: number): void;
3
- };
4
- declare function history(framebufferDepth?: number): (shaderPad: ShaderPad, context: PluginContext) => void;
5
-
6
- declare module '../index' {
7
- interface ShaderPad {
8
- save(filename: string): Promise<void>;
9
- }
10
- }
11
- declare function save(): (shaderPad: ShaderPad, context: PluginContext) => void;
12
- type WithSave<T extends ShaderPad> = T & {
13
- save(filename: string): Promise<void>;
14
- };
15
-
16
1
  interface Uniform {
17
2
  type: 'float' | 'int';
18
3
  length: 1 | 2 | 3 | 4;
19
4
  location: WebGLUniformLocation;
5
+ arrayLength?: number;
20
6
  }
21
7
  interface Texture {
22
8
  texture: WebGLTexture;
23
9
  unitIndex: number;
24
- historyDepth?: number;
25
- writeIndex?: number;
10
+ width: number;
11
+ height: number;
12
+ history?: {
13
+ depth: number;
14
+ writeIndex: number;
15
+ };
26
16
  }
27
- type TextureSource = HTMLImageElement | HTMLVideoElement;
17
+ type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
28
18
  interface PluginContext {
29
19
  gl: WebGL2RenderingContext;
30
20
  uniforms: Map<string, Uniform>;
31
- textures: Map<string, Texture>;
21
+ textures: Map<string | symbol, Texture>;
32
22
  get program(): WebGLProgram | null;
33
23
  canvas: HTMLCanvasElement;
34
- reserveTextureUnit: (name: string) => number;
35
- releaseTextureUnit: (name: string) => void;
24
+ reserveTextureUnit: (name: string | symbol) => number;
25
+ releaseTextureUnit: (name: string | symbol) => void;
26
+ injectGLSL: (code: string) => void;
36
27
  }
37
28
  type Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;
38
- type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset';
29
+ type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset' | 'initializeTexture' | 'updateTextures' | 'initializeUniform' | 'updateUniforms';
39
30
  interface Options {
40
31
  canvas?: HTMLCanvasElement | null;
41
32
  plugins?: Plugin[];
33
+ history?: number;
42
34
  }
43
35
  declare class ShaderPad {
44
36
  private isInternalCanvas;
45
37
  private isTouchDevice;
46
38
  private gl;
47
- private downloadLink;
48
39
  private fragmentShaderSrc;
49
40
  private uniforms;
50
41
  private textures;
@@ -65,6 +56,7 @@ declare class ShaderPad {
65
56
  canvas: HTMLCanvasElement;
66
57
  onResize?: (width: number, height: number) => void;
67
58
  private hooks;
59
+ private historyDepth;
68
60
  constructor(fragmentShaderSrc: string, options?: Options);
69
61
  registerHook(name: LifecycleMethod, fn: Function): void;
70
62
  private init;
@@ -74,18 +66,27 @@ declare class ShaderPad {
74
66
  private handleResize;
75
67
  private addEventListeners;
76
68
  private updateResolution;
77
- initializeUniform(name: string, type: 'float' | 'int', value: number | number[]): void;
78
- updateUniforms(updates: Record<string, number | number[]>): void;
69
+ private reserveTextureUnit;
70
+ private releaseTextureUnit;
71
+ private clearHistoryTextureLayers;
72
+ initializeUniform(name: string, type: 'float' | 'int', value: number | number[] | (number | number[])[], options?: {
73
+ arrayLength?: number;
74
+ }): void;
75
+ updateUniforms(updates: Record<string, number | number[] | (number | number[])[]>, options?: {
76
+ startIndex?: number;
77
+ }): void;
78
+ private createTexture;
79
+ private _initializeTexture;
80
+ initializeTexture(name: string, source: TextureSource, options?: {
81
+ history?: number;
82
+ }): void;
83
+ updateTextures(updates: Record<string, TextureSource>): void;
84
+ private updateTexture;
79
85
  step(time: number): void;
80
86
  play(callback?: (time: number, frame: number) => void): void;
81
87
  pause(): void;
82
88
  reset(): void;
83
- initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement): void;
84
- updateTextures(updates: Record<string, HTMLImageElement | HTMLVideoElement>): void;
85
- save(filename: string): Promise<void>;
86
89
  destroy(): void;
87
- private reserveTextureUnit;
88
- private releaseTextureUnit;
89
90
  }
90
91
 
91
- export { type PluginContext, type TextureSource, type WithHistory, type WithSave, ShaderPad as default, history, save };
92
+ export { type Options, type PluginContext, type TextureSource, ShaderPad as default };
package/dist/index.d.ts CHANGED
@@ -1,50 +1,41 @@
1
- type WithHistory<T extends ShaderPad> = T & {
2
- initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement, historyDepth?: number): void;
3
- };
4
- declare function history(framebufferDepth?: number): (shaderPad: ShaderPad, context: PluginContext) => void;
5
-
6
- declare module '../index' {
7
- interface ShaderPad {
8
- save(filename: string): Promise<void>;
9
- }
10
- }
11
- declare function save(): (shaderPad: ShaderPad, context: PluginContext) => void;
12
- type WithSave<T extends ShaderPad> = T & {
13
- save(filename: string): Promise<void>;
14
- };
15
-
16
1
  interface Uniform {
17
2
  type: 'float' | 'int';
18
3
  length: 1 | 2 | 3 | 4;
19
4
  location: WebGLUniformLocation;
5
+ arrayLength?: number;
20
6
  }
21
7
  interface Texture {
22
8
  texture: WebGLTexture;
23
9
  unitIndex: number;
24
- historyDepth?: number;
25
- writeIndex?: number;
10
+ width: number;
11
+ height: number;
12
+ history?: {
13
+ depth: number;
14
+ writeIndex: number;
15
+ };
26
16
  }
27
- type TextureSource = HTMLImageElement | HTMLVideoElement;
17
+ type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
28
18
  interface PluginContext {
29
19
  gl: WebGL2RenderingContext;
30
20
  uniforms: Map<string, Uniform>;
31
- textures: Map<string, Texture>;
21
+ textures: Map<string | symbol, Texture>;
32
22
  get program(): WebGLProgram | null;
33
23
  canvas: HTMLCanvasElement;
34
- reserveTextureUnit: (name: string) => number;
35
- releaseTextureUnit: (name: string) => void;
24
+ reserveTextureUnit: (name: string | symbol) => number;
25
+ releaseTextureUnit: (name: string | symbol) => void;
26
+ injectGLSL: (code: string) => void;
36
27
  }
37
28
  type Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;
38
- type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset';
29
+ type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset' | 'initializeTexture' | 'updateTextures' | 'initializeUniform' | 'updateUniforms';
39
30
  interface Options {
40
31
  canvas?: HTMLCanvasElement | null;
41
32
  plugins?: Plugin[];
33
+ history?: number;
42
34
  }
43
35
  declare class ShaderPad {
44
36
  private isInternalCanvas;
45
37
  private isTouchDevice;
46
38
  private gl;
47
- private downloadLink;
48
39
  private fragmentShaderSrc;
49
40
  private uniforms;
50
41
  private textures;
@@ -65,6 +56,7 @@ declare class ShaderPad {
65
56
  canvas: HTMLCanvasElement;
66
57
  onResize?: (width: number, height: number) => void;
67
58
  private hooks;
59
+ private historyDepth;
68
60
  constructor(fragmentShaderSrc: string, options?: Options);
69
61
  registerHook(name: LifecycleMethod, fn: Function): void;
70
62
  private init;
@@ -74,18 +66,27 @@ declare class ShaderPad {
74
66
  private handleResize;
75
67
  private addEventListeners;
76
68
  private updateResolution;
77
- initializeUniform(name: string, type: 'float' | 'int', value: number | number[]): void;
78
- updateUniforms(updates: Record<string, number | number[]>): void;
69
+ private reserveTextureUnit;
70
+ private releaseTextureUnit;
71
+ private clearHistoryTextureLayers;
72
+ initializeUniform(name: string, type: 'float' | 'int', value: number | number[] | (number | number[])[], options?: {
73
+ arrayLength?: number;
74
+ }): void;
75
+ updateUniforms(updates: Record<string, number | number[] | (number | number[])[]>, options?: {
76
+ startIndex?: number;
77
+ }): void;
78
+ private createTexture;
79
+ private _initializeTexture;
80
+ initializeTexture(name: string, source: TextureSource, options?: {
81
+ history?: number;
82
+ }): void;
83
+ updateTextures(updates: Record<string, TextureSource>): void;
84
+ private updateTexture;
79
85
  step(time: number): void;
80
86
  play(callback?: (time: number, frame: number) => void): void;
81
87
  pause(): void;
82
88
  reset(): void;
83
- initializeTexture(name: string, source: HTMLImageElement | HTMLVideoElement): void;
84
- updateTextures(updates: Record<string, HTMLImageElement | HTMLVideoElement>): void;
85
- save(filename: string): Promise<void>;
86
89
  destroy(): void;
87
- private reserveTextureUnit;
88
- private releaseTextureUnit;
89
90
  }
90
91
 
91
- export { type PluginContext, type TextureSource, type WithHistory, type WithSave, ShaderPad as default, history, save };
92
+ export { type Options, type PluginContext, type TextureSource, ShaderPad as default };