@stevejtrettel/shader-sandbox 0.1.0

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 (106) hide show
  1. package/README.md +391 -0
  2. package/bin/cli.js +389 -0
  3. package/dist-lib/app/App.d.ts +134 -0
  4. package/dist-lib/app/App.d.ts.map +1 -0
  5. package/dist-lib/app/App.js +570 -0
  6. package/dist-lib/app/types.d.ts +32 -0
  7. package/dist-lib/app/types.d.ts.map +1 -0
  8. package/dist-lib/app/types.js +6 -0
  9. package/dist-lib/editor/EditorPanel.d.ts +39 -0
  10. package/dist-lib/editor/EditorPanel.d.ts.map +1 -0
  11. package/dist-lib/editor/EditorPanel.js +274 -0
  12. package/dist-lib/editor/prism-editor.css +99 -0
  13. package/dist-lib/editor/prism-editor.d.ts +19 -0
  14. package/dist-lib/editor/prism-editor.d.ts.map +1 -0
  15. package/dist-lib/editor/prism-editor.js +96 -0
  16. package/dist-lib/embed.d.ts +17 -0
  17. package/dist-lib/embed.d.ts.map +1 -0
  18. package/dist-lib/embed.js +35 -0
  19. package/dist-lib/engine/ShadertoyEngine.d.ts +160 -0
  20. package/dist-lib/engine/ShadertoyEngine.d.ts.map +1 -0
  21. package/dist-lib/engine/ShadertoyEngine.js +704 -0
  22. package/dist-lib/engine/glHelpers.d.ts +79 -0
  23. package/dist-lib/engine/glHelpers.d.ts.map +1 -0
  24. package/dist-lib/engine/glHelpers.js +298 -0
  25. package/dist-lib/engine/types.d.ts +77 -0
  26. package/dist-lib/engine/types.d.ts.map +1 -0
  27. package/dist-lib/engine/types.js +7 -0
  28. package/dist-lib/index.d.ts +12 -0
  29. package/dist-lib/index.d.ts.map +1 -0
  30. package/dist-lib/index.js +9 -0
  31. package/dist-lib/layouts/DefaultLayout.d.ts +17 -0
  32. package/dist-lib/layouts/DefaultLayout.d.ts.map +1 -0
  33. package/dist-lib/layouts/DefaultLayout.js +27 -0
  34. package/dist-lib/layouts/FullscreenLayout.d.ts +17 -0
  35. package/dist-lib/layouts/FullscreenLayout.d.ts.map +1 -0
  36. package/dist-lib/layouts/FullscreenLayout.js +27 -0
  37. package/dist-lib/layouts/SplitLayout.d.ts +26 -0
  38. package/dist-lib/layouts/SplitLayout.d.ts.map +1 -0
  39. package/dist-lib/layouts/SplitLayout.js +61 -0
  40. package/dist-lib/layouts/TabbedLayout.d.ts +38 -0
  41. package/dist-lib/layouts/TabbedLayout.d.ts.map +1 -0
  42. package/dist-lib/layouts/TabbedLayout.js +305 -0
  43. package/dist-lib/layouts/index.d.ts +24 -0
  44. package/dist-lib/layouts/index.d.ts.map +1 -0
  45. package/dist-lib/layouts/index.js +36 -0
  46. package/dist-lib/layouts/split.css +196 -0
  47. package/dist-lib/layouts/tabbed.css +345 -0
  48. package/dist-lib/layouts/types.d.ts +48 -0
  49. package/dist-lib/layouts/types.d.ts.map +1 -0
  50. package/dist-lib/layouts/types.js +4 -0
  51. package/dist-lib/main.d.ts +15 -0
  52. package/dist-lib/main.d.ts.map +1 -0
  53. package/dist-lib/main.js +102 -0
  54. package/dist-lib/project/generatedLoader.d.ts +3 -0
  55. package/dist-lib/project/generatedLoader.d.ts.map +1 -0
  56. package/dist-lib/project/generatedLoader.js +17 -0
  57. package/dist-lib/project/loadProject.d.ts +22 -0
  58. package/dist-lib/project/loadProject.d.ts.map +1 -0
  59. package/dist-lib/project/loadProject.js +350 -0
  60. package/dist-lib/project/loaderHelper.d.ts +7 -0
  61. package/dist-lib/project/loaderHelper.d.ts.map +1 -0
  62. package/dist-lib/project/loaderHelper.js +240 -0
  63. package/dist-lib/project/types.d.ts +192 -0
  64. package/dist-lib/project/types.d.ts.map +1 -0
  65. package/dist-lib/project/types.js +7 -0
  66. package/dist-lib/styles/base.css +29 -0
  67. package/package.json +48 -0
  68. package/src/app/App.ts +699 -0
  69. package/src/app/app.css +208 -0
  70. package/src/app/types.ts +36 -0
  71. package/src/editor/EditorPanel.ts +340 -0
  72. package/src/editor/editor-panel.css +175 -0
  73. package/src/editor/prism-editor.css +99 -0
  74. package/src/editor/prism-editor.ts +124 -0
  75. package/src/embed.ts +55 -0
  76. package/src/engine/ShadertoyEngine.ts +929 -0
  77. package/src/engine/glHelpers.ts +432 -0
  78. package/src/engine/types.ts +118 -0
  79. package/src/index.ts +13 -0
  80. package/src/layouts/DefaultLayout.ts +40 -0
  81. package/src/layouts/FullscreenLayout.ts +40 -0
  82. package/src/layouts/SplitLayout.ts +81 -0
  83. package/src/layouts/TabbedLayout.ts +371 -0
  84. package/src/layouts/default.css +22 -0
  85. package/src/layouts/fullscreen.css +15 -0
  86. package/src/layouts/index.ts +44 -0
  87. package/src/layouts/split.css +196 -0
  88. package/src/layouts/tabbed.css +345 -0
  89. package/src/layouts/types.ts +58 -0
  90. package/src/main.ts +114 -0
  91. package/src/project/generatedLoader.ts +23 -0
  92. package/src/project/loadProject.ts +421 -0
  93. package/src/project/loaderHelper.ts +300 -0
  94. package/src/project/types.ts +243 -0
  95. package/src/styles/base.css +29 -0
  96. package/src/styles/embed.css +14 -0
  97. package/src/vite-env.d.ts +1 -0
  98. package/templates/index.html +28 -0
  99. package/templates/main.ts +126 -0
  100. package/templates/package.json +12 -0
  101. package/templates/shaders/example-buffer/bufferA.glsl +14 -0
  102. package/templates/shaders/example-buffer/config.json +10 -0
  103. package/templates/shaders/example-buffer/image.glsl +5 -0
  104. package/templates/shaders/example-gradient/config.json +4 -0
  105. package/templates/shaders/example-gradient/image.glsl +7 -0
  106. package/templates/vite.config.js +35 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Engine Layer - WebGL Helper Functions
