@stevejtrettel/shader-sandbox 0.1.3 → 0.1.4

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.
Files changed (113) hide show
  1. package/README.md +220 -23
  2. package/bin/cli.js +106 -14
  3. package/dist-lib/app/App.d.ts +143 -15
  4. package/dist-lib/app/App.d.ts.map +1 -1
  5. package/dist-lib/app/App.js +1343 -108
  6. package/dist-lib/app/app.css +349 -24
  7. package/dist-lib/app/types.d.ts +48 -5
  8. package/dist-lib/app/types.d.ts.map +1 -1
  9. package/dist-lib/editor/EditorPanel.d.ts +2 -2
  10. package/dist-lib/editor/EditorPanel.d.ts.map +1 -1
  11. package/dist-lib/editor/EditorPanel.js +1 -1
  12. package/dist-lib/editor/editor-panel.css +55 -32
  13. package/dist-lib/editor/prism-editor.css +16 -16
  14. package/dist-lib/embed.js +1 -1
  15. package/dist-lib/engine/{ShadertoyEngine.d.ts → ShaderEngine.d.ts} +134 -10
  16. package/dist-lib/engine/ShaderEngine.d.ts.map +1 -0
  17. package/dist-lib/engine/ShaderEngine.js +1523 -0
  18. package/dist-lib/engine/glHelpers.d.ts +24 -0
  19. package/dist-lib/engine/glHelpers.d.ts.map +1 -1
  20. package/dist-lib/engine/glHelpers.js +88 -0
  21. package/dist-lib/engine/std140.d.ts +47 -0
  22. package/dist-lib/engine/std140.d.ts.map +1 -0
  23. package/dist-lib/engine/std140.js +119 -0
  24. package/dist-lib/engine/types.d.ts +55 -5
  25. package/dist-lib/engine/types.d.ts.map +1 -1
  26. package/dist-lib/engine/types.js +1 -1
  27. package/dist-lib/index.d.ts +4 -3
  28. package/dist-lib/index.d.ts.map +1 -1
  29. package/dist-lib/index.js +2 -1
  30. package/dist-lib/layouts/SplitLayout.d.ts +2 -1
  31. package/dist-lib/layouts/SplitLayout.d.ts.map +1 -1
  32. package/dist-lib/layouts/SplitLayout.js +3 -0
  33. package/dist-lib/layouts/TabbedLayout.d.ts.map +1 -1
  34. package/dist-lib/layouts/UILayout.d.ts +55 -0
  35. package/dist-lib/layouts/UILayout.d.ts.map +1 -0
  36. package/dist-lib/layouts/UILayout.js +147 -0
  37. package/dist-lib/layouts/default.css +2 -2
  38. package/dist-lib/layouts/index.d.ts +11 -1
  39. package/dist-lib/layouts/index.d.ts.map +1 -1
  40. package/dist-lib/layouts/index.js +17 -1
  41. package/dist-lib/layouts/split.css +33 -31
  42. package/dist-lib/layouts/tabbed.css +127 -74
  43. package/dist-lib/layouts/types.d.ts +14 -3
  44. package/dist-lib/layouts/types.d.ts.map +1 -1
  45. package/dist-lib/main.js +33 -0
  46. package/dist-lib/project/configHelpers.d.ts +45 -0
  47. package/dist-lib/project/configHelpers.d.ts.map +1 -0
  48. package/dist-lib/project/configHelpers.js +196 -0
  49. package/dist-lib/project/generatedLoader.d.ts +2 -2
  50. package/dist-lib/project/generatedLoader.d.ts.map +1 -1
  51. package/dist-lib/project/generatedLoader.js +23 -5
  52. package/dist-lib/project/loadProject.d.ts +6 -6
  53. package/dist-lib/project/loadProject.d.ts.map +1 -1
  54. package/dist-lib/project/loadProject.js +396 -144
  55. package/dist-lib/project/loaderHelper.d.ts +4 -4
  56. package/dist-lib/project/loaderHelper.d.ts.map +1 -1
  57. package/dist-lib/project/loaderHelper.js +278 -116
  58. package/dist-lib/project/types.d.ts +292 -13
  59. package/dist-lib/project/types.d.ts.map +1 -1
  60. package/dist-lib/project/types.js +13 -1
  61. package/dist-lib/styles/base.css +5 -1
  62. package/dist-lib/uniforms/UniformControls.d.ts +60 -0
  63. package/dist-lib/uniforms/UniformControls.d.ts.map +1 -0
  64. package/dist-lib/uniforms/UniformControls.js +518 -0
  65. package/dist-lib/uniforms/UniformStore.d.ts +74 -0
  66. package/dist-lib/uniforms/UniformStore.d.ts.map +1 -0
  67. package/dist-lib/uniforms/UniformStore.js +145 -0
  68. package/dist-lib/uniforms/UniformsPanel.d.ts +53 -0
  69. package/dist-lib/uniforms/UniformsPanel.d.ts.map +1 -0
  70. package/dist-lib/uniforms/UniformsPanel.js +124 -0
  71. package/dist-lib/uniforms/index.d.ts +11 -0
  72. package/dist-lib/uniforms/index.d.ts.map +1 -0
  73. package/dist-lib/uniforms/index.js +8 -0
  74. package/package.json +1 -1
  75. package/src/app/App.ts +1469 -126
  76. package/src/app/app.css +349 -24
  77. package/src/app/types.ts +53 -5
  78. package/src/editor/EditorPanel.ts +5 -5
  79. package/src/editor/editor-panel.css +55 -32
  80. package/src/editor/prism-editor.css +16 -16
  81. package/src/embed.ts +1 -1
  82. package/src/engine/ShaderEngine.ts +1934 -0
  83. package/src/engine/glHelpers.ts +117 -0
  84. package/src/engine/std140.ts +136 -0
  85. package/src/engine/types.ts +69 -5
  86. package/src/index.ts +4 -3
  87. package/src/layouts/SplitLayout.ts +8 -3
  88. package/src/layouts/TabbedLayout.ts +3 -3
  89. package/src/layouts/UILayout.ts +185 -0
  90. package/src/layouts/default.css +2 -2
  91. package/src/layouts/index.ts +20 -1
  92. package/src/layouts/split.css +33 -31
  93. package/src/layouts/tabbed.css +127 -74
  94. package/src/layouts/types.ts +19 -3
  95. package/src/layouts/ui.css +289 -0
  96. package/src/main.ts +39 -1
  97. package/src/project/configHelpers.ts +225 -0
  98. package/src/project/generatedLoader.ts +27 -6
  99. package/src/project/loadProject.ts +459 -173
  100. package/src/project/loaderHelper.ts +377 -130
  101. package/src/project/types.ts +360 -14
  102. package/src/styles/base.css +5 -1
  103. package/src/styles/theme.css +292 -0
  104. package/src/uniforms/UniformControls.ts +660 -0
  105. package/src/uniforms/UniformStore.ts +166 -0
  106. package/src/uniforms/UniformsPanel.ts +163 -0
  107. package/src/uniforms/index.ts +13 -0
  108. package/src/uniforms/uniform-controls.css +342 -0
  109. package/src/uniforms/uniforms-panel.css +277 -0
  110. package/templates/shaders/example-buffer/config.json +1 -0
  111. package/dist-lib/engine/ShadertoyEngine.d.ts.map +0 -1
  112. package/dist-lib/engine/ShadertoyEngine.js +0 -704
  113. package/src/engine/ShadertoyEngine.ts +0 -929