3
+ *
4
+ * Low-level WebGL utilities for shader compilation, texture creation,
5
+ * framebuffer management, and VAO setup.
6
+ *
7
+ * Based on docs/engine-spec.md
8
+ */
9
+ /**
10
+ * Compile a shader of given type from source. Throws on error.
11
+ */
12
+ export declare function createShader(gl: WebGL2RenderingContext, type: GLenum, source: string): WebGLShader;
13
+ /**
14
+ * Link a program from vertex + fragment source strings.
15
+ * Throws with a descriptive error if link fails.
16
+ */
17
+ export declare function createProgramFromSources(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram;
18
+ /**
19
+ * Create a full-screen triangle VAO.
20
+ *
21
+ * Attribute layout:
22
+ * - location 0: vec2 position in clip space.
23
+ *
24
+ * The triangle covers the entire screen using three vertices:
25
+ * (-1, -1), (3, -1), (-1, 3)
26
+ * This ensures all pixels are covered without needing two triangles.
27
+ */
28
+ export declare function createFullscreenTriangleVAO(gl: WebGL2RenderingContext): WebGLVertexArrayObject;
29
+ /**
30
+ * Create a float RGBA texture (no data) for use as a render target.
31
+ * This MUST use an internal format compatible with EXT_color_buffer_float.
32
+ *
33
+ * Per spec: ALL render targets MUST be RGBA32F.
34
+ */
35
+ export declare function createRenderTargetTexture(gl: WebGL2RenderingContext, width: number, height: number): WebGLTexture;
36
+ /**
37
+ * Create a framebuffer with a single color attachment at COLOR_ATTACHMENT0.
38
+ * Throws if framebuffer is not complete.
39
+ */
40
+ export declare function createFramebufferWithColorAttachment(gl: WebGL2RenderingContext, texture: WebGLTexture): WebGLFramebuffer;
41
+ /**
42
+ * Create a 1x1 black float texture for unused channels.
43
+ *
44
+ * Per spec: UNUSED CHANNELS MUST STILL BIND A 1×1 BLACK FLOAT TEXTURE.
45
+ * This prevents NaN/undefined behavior when sampling unused channels.
46
+ */
47
+ export declare function createBlackTexture(gl: WebGL2RenderingContext): WebGLTexture;
48
+ /**
49
+ * Create a 256x3 keyboard state texture.
50
+ *
51
+ * Shadertoy keyboard texture format:
52
+ * - Width: 256 (one column per ASCII keycode)
53
+ * - Height: 3 (3 rows for different data)
54
+ * - Row 0 (sample at y=0.25): Current key state (0.0 = up, 1.0 = down)
55
+ * - Row 1: Unused
56
+ * - Row 2 (sample at y=0.75): Toggle state (flips between 0.0 and 1.0 on each press)
57
+ *
58
+ * Returns the WebGLTexture. Data is initialized to all zeros.
59
+ * Use updateKeyboardTexture() to update the state.
60
+ */
61
+ export declare function createKeyboardTexture(gl: WebGL2RenderingContext): WebGLTexture;
62
+ /**
63
+ * Update keyboard texture with current key states.
64
+ *
65
+ * @param gl WebGL context
66
+ * @param texture The keyboard texture to update
67
+ * @param keyStates Map of keycode -> current state (true = down, false = up)
68
+ * @param toggleStates Map of keycode -> toggle state (0.0 or 1.0)
69
+ */
70
+ export declare function updateKeyboardTexture(gl: WebGL2RenderingContext, texture: WebGLTexture, keyStates: Map<number, boolean>, toggleStates: Map<number, number>): void;
71
+ /**
72
+ * Create a 2D texture from an HTMLImageElement (or ImageBitmap).
73
+ * This is used for project textures (dog.png, noise.png, etc.)
74
+ *
75
+ * NOTE: actual image loading is done by the App; engine just gets an
76
+ * already-loaded image object.
77
+ */
78
+ export declare function createTextureFromImage(gl: WebGL2RenderingContext, image: HTMLImageElement | ImageBitmap, filter: 'nearest' | 'linear', wrap: 'clamp' | 'repeat'): WebGLTexture;
79
+ //# sourceMappingURL=glHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glHelpers.d.ts","sourceRoot":"","sources":["../../src/engine/glHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;GAEG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,sBAAsB,EAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,WAAW,CAiBb;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,sBAAsB,EAC1B,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,GACrB,YAAY,CA6Bd;AAMD;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,sBAAsB,GAAG,sBAAsB,CAuC9F;AAMD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,sBAAsB,EAC1B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,YAAY,CAiCd;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,CAClD,EAAE,EAAE,sBAAsB,EAC1B,OAAO,EAAE,YAAY,GACpB,gBAAgB,CA2BlB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,sBAAsB,GAAG,YAAY,CAkC3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,sBAAsB,GAAG,YAAY,CAoC9E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,sBAAsB,EAC1B,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,IAAI,CAsCN;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,sBAAsB,EAC1B,KAAK,EAAE,gBAAgB,GAAG,WAAW,EACrC,MAAM,EAAE,SAAS,GAAG,QAAQ,EAC5B,IAAI,EAAE,OAAO,GAAG,QAAQ,GACvB,YAAY,CA+Bd"}
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Engine Layer - WebGL Helper Functions
3
+ *
4
+ * Low-level WebGL utilities for shader compilation, texture creation,
5
+ * framebuffer management, and VAO setup.
6
+ *
7
+ * Based on docs/engine-spec.md
8
+ */
9
+ // =============================================================================
10
+ // Shader Compilation
11
+ // =============================================================================
12
+ /**
13
+ * Compile a shader of given type from source. Throws on error.
14
+ */
15
+ export function createShader(gl, type, source) {
16
+ const shader = gl.createShader(type);
17
+ if (!shader) {
18
+ throw new Error('Failed to create shader object');
19
+ }
20
+ gl.shaderSource(shader, source);
21
+ gl.compileShader(shader);
22
+ const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
23
+ if (!success) {
24
+ const infoLog = gl.getShaderInfoLog(shader);
25
+ gl.deleteShader(shader);
26
+ throw new Error(`Shader compilation failed:\n${infoLog}`);
27
+ }
28
+ return shader;
29
+ }
30
+ /**
31
+ * Link a program from vertex + fragment source strings.
32
+ * Throws with a descriptive error if link fails.
33
+ */
34
+ export function createProgramFromSources(gl, vertexSource, fragmentSource) {
35
+ const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
36
+ const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
37
+ const program = gl.createProgram();
38
+ if (!program) {
39
+ throw new Error('Failed to create program object');
40
+ }
41
+ gl.attachShader(program, vertexShader);
42
+ gl.attachShader(program, fragmentShader);
43
+ gl.linkProgram(program);
44
+ const success = gl.getProgramParameter(program, gl.LINK_STATUS);
45
+ if (!success) {
46
+ const infoLog = gl.getProgramInfoLog(program);
47
+ gl.deleteProgram(program);
48
+ gl.deleteShader(vertexShader);
49
+ gl.deleteShader(fragmentShader);
50
+ throw new Error(`Program linking failed:\n${infoLog}`);
51
+ }
52
+ // Clean up shaders (no longer needed after linking)
53
+ gl.detachShader(program, vertexShader);
54
+ gl.detachShader(program, fragmentShader);
55
+ gl.deleteShader(vertexShader);
56
+ gl.deleteShader(fragmentShader);
57
+ return program;
58
+ }
59
+ // =============================================================================
60
+ // Fullscreen Triangle VAO
61
+ // =============================================================================
62
+ /**
63
+ * Create a full-screen triangle VAO.
64
+ *
65
+ * Attribute layout:
66
+ * - location 0: vec2 position in clip space.
67
+ *
68
+ * The triangle covers the entire screen using three vertices:
69
+ * (-1, -1), (3, -1), (-1, 3)
70
+ * This ensures all pixels are covered without needing two triangles.
71
+ */
72
+ export function createFullscreenTriangleVAO(gl) {
73
+ const vao = gl.createVertexArray();
74
+ if (!vao) {
75
+ throw new Error('Failed to create VAO');
76
+ }
77
+ gl.bindVertexArray(vao);
78
+ // Create VBO with triangle vertices
79
+ const positions = new Float32Array([
80
+ -1, -1, // Bottom-left
81
+ 3, -1, // Bottom-right (extends beyond viewport)
82
+ -1, 3, // Top-left (extends beyond viewport)
83
+ ]);
84
+ const vbo = gl.createBuffer();
85
+ if (!vbo) {
86
+ throw new Error('Failed to create VBO');
87
+ }
88
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
89
+ gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
90
+ // Enable and configure vertex attribute at location 0
91
+ gl.enableVertexAttribArray(0);
92
+ gl.vertexAttribPointer(0, // attribute location
93
+ 2, // size (vec2)
94
+ gl.FLOAT, // type
95
+ false, // normalized
96
+ 0, // stride
97
+ 0 // offset
98
+ );
99
+ // Unbind
100
+ gl.bindVertexArray(null);
101
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
102
+ return vao;
103
+ }
104
+ // =============================================================================
105
+ // Texture Creation
106
+ // =============================================================================
107
+ /**
108
+ * Create a float RGBA texture (no data) for use as a render target.
109
+ * This MUST use an internal format compatible with EXT_color_buffer_float.
110
+ *
111
+ * Per spec: ALL render targets MUST be RGBA32F.
112
+ */
113
+ export function createRenderTargetTexture(gl, width, height) {
114
+ const tex = gl.createTexture();
115
+ if (!tex) {
116
+ throw new Error('Failed to create texture');
117
+ }
118
+ gl.bindTexture(gl.TEXTURE_2D, tex);
119
+ // Allocate RGBA32F texture (float format required by Shadertoy spec)
120
+ gl.texImage2D(gl.TEXTURE_2D, 0, // mip level
121
+ gl.RGBA32F, // internal format (32-bit float per channel)
122
+ width, height, 0, // border (must be 0)
123
+ gl.RGBA, // format
124
+ gl.FLOAT, // type
125
+ null // no data (allocate only)
126
+ );
127
+ // Set filtering to NEAREST (common for simulation/compute shaders)
128
+ // This prevents interpolation artifacts in PDE/physics shaders
129
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
130
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
131
+ // Set wrap mode to CLAMP_TO_EDGE (prevent wrap-around at boundaries)
132
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
133
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
134
+ gl.bindTexture(gl.TEXTURE_2D, null);
135
+ return tex;
136
+ }
137
+ /**
138
+ * Create a framebuffer with a single color attachment at COLOR_ATTACHMENT0.
139
+ * Throws if framebuffer is not complete.
140
+ */
141
+ export function createFramebufferWithColorAttachment(gl, texture) {
142
+ const fbo = gl.createFramebuffer();
143
+ if (!fbo) {
144
+ throw new Error('Failed to create framebuffer');
145
+ }
146
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
147
+ // Attach texture to color attachment 0
148
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0 // mip level
149
+ );
150
+ // Check framebuffer completeness
151
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
152
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
153
+ gl.deleteFramebuffer(fbo);
154
+ throw new Error(`Framebuffer incomplete: ${getFramebufferStatusString(gl, status)}`);
155
+ }
156
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
157
+ return fbo;
158
+ }
159
+ /**
160
+ * Create a 1x1 black float texture for unused channels.
161
+ *
162
+ * Per spec: UNUSED CHANNELS MUST STILL BIND A 1×1 BLACK FLOAT TEXTURE.
163
+ * This prevents NaN/undefined behavior when sampling unused channels.
164
+ */
165
+ export function createBlackTexture(gl) {
166
+ const tex = gl.createTexture();
167
+ if (!tex) {
168
+ throw new Error('Failed to create black texture');
169
+ }
170
+ gl.bindTexture(gl.TEXTURE_2D, tex);
171
+ // 1x1 black pixel [0, 0, 0, 1] as float
172
+ const blackPixel = new Float32Array([0, 0, 0, 1]);
173
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, 1, 1, 0, gl.RGBA, gl.FLOAT, blackPixel);
174
+ // Set filtering
175
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
176
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
177
+ // Set wrap mode
178
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
179
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
180
+ gl.bindTexture(gl.TEXTURE_2D, null);
181
+ return tex;
182
+ }
183
+ /**
184
+ * Create a 256x3 keyboard state texture.
185
+ *
186
+ * Shadertoy keyboard texture format:
187
+ * - Width: 256 (one column per ASCII keycode)
188
+ * - Height: 3 (3 rows for different data)
189
+ * - Row 0 (sample at y=0.25): Current key state (0.0 = up, 1.0 = down)
190
+ * - Row 1: Unused
191
+ * - Row 2 (sample at y=0.75): Toggle state (flips between 0.0 and 1.0 on each press)
192
+ *
193
+ * Returns the WebGLTexture. Data is initialized to all zeros.
194
+ * Use updateKeyboardTexture() to update the state.
195
+ */
196
+ export function createKeyboardTexture(gl) {
197
+ const tex = gl.createTexture();
198
+ if (!tex) {
199
+ throw new Error('Failed to create keyboard texture');
200
+ }
201
+ gl.bindTexture(gl.TEXTURE_2D, tex);
202
+ // 256x3 texture, all zeros initially
203
+ const width = 256;
204
+ const height = 3;
205
+ const data = new Float32Array(width * height * 4); // RGBA, all zeros
206
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, data);
207
+ // NEAREST filtering - no interpolation between keys!
208
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
209
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
210
+ // CLAMP to edge
211
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
212
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
213
+ gl.bindTexture(gl.TEXTURE_2D, null);
214
+ return tex;
215
+ }
216
+ /**
217
+ * Update keyboard texture with current key states.
218
+ *
219
+ * @param gl WebGL context
220
+ * @param texture The keyboard texture to update
221
+ * @param keyStates Map of keycode -> current state (true = down, false = up)
222
+ * @param toggleStates Map of keycode -> toggle state (0.0 or 1.0)
223
+ */
224
+ export function updateKeyboardTexture(gl, texture, keyStates, toggleStates) {
225
+ const width = 256;
226
+ const height = 3;
227
+ const data = new Float32Array(width * height * 4);
228
+ // Fill in key states
229
+ for (let keycode = 0; keycode < 256; keycode++) {
230
+ const isDown = keyStates.get(keycode) || false;
231
+ const toggleValue = toggleStates.get(keycode) || 0.0;
232
+ // Row 0 (y=0): Current key state
233
+ const row0Index = (0 * width + keycode) * 4;
234
+ data[row0Index + 0] = isDown ? 1.0 : 0.0; // R channel
235
+ data[row0Index + 1] = isDown ? 1.0 : 0.0; // G channel (redundant but matches Shadertoy)
236
+ data[row0Index + 2] = isDown ? 1.0 : 0.0; // B channel
237
+ data[row0Index + 3] = 1.0; // A channel
238
+ // Row 1 (y=1): Unused (keep as zeros)
239
+ // Row 2 (y=2): Toggle state
240
+ const row2Index = (2 * width + keycode) * 4;
241
+ data[row2Index + 0] = toggleValue;
242
+ data[row2Index + 1] = toggleValue;
243
+ data[row2Index + 2] = toggleValue;
244
+ data[row2Index + 3] = 1.0;
245
+ }
246
+ gl.bindTexture(gl.TEXTURE_2D, texture);
247
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, // x, y offset
248
+ width, height, gl.RGBA, gl.FLOAT, data);
249
+ gl.bindTexture(gl.TEXTURE_2D, null);
250
+ }
251
+ /**
252
+ * Create a 2D texture from an HTMLImageElement (or ImageBitmap).
253
+ * This is used for project textures (dog.png, noise.png, etc.)
254
+ *
255
+ * NOTE: actual image loading is done by the App; engine just gets an
256
+ * already-loaded image object.
257
+ */
258
+ export function createTextureFromImage(gl, image, filter, wrap) {
259
+ const tex = gl.createTexture();
260
+ if (!tex) {
261
+ throw new Error('Failed to create texture');
262
+ }
263
+ gl.bindTexture(gl.TEXTURE_2D, tex);
264
+ // Upload image data
265
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
266
+ // Set filtering
267
+ const glFilter = filter === 'nearest' ? gl.NEAREST : gl.LINEAR;
268
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glFilter);
269
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glFilter);
270
+ // Set wrap mode
271
+ const glWrap = wrap === 'clamp' ? gl.CLAMP_TO_EDGE : gl.REPEAT;
272
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, glWrap);
273
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, glWrap);
274
+ gl.bindTexture(gl.TEXTURE_2D, null);
275
+ return tex;
276
+ }
277
+ // =============================================================================
278
+ // Helper Utilities
279
+ // =============================================================================
280
+ /**
281
+ * Get a human-readable string for framebuffer status.
282
+ */
283
+ function getFramebufferStatusString(gl, status) {
284
+ switch (status) {
285
+ case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
286
+ return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT';
287
+ case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
288
+ return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT';
289
+ case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
290
+ return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS';
291
+ case gl.FRAMEBUFFER_UNSUPPORTED:
292
+ return 'FRAMEBUFFER_UNSUPPORTED';
293
+ case gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
294
+ return 'FRAMEBUFFER_INCOMPLETE_MULTISAMPLE';
295
+ default:
296
+ return `Unknown status: ${status}`;
297
+ }
298
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Engine Layer - Type Definitions
3
+ *
4
+ * Internal types used by ShadertoyEngine for managing WebGL resources.
5
+ * Based on docs/engine-spec.md
6
+ */
7
+ import type { ShadertoyProject, PassName, Channels } from '../project/types';
8
+ /**
9
+ * Options for constructing a ShadertoyEngine.
10
+ *
11
+ * The App is responsible for creating the WebGL2RenderingContext
12
+ * and passing it in.
13
+ */
14
+ export interface EngineOptions {
15
+ gl: WebGL2RenderingContext;
16
+ project: ShadertoyProject;
17
+ }
18
+ /**
19
+ * Per-pass uniform locations and metadata.
20
+ *
21
+ * NOTE: This is separate from RuntimePass so that we can keep
22
+ * the GL program + locations together.
23
+ */
24
+ export interface PassUniformLocations {
25
+ program: WebGLProgram;
26
+ iResolution: WebGLUniformLocation | null;
27
+ iTime: WebGLUniformLocation | null;
28
+ iTimeDelta: WebGLUniformLocation | null;
29
+ iFrame: WebGLUniformLocation | null;
30
+ iMouse: WebGLUniformLocation | null;
31
+ iChannel: (WebGLUniformLocation | null)[];
32
+ }
33
+ /**
34
+ * A runtime representation of a pass:
35
+ * - knows which project pass it corresponds to
36
+ * - owns two textures (current + previous) for ping-pong
37
+ * - owns a framebuffer and VAO for drawing
38
+ */
39
+ export interface RuntimePass {
40
+ name: PassName;
41
+ projectChannels: Channels;
42
+ vao: WebGLVertexArrayObject;
43
+ uniforms: PassUniformLocations;
44
+ framebuffer: WebGLFramebuffer;
45
+ currentTexture: WebGLTexture;
46
+ previousTexture: WebGLTexture;
47
+ }
48
+ /**
49
+ * Runtime representation of an external 2D texture.
50
+ * This corresponds 1:1 to ShadertoyTexture2D from the project.
51
+ */
52
+ export interface RuntimeTexture2D {
53
+ name: string;
54
+ texture: WebGLTexture;
55
+ width: number;
56
+ height: number;
57
+ }
58
+ /**
59
+ * Keyboard texture representation.
60
+ * For v1, you can leave this unimplemented or just stub it out.
61
+ */
62
+ export interface RuntimeKeyboardTexture {
63
+ texture: WebGLTexture;
64
+ width: number;
65
+ height: number;
66
+ }
67
+ /**
68
+ * Engine stats (for optional overlay / debugging).
69
+ */
70
+ export interface EngineStats {
71
+ frame: number;
72
+ time: number;
73
+ deltaTime: number;
74
+ width: number;
75
+ height: number;
76
+ }
77
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAM1B;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,sBAAsB,CAAC;IAC3B,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAMD;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,YAAY,CAAC;IAGtB,WAAW,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACzC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACxC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAGpC,QAAQ,EAAE,CAAC,oBAAoB,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3C;AAMD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,eAAe,EAAE,QAAQ,CAAC;IAE1B,GAAG,EAAE,sBAAsB,CAAC;IAC5B,QAAQ,EAAE,oBAAoB,CAAC;IAE/B,WAAW,EAAE,gBAAgB,CAAC;IAK9B,cAAc,EAAE,YAAY,CAAC;IAC7B,eAAe,EAAE,YAAY,CAAC;CAC/B;AAMD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Engine Layer - Type Definitions
3
+ *
4
+ * Internal types used by ShadertoyEngine for managing WebGL resources.
5
+ * Based on docs/engine-spec.md
6
+ */
7
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shadertoy Runner - Public API
3
+ *
4
+ * This module exports everything needed to create a shader playground.
5
+ */
6
+ import './styles/base.css';
7
+ export { App } from './app/App';
8
+ export { createLayout } from './layouts';
9
+ export { loadDemo } from './project/loaderHelper';
10
+ export type { ShadertoyProject, ShadertoyConfig, PassName } from './project/types';
11
+ export type { RecompileResult, BaseLayout, LayoutMode, LayoutOptions, RecompileHandler } from './layouts/types';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,mBAAmB,CAAC;AAE3B,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACnF,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shadertoy Runner - Public API
3
+ *
4
+ * This module exports everything needed to create a shader playground.
5
+ */
6
+ import './styles/base.css';
7
+ export { App } from './app/App';
8
+ export { createLayout } from './layouts';
9
+ export { loadDemo } from './project/loaderHelper';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Default Layout
3
+ *
4
+ * Centered canvas with rounded corners and drop shadow.
5
+ * Default layout mode for a polished presentation.
6
+ */
7
+ import './default.css';
8
+ import { BaseLayout, LayoutOptions } from './types';
9
+ export declare class DefaultLayout implements BaseLayout {
10
+ private container;
11
+ private root;
12
+ private canvasContainer;
13
+ constructor(opts: LayoutOptions);
14
+ getCanvasContainer(): HTMLElement;
15
+ dispose(): void;
16
+ }
17
+ //# sourceMappingURL=DefaultLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/DefaultLayout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEpD,qBAAa,aAAc,YAAW,UAAU;IAC9C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,eAAe,CAAc;gBAEzB,IAAI,EAAE,aAAa;IAgB/B,kBAAkB,IAAI,WAAW;IAIjC,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Default Layout
3
+ *
4
+ * Centered canvas with rounded corners and drop shadow.
5
+ * Default layout mode for a polished presentation.
6
+ */
7
+ import './default.css';
8
+ export class DefaultLayout {
9
+ constructor(opts) {
10
+ this.container = opts.container;
11
+ // Create root layout container
12
+ this.root = document.createElement('div');
13
+ this.root.className = 'layout-default';
14
+ // Create canvas container
15
+ this.canvasContainer = document.createElement('div');
16
+ this.canvasContainer.className = 'canvas-container';
17
+ // Assemble and append to DOM
18
+ this.root.appendChild(this.canvasContainer);
19
+ this.container.appendChild(this.root);
20
+ }
21
+ getCanvasContainer() {
22
+ return this.canvasContainer;
23
+ }
24
+ dispose() {
25
+ this.container.innerHTML = '';
26
+ }
27
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Fullscreen Layout
3
+ *
4
+ * Canvas fills entire viewport, no padding or styling.
5
+ * Used for immersive shader experiences.
6
+ */
7
+ import './fullscreen.css';
8
+ import { BaseLayout, LayoutOptions } from './types';
9
+ export declare class FullscreenLayout implements BaseLayout {
10
+ private container;
11
+ private root;
12
+ private canvasContainer;
13
+ constructor(opts: LayoutOptions);
14
+ getCanvasContainer(): HTMLElement;
15
+ dispose(): void;
16
+ }
17
+ //# sourceMappingURL=FullscreenLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FullscreenLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/FullscreenLayout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEpD,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,eAAe,CAAc;gBAEzB,IAAI,EAAE,aAAa;IAgB/B,kBAAkB,IAAI,WAAW;IAIjC,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Fullscreen Layout
3
+ *
4
+ * Canvas fills entire viewport, no padding or styling.
5
+ * Used for immersive shader experiences.
6
+ */
7
+ import './fullscreen.css';
8
+ export class FullscreenLayout {
9
+ constructor(opts) {
10
+ this.container = opts.container;
11
+ // Create root layout container
12
+ this.root = document.createElement('div');
13
+ this.root.className = 'layout-fullscreen';
14
+ // Create canvas container
15
+ this.canvasContainer = document.createElement('div');
16
+ this.canvasContainer.className = 'canvas-container';
17
+ // Assemble and append to DOM
18
+ this.root.appendChild(this.canvasContainer);
19
+ this.container.appendChild(this.root);
20
+ }
21
+ getCanvasContainer() {
22
+ return this.canvasContainer;
23
+ }
24
+ dispose() {
25
+ this.container.innerHTML = '';
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Split Layout
3
+ *
4
+ * Shader on left, editable code on right with syntax highlighting.
5
+ * Ideal for teaching and presentations where viewers can tweak the code.
6
+ */
7
+ import './split.css';
8
+ import { BaseLayout, LayoutOptions, RecompileHandler } from './types';
9
+ export declare class SplitLayout implements BaseLayout {
10
+ private container;
11
+ private project;
12
+ private root;
13
+ private canvasContainer;
14
+ private codePanel;
15
+ private editorPanel;
16
+ private recompileHandler;
17
+ constructor(opts: LayoutOptions);
18
+ getCanvasContainer(): HTMLElement;
19
+ setRecompileHandler(handler: RecompileHandler): void;
20
+ dispose(): void;
21
+ /**
22
+ * Build editor panel (dynamically loaded).
23
+ */
24
+ private buildEditorPanel;
25
+ }
26
+ //# sourceMappingURL=SplitLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SplitLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/SplitLayout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGtE,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,eAAe,CAAc;IACrC,OAAO,CAAC,SAAS,CAAc;IAE/B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,gBAAgB,CAAiC;gBAE7C,IAAI,EAAE,aAAa;IAyB/B,kBAAkB,IAAI,WAAW;IAIjC,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAOpD,OAAO,IAAI,IAAI;IAQf;;OAEG;YACW,gBAAgB;CAW/B"}