@@ -11,6 +11,165 @@
11
11
 
12
12
  export type PassName = 'Image' | 'BufferA' | 'BufferB' | 'BufferC' | 'BufferD';
13
13
 
14
+ /**
15
+ * Theme mode for the shader viewer.
16
+ * - 'light': Always use light theme
17
+ * - 'dark': Always use dark theme
18
+ * - 'system': Follow OS preference (prefers-color-scheme)
19
+ */
20
+ export type ThemeMode = 'light' | 'dark' | 'system';
21
+
22
+ // =============================================================================
23
+ // Custom Uniform Definitions
24
+ // =============================================================================
25
+
26
+ /**
27
+ * Supported uniform types for user-defined controls.
28
+ * For now, we start with float only. More types will be added later.
29
+ */
30
+ export type UniformType = 'float' | 'int' | 'bool' | 'vec2' | 'vec3' | 'vec4';
31
+
32
+ /**
33
+ * Base uniform definition shared by all types.
34
+ */
35
+ interface UniformDefinitionBase {
36
+ /** Display label (defaults to uniform name if not provided) */
37
+ label?: string;
38
+ /** If true, uniform is declared but has no UI control (for script-only uniforms) */
39
+ hidden?: boolean;
40
+ }
41
+
42
+ /**
43
+ * Float uniform with slider control.
44
+ */
45
+ export interface FloatUniformDefinition extends UniformDefinitionBase {
46
+ type: 'float';
47
+ value: number;
48
+ min?: number; // Default: 0
49
+ max?: number; // Default: 1
50
+ step?: number; // Default: 0.01
51
+ }
52
+
53
+ /**
54
+ * Integer uniform with discrete slider.
55
+ */
56
+ export interface IntUniformDefinition extends UniformDefinitionBase {
57
+ type: 'int';
58
+ value: number;
59
+ min?: number; // Default: 0
60
+ max?: number; // Default: 10
61
+ step?: number; // Default: 1
62
+ }
63
+
64
+ /**
65
+ * Boolean uniform with toggle control.
66
+ */
67
+ export interface BoolUniformDefinition extends UniformDefinitionBase {
68
+ type: 'bool';
69
+ value: boolean;
70
+ }
71
+
72
+ /**
73
+ * Vec2 uniform (2D position picker).
74
+ */
75
+ export interface Vec2UniformDefinition extends UniformDefinitionBase {
76
+ type: 'vec2';
77
+ value: [number, number];
78
+ min?: [number, number]; // Default: [0, 0]
79
+ max?: [number, number]; // Default: [1, 1]
80
+ }
81
+
82
+ /**
83
+ * Vec3 uniform (color picker or 3D value).
84
+ */
85
+ export interface Vec3UniformDefinition extends UniformDefinitionBase {
86
+ type: 'vec3';
87
+ value: [number, number, number];
88
+ /** If true, use color picker UI. Otherwise use 3 sliders. */
89
+ color?: boolean;
90
+ /** Per-component min (default: [0, 0, 0]) */
91
+ min?: [number, number, number];
92
+ /** Per-component max (default: [1, 1, 1]) */
93
+ max?: [number, number, number];
94
+ /** Per-component step (default: [0.01, 0.01, 0.01]) */
95
+ step?: [number, number, number];
96
+ }
97
+
98
+ /**
99
+ * Vec4 uniform (color with alpha or 4D value).
100
+ */
101
+ export interface Vec4UniformDefinition extends UniformDefinitionBase {
102
+ type: 'vec4';
103
+ value: [number, number, number, number];
104
+ /** If true, use color picker with alpha. Otherwise use 4 sliders. */
105
+ color?: boolean;
106
+ /** Per-component min (default: [0, 0, 0, 0]) */
107
+ min?: [number, number, number, number];
108
+ /** Per-component max (default: [1, 1, 1, 1]) */
109
+ max?: [number, number, number, number];
110
+ /** Per-component step (default: [0.01, 0.01, 0.01, 0.01]) */
111
+ step?: [number, number, number, number];
112
+ }
113
+
114
+ /**
115
+ * Array uniform types supported in UBOs.
116
+ */
117
+ export type ArrayUniformType = 'float' | 'vec2' | 'vec3' | 'vec4' | 'mat3' | 'mat4';
118
+
119
+ /**
120
+ * Array uniform backed by a Uniform Buffer Object (UBO).
121
+ * Data is provided from JavaScript via setUniformValue().
122
+ * The engine auto-injects the layout(std140) uniform block into the shader.
123
+ */
124
+ export interface ArrayUniformDefinition extends UniformDefinitionBase {
125
+ type: ArrayUniformType;
126
+ /** Number of elements in the array */
127
+ count: number;
128
+ }
129
+
130
+ /**
131
+ * Type guard: returns true if a uniform definition is an array uniform (has count).
132
+ */
133
+ export function isArrayUniform(def: UniformDefinition): def is ArrayUniformDefinition {
134
+ return 'count' in def && typeof (def as any).count === 'number';
135
+ }
136
+
137
+ /**
138
+ * Returns true if a uniform should have a UI control.
139
+ * Excludes array uniforms (UBOs) and hidden uniforms (script-only).
140
+ */
141
+ export function hasUIControl(def: UniformDefinition): boolean {
142
+ return !isArrayUniform(def) && !def.hidden;
143
+ }
144
+
145
+ /**
146
+ * Union of all uniform definition types.
147
+ */
148
+ export type UniformDefinition =
149
+ | FloatUniformDefinition
150
+ | IntUniformDefinition
151
+ | BoolUniformDefinition
152
+ | Vec2UniformDefinition
153
+ | Vec3UniformDefinition
154
+ | Vec4UniformDefinition
155
+ | ArrayUniformDefinition;
156
+
157
+ /**
158
+ * Map of uniform names to their definitions.
159
+ */
160
+ export type UniformDefinitions = Record<string, UniformDefinition>;
161
+
162
+ /**
163
+ * A single uniform value at runtime.
164
+ */
165
+ export type UniformValue = number | boolean | number[] | Float32Array;
166
+
167
+ /**
168
+ * Runtime uniform values (current state).
169
+ * Keys are uniform names, values are the current value.
170
+ */
171
+ export type UniformValues = Record<string, UniformValue>;
172
+
14
173
  // =============================================================================
15
174
  // Channel Definitions (JSON Config Format)
16
175
  // =============================================================================
@@ -42,22 +201,60 @@ export interface ChannelJSONKeyboard {
42
201
  keyboard: true;
43
202
  }
44
203
 
204
+ /**
205
+ * Reference to audio input (microphone).
206
+ * Provides a 512x2 texture: row 0 = FFT spectrum, row 1 = waveform.
207
+ */
208
+ export interface ChannelJSONAudio {
209
+ audio: true;
210
+ }
211
+
212
+ /**
213
+ * Reference to webcam input.
214
+ */
215
+ export interface ChannelJSONWebcam {
216
+ webcam: true;
217
+ }
218
+
219
+ /**
220
+ * Reference to a video file.
221
+ */
222
+ export interface ChannelJSONVideo {
223
+ video: string; // Path to video file
224
+ }
225
+
226
+ /**
227
+ * Reference to a script-uploaded texture.
228
+ * The texture is created/updated at runtime via engine.updateTexture().
229
+ */
230
+ export interface ChannelJSONScript {
231
+ script: string; // Texture name (matched by script's updateTexture calls)
232
+ }
233
+
45
234
  /**
46
235
  * Union type for channel sources in JSON config (object form).
47
236
  */
48
237
  export type ChannelJSONObject =
49
238
  | ChannelJSONBuffer
50
239
  | ChannelJSONTexture
51
- | ChannelJSONKeyboard;
240
+ | ChannelJSONKeyboard
241
+ | ChannelJSONAudio
242
+ | ChannelJSONWebcam
243
+ | ChannelJSONVideo
244
+ | ChannelJSONScript;
52
245
 
53
246
  /**
54
247
  * Channel value in simplified config format.
55
248
  * Can be a string shorthand or full object:
56
249
  * - "BufferA", "BufferB", etc. → buffer reference
57
250
  * - "keyboard" → keyboard input
251
+ * - "audio" → microphone audio input
252
+ * - "webcam" → webcam video input
58
253
  * - "photo.jpg" (with extension) → texture file
59
254
  * - { buffer: "BufferA" } → explicit buffer with options
60
255
  * - { texture: "photo.jpg", filter: "nearest" } → texture with options
256
+ * - { video: "clip.mp4" } → video file
257
+ * - { script: "myData" } → script-uploaded texture
61
258
  */
62
259
  export type ChannelValue = string | ChannelJSONObject;
63
260
 
@@ -104,6 +301,9 @@ export interface PassConfigSimplified {
104
301
  * }
105
302
  */
106
303
  export interface ShadertoyConfig {
304
+ /** Must be 'shadertoy' to use Shadertoy-compatible iChannel mode. */
305
+ mode: 'shadertoy';
306
+
107
307
  // Metadata (flat, not nested)
108
308
  title?: string;
109
309
  author?: string;
@@ -111,9 +311,18 @@ export interface ShadertoyConfig {
111
311
 
112
312
  // Settings
113
313
  layout?: 'fullscreen' | 'default' | 'split' | 'tabbed';
314
+ theme?: ThemeMode;
114
315
  controls?: boolean;
115
316
  common?: string;
116
317
 
318
+ // Playback settings
319
+ /** Start paused on first frame (default: false) */
320
+ startPaused?: boolean;
321
+
322
+ // Resolution settings
323
+ /** Pixel ratio multiplier (default: window.devicePixelRatio). Use <1 for lower resolution. */
324
+ pixelRatio?: number;
325
+
117
326
  // Passes (at top level)
118
327
  Image?: PassConfigSimplified;
119
328
  BufferA?: PassConfigSimplified;
@@ -122,6 +331,69 @@ export interface ShadertoyConfig {
122
331
  BufferD?: PassConfigSimplified;
123
332
  }
124
333
 
334
+ // =============================================================================
335
+ // Standard Mode Config (Named Buffers & Textures)
336
+ // =============================================================================
337
+
338
+ /**
339
+ * Per-buffer configuration in standard mode.
340
+ */
341
+ export interface StandardBufferConfig {
342
+ filter?: 'nearest' | 'linear';
343
+ wrap?: 'clamp' | 'repeat';
344
+ }
345
+
346
+ /**
347
+ * Standard mode config format.
348
+ * Buffers and textures are available everywhere by name.
349
+ * Also supports pass-level configs (Image, BufferA, etc.) for simple cases
350
+ * where named buffers aren't needed.
351
+ */
352
+ export interface StandardConfig {
353
+ mode?: 'standard';
354
+
355
+ // Metadata
356
+ title?: string;
357
+ author?: string;
358
+ description?: string;
359
+
360
+ // Settings
361
+ layout?: 'fullscreen' | 'default' | 'split' | 'tabbed';
362
+ theme?: ThemeMode;
363
+ controls?: boolean;
364
+ common?: string;
365
+ startPaused?: boolean;
366
+ pixelRatio?: number;
367
+
368
+ // Custom uniforms
369
+ uniforms?: UniformDefinitions;
370
+
371
+ /**
372
+ * Named buffers (framebuffers with ping-pong).
373
+ * Array shorthand ["velocity"] normalizes to { "velocity": {} }.
374
+ * Max 4 buffers (maps to BufferA-D internally).
375
+ */
376
+ buffers?: string[] | Record<string, StandardBufferConfig>;
377
+
378
+ /**
379
+ * Named textures available in all passes.
380
+ * Value is a file path or special source: "keyboard", "audio", "webcam".
381
+ */
382
+ textures?: Record<string, string>;
383
+
384
+ // Pass-level configs (for standard mode without named buffers)
385
+ Image?: PassConfigSimplified;
386
+ BufferA?: PassConfigSimplified;
387
+ BufferB?: PassConfigSimplified;
388
+ BufferC?: PassConfigSimplified;
389
+ BufferD?: PassConfigSimplified;
390
+ }
391
+
392
+ /**
393
+ * Union of all config formats that can appear in config.json.
394
+ */
395
+ export type ProjectConfig = ShadertoyConfig | StandardConfig;
396
+
125
397
  // =============================================================================
126
398
  // Internal Channel Representation (Normalized)
127
399
  // =============================================================================
@@ -134,7 +406,11 @@ export type ChannelSource =
134
406
  | { kind: 'none' }
135
407
  | { kind: 'buffer'; buffer: PassName; current: boolean }
136
408
  | { kind: 'texture'; name: string; cubemap: boolean } // Internal texture ID (e.g., "tex0")
137
- | { kind: 'keyboard' };
409
+ | { kind: 'keyboard' }
410
+ | { kind: 'audio' }
411
+ | { kind: 'webcam' }
412
+ | { kind: 'video'; src: string }
413
+ | { kind: 'script'; name: string };
138
414
 
139
415
  /**
140
416
  * Exactly 4 channels (iChannel0-3), matching Shadertoy's fixed channel count.
@@ -149,7 +425,7 @@ export type Channels = [ChannelSource, ChannelSource, ChannelSource, ChannelSour
149
425
  * External 2D texture loaded from image file.
150
426
  * Textures are deduplicated by (source, filter, wrap) tuple.
151
427
  */
152
- export interface ShadertoyTexture2D {
428
+ export interface ShaderTexture2D {
153
429
  name: string; // Internal ID (e.g., "tex0", "tex1")
154
430
  filename?: string; // Original filename for display (e.g., "texture.png")
155
431
  source: string; // Path/URL to image file
@@ -164,10 +440,12 @@ export interface ShadertoyTexture2D {
164
440
  /**
165
441
  * A single shader pass in the rendering pipeline.
166
442
  */
167
- export interface ShadertoyPass {
443
+ export interface ShaderPass {
168
444
  name: PassName;
169
445
  glslSource: string; // Full GLSL source code
170
446
  channels: Channels; // iChannel0..3
447
+ /** Named samplers (standard mode). Maps sampler name → source. */
448
+ namedSamplers?: Map<string, ChannelSource>;
171
449
  }
172
450
 
173
451
  // =============================================================================
@@ -177,7 +455,7 @@ export interface ShadertoyPass {
177
455
  /**
178
456
  * Project metadata (title, author, description).
179
457
  */
180
- export interface ShadertoyMeta {
458
+ export interface ShaderMeta {
181
459
  title: string;
182
460
  author: string | null;
183
461
  description: string | null;
@@ -189,7 +467,7 @@ export interface ShadertoyMeta {
189
467
 
190
468
  /**
191
469
  * Complete in-memory representation of a Shadertoy project.
192
- * Produced by loadProject() and consumed by ShadertoyEngine.
470
+ * Produced by loadProject() and consumed by ShaderEngine.
193
471
  *
194
472
  * Guarantees:
195
473
  * - passes.Image always exists
@@ -197,7 +475,13 @@ export interface ShadertoyMeta {
197
475
  * - Textures are deduplicated
198
476
  * - All paths resolved and GLSL loaded
199
477
  */
200
- export interface ShadertoyProject {
478
+ export interface ShaderProject {
479
+ /**
480
+ * Project mode. 'shadertoy' uses iChannel0-3, 'standard' uses named samplers.
481
+ * Defaults to 'standard' if not specified.
482
+ */
483
+ mode: 'shadertoy' | 'standard';
484
+
201
485
  /**
202
486
  * Project root directory path.
203
487
  */
@@ -206,18 +490,37 @@ export interface ShadertoyProject {
206
490
  /**
207
491
  * Project metadata.
208
492
  */
209
- meta: ShadertoyMeta;
493
+ meta: ShaderMeta;
210
494
 
211
495
  /**
212
496
  * Layout mode for the shader viewer.
213
497
  */
214
498
  layout: 'fullscreen' | 'default' | 'split' | 'tabbed';
215
499
 
500
+ /**
501
+ * Theme mode for the shader viewer.
502
+ * Defaults to 'light' if not specified.
503
+ */
504
+ theme: ThemeMode;
505
+
216
506
  /**
217
507
  * Whether to show playback controls (play/pause, reset).
218
508
  */
219
509
  controls: boolean;
220
510
 
511
+ /**
512
+ * Whether to start paused on first frame.
513
+ * Defaults to false.
514
+ */
515
+ startPaused: boolean;
516
+
517
+ /**
518
+ * Pixel ratio for resolution scaling.
519
+ * Defaults to null (use window.devicePixelRatio).
520
+ * Use values < 1 for lower resolution (better performance).
521
+ */
522
+ pixelRatio: number | null;
523
+
221
524
  /**
222
525
  * Common GLSL code (prepended to all shaders), or null if none.
223
526
  */
@@ -228,16 +531,59 @@ export interface ShadertoyProject {
228
531
  * Image is always present, BufferA-D are optional.
229
532
  */
230
533
  passes: {
231
- Image: ShadertoyPass;
232
- BufferA?: ShadertoyPass;
233
- BufferB?: ShadertoyPass;
234
- BufferC?: ShadertoyPass;
235
- BufferD?: ShadertoyPass;
534
+ Image: ShaderPass;
535
+ BufferA?: ShaderPass;
536
+ BufferB?: ShaderPass;
537
+ BufferC?: ShaderPass;
538
+ BufferD?: ShaderPass;
236
539
  };
237
540
 
238
541
  /**
239
542
  * Deduplicated list of external textures.
240
543
  * All ChannelSource with kind: 'texture2D' refer to names in this list.
241
544
  */
242
- textures: ShadertoyTexture2D[];
545
+ textures: ShaderTexture2D[];
546
+
547
+ /**
548
+ * Custom uniform definitions from config.
549
+ * Users must declare these uniforms in their shader code.
550
+ * Array uniforms (with count) are auto-declared by the engine.
551
+ */
552
+ uniforms: UniformDefinitions;
553
+
554
+ /**
555
+ * Demo script hooks (from script.js in demo folder).
556
+ * Provides setup() and onFrame() callbacks for JS-driven computation.
557
+ */
558
+ script: DemoScriptHooks | null;
559
+ }
560
+
561
+ // =============================================================================
562
+ // Demo Script Hooks
563
+ // =============================================================================
564
+
565
+ /**
566
+ * The API surface exposed to script.js hooks.
567
+ * A restricted view of the engine for safety and clarity.
568
+ */
569
+ export interface ScriptEngineAPI {
570
+ setUniformValue(name: string, value: UniformValue): void;
571
+ getUniformValue(name: string): UniformValue | undefined;
572
+ /** Upload or update a named texture for use as a script channel. */
573
+ updateTexture(name: string, width: number, height: number, data: Uint8Array | Float32Array): void;
574
+ /** Read pixels from a buffer pass (previous frame). Returns RGBA Uint8Array. */
575
+ readPixels(passName: string, x: number, y: number, width: number, height: number): Uint8Array;
576
+ readonly width: number;
577
+ readonly height: number;
578
+ }
579
+
580
+ /**
581
+ * Hooks exported by a demo's script.js file.
582
+ * Both are optional — a script can export just setup, just onFrame, or both.
583
+ */
584
+ export interface DemoScriptHooks {
585
+ /** Called once after engine init, before the first frame */
586
+ setup?: (engine: ScriptEngineAPI) => void;
587
+ /** Called every frame before shader execution */
588
+ onFrame?: (engine: ScriptEngineAPI, time: number, deltaTime: number, frame: number) => void;
243
589
  }
@@ -2,6 +2,8 @@
2
2
  * Base Styles - Global resets and root element styling
3
3
  */
4
4
 
5
+ @import './theme.css';
6
+
5
7
  * {
6
8
  margin: 0;
7
9
  padding: 0;
@@ -16,7 +18,9 @@ html, body {
16
18
  }
17
19
 
18
20
  body {
19
- background: #f5f5f5;
21
+ background: var(--bg-primary);
22
+ color: var(--text-primary);
23
+ transition: background-color 0.2s ease, color 0.2s ease;
20
24
  }
21
25
 
22
26
  #app {