@rosalana/sandbox 0.0.5 → 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.
package/README.md CHANGED
@@ -18,13 +18,13 @@ It's **DX‑friendly**, small, and intentionally minimal — perfect for gradien
18
18
 
19
19
  ### Bundle size comparison
20
20
 
21
- | Library | Minified | Gzipped |
22
- | ----------- | -------- | -------- |
23
- | **Sandbox** | 31 KB | **8 KB** |
24
- | three.js | 694 KB | 175 KB |
25
- | p5.js | 1.1 MB | 351 KB |
21
+ | Library | Minified | Gzipped |
22
+ | ----------- | -------- | --------- |
23
+ | **Sandbox** | 85 KB | **23 KB** |
24
+ | three.js | 694 KB | 175 KB |
25
+ | p5.js | 1.1 MB | 351 KB |
26
26
 
27
- Sandbox is **~22x smaller** than three.js and **~44x smaller** than p5.js.
27
+ Sandbox is **~8x smaller** than three.js and **~15x smaller** than p5.js.
28
28
 
29
29
  It works in both **WebGL1 and WebGL2** contexts, with automatic fallback and detection.
30
30
 
@@ -35,10 +35,7 @@ It works in both **WebGL1 and WebGL2** contexts, with automatic fallback and det
35
35
  - [Playback control](#playback-control)
36
36
  - [Time control](#time-control)
37
37
  - [Static rendering](#static-rendering)
38
- - [Shaders](#shaders)
39
- - [WebGL version detection](#webgl-version-detection)
40
- - [Uniforms](#uniforms)
41
- - [Built‑in uniforms](#built-in-uniforms)
38
+ - [Sandbox Shaders](#sandbox-shaders)
42
39
  - [Hooks](#hooks)
43
40
  - [Self-removing hooks](#self-removing-hooks)
44
41
  - [Chaining](#chaining)
@@ -120,76 +117,177 @@ Or render at a specific time — perfect for deterministic, reproducible output:
120
117
  sandbox.renderAt(1.5);
121
118
  ```
122
119
 
123
- ## Shaders
120
+ ## Sandbox Shaders
124
121
 
125
- Shaders are the only thing you need to provide. Sandbox comes with a default fullscreen vertex shader, so you usually don't need to write one yourself and it automatically switches between WebGL1 and WebGL2 versions depending on your fragment shader.
122
+ Writing GLSL from scratch means a lot of ceremony uniform declarations, copy-pasting utility functions, wiring everything together. Sandbox takes care of the boring parts so you can focus on the shader itself.
126
123
 
127
- Update your fragment shader on the fly:
124
+ The idea is simple: define reusable GLSL snippets as **modules**, then `#import` them with a single line. Sandbox resolves dependencies, declares uniforms, and injects everything into the final shader automatically.
125
+
126
+ ### Writing shaders
127
+
128
+ You only need to provide a fragment shader. Sandbox ships with a default fullscreen vertex shader and automatically matches WebGL versions — so you can focus on the fun part.
128
129
 
129
130
  ```ts
130
131
  sandbox.setFragment(fragmentSource);
131
132
  ```
132
133
 
133
- When you need full control over both vertex and fragment:
134
+ Need full control over both shaders? No problem:
134
135
 
135
136
  ```ts
136
137
  sandbox.setShader(vertexSource, fragmentSource);
137
138
  ```
138
139
 
139
- ### WebGL version detection
140
+ Sandbox detects WebGL version from your code (`#version 300 es` → WebGL2, no directive → WebGL1) and falls back gracefully. You can always check what you're running:
140
141
 
141
- Sandbox figures out which WebGL version you're using by looking at your shader code:
142
+ ```ts
143
+ sandbox.version; // 1 or 2
144
+ ```
142
145
 
143
- - `#version 300 es` → WebGL2
144
- - no version directive → WebGL1
146
+ ### Built-in uniforms
145
147
 
146
- If WebGL2 isn't available, Sandbox falls back to WebGL1 automatically. You can always check what you're running:
148
+ These uniforms are populated automatically every frame. Just use them in your shader no declaration or setup needed:
149
+
150
+ | Uniform | Type | Description |
151
+ | -------------- | ----- | --------------------------- |
152
+ | `u_resolution` | vec2 | Canvas size in pixels |
153
+ | `u_time` | float | Elapsed time (seconds) |
154
+ | `u_delta` | float | Delta time since last frame |
155
+ | `u_mouse` | vec2 | Mouse position on canvas |
156
+ | `u_frame` | int | Frame counter |
157
+
158
+ Built-in uniforms are globally available — even inside imported module functions. They're never namespaced, so `u_time` is always just `u_time`, everywhere.
159
+
160
+ ### Custom uniforms
161
+
162
+ Need your own data in the shader? Declare the uniform in GLSL, then set it from JavaScript:
147
163
 
148
164
  ```ts
149
- sandbox.version; // 1 or 2
165
+ sandbox.setUniform<number>("u_intensity", 0.8);
166
+
167
+ sandbox.setUniforms({
168
+ u_intensity: 0.75,
169
+ u_color: [1, 0.2, 0.3],
170
+ });
171
+ ```
172
+
173
+ Read a value back:
174
+
175
+ ```ts
176
+ const intensity = sandbox.getUniform<number>("u_intensity");
150
177
  ```
151
178
 
152
- ## Uniforms
179
+ Everything is **type-safe** and **chainable**. All numeric values are treated as floats — simple and predictable.
153
180
 
154
- Uniforms are how you feed data into your shader. Sandbox makes them **type‑safe** and **chainable** — no more guessing what went wrong.
181
+ ### Modules
155
182
 
156
- Set a single uniform:
183
+ > [!IMPORTANT]
184
+ > Sandbox's built-in GLSL modules are still in beta and may change at any time.
185
+ > We published them early to get feedback. If you have ideas for improvements, please open an issue or a PR — we'd love your input.
186
+
187
+ Modules are reusable GLSL snippets that you can import into any shader. Sandbox ships with a built-in `"sandbox"` module, and you can define your own:
157
188
 
158
189
  ```ts
159
- sandbox.setUniform<number>("u_intensity", 0.8);
190
+ Sandbox.defineModule("my_effects", myGLSLSource);
160
191
  ```
161
192
 
162
- Set multiple uniforms at once:
193
+ Then import any function from it:
194
+
195
+ ```glsl
196
+ #import bloom from "my_effects"
197
+ #import hex from "sandbox"
198
+
199
+ void main() {
200
+ vec3 color = hex(0xFF5733);
201
+ fragColor = vec4(bloom(color), 1.0);
202
+ }
203
+ ```
204
+
205
+ When you import a function, Sandbox pulls in everything it needs — the function body, any helper functions it calls, and any required uniforms. Each import is fully isolated, so importing the same function twice won't cause conflicts.
206
+
207
+ > [!NOTE]
208
+ > All imported code is namespaced automatically to avoid naming collisions.
209
+
210
+ Module names starting with `"sandbox"` are reserved for built-in modules. Each module can only be defined once — this prevents accidental overwrites.
211
+
212
+ Want to see what's available? Inspect all registered modules at any time:
163
213
 
164
214
  ```ts
165
- sandbox.setUniforms<{
166
- u_intensity: number;
167
- u_color: Vec3;
168
- }>({
169
- u_intensity: 0.75,
170
- u_color: [1, 0.2, 0.3],
215
+ Sandbox.availableModules();
216
+ ```
217
+
218
+ You can also preview how your shader will look after processing:
219
+
220
+ ```ts
221
+ Sandbox.compile(shaderSource);
222
+ ```
223
+
224
+ This is useful for debugging or precompiling shaders before deploying.
225
+
226
+ ### Module options
227
+
228
+ Imported functions can expose configurable options — friendly names that map to GLSL uniforms under the hood. You control them from JavaScript using `sandbox.module()`:
229
+
230
+ ```ts
231
+ sandbox.module("effect", {
232
+ intensity: 0.8,
171
233
  });
172
234
  ```
173
235
 
174
- Read back a uniform value:
236
+ This is a powerful way to customize imported effects without touching any GLSL code.
237
+
238
+ #### Hardcoded vs. dynamic values
239
+
240
+ By default, a module's uniforms are only included in the final shader when you actually reference them. This gives you a choice — hardcode a value directly, or use the `@` syntax to wire it up as a configurable uniform:
241
+
242
+ ```glsl
243
+ #import effect from "my_module"
244
+
245
+ void main() {
246
+ vec3 a = effect(v_texcoord, 2.0); // hardcoded value
247
+ vec3 b = effect(v_texcoord, @effect.intensity); // dynamic — set via sandbox.module()
248
+ fragColor = vec4(a + b, 1.0);
249
+ }
250
+ ```
251
+
252
+ The `@effect.intensity` syntax tells Sandbox to inject the uniform and keep it in sync with whatever you set in JavaScript.
253
+
254
+ #### Defining options
255
+
256
+ When defining a module, you declare which options each function supports:
175
257
 
176
258
  ```ts
177
- const intensity = sandbox.getUniform<number>("u_intensity");
259
+ Sandbox.defineModule("my_gradient", gradientSource, {
260
+ myFunc: {
261
+ colors: {
262
+ uniform: "u_colors",
263
+ default: [[1, 0, 0], [0, 0, 1]],
264
+ },
265
+ speed: { uniform: "u_speed", default: 1.0 },
266
+ },
267
+ });
178
268
  ```
179
269
 
180
- All numeric values are treated as floats. This keeps the API simple and predictable.
270
+ Each option has a `uniform` (the GLSL name it maps to) and an optional `default` value applied automatically on import.
181
271
 
182
- ## Built‑in uniforms
272
+ If all functions in your module share the same options, use the `default` key to avoid repetition:
183
273
 
184
- These uniforms are filled automatically every frame — no setup needed. Just declare them in your shader and they work:
274
+ ```ts
275
+ Sandbox.defineModule("my_module", source, {
276
+ default: {
277
+ colors: {
278
+ uniform: "u_colors",
279
+ default: [[1, 0, 0], [0, 0, 1]],
280
+ },
281
+ speed: { uniform: "u_speed", default: 1.0 },
282
+ },
283
+ specialFunc: {
284
+ speed: { uniform: "u_speed", default: 2.0 },
285
+ // "colors" is inherited from default
286
+ },
287
+ });
288
+ ```
185
289
 
186
- | Uniform | Type | Description |
187
- | -------------- | ----- | --------------------------- |
188
- | `u_resolution` | vec2 | Canvas size in pixels |
189
- | `u_time` | float | Elapsed time (seconds) |
190
- | `u_delta` | float | Delta time since last frame |
191
- | `u_mouse` | vec2 | Mouse position on canvas |
192
- | `u_frame` | int | Frame counter |
290
+ Per-function options always take priority over `default` when both define the same key.
193
291
 
194
292
  ## Hooks
195
293
 
@@ -264,11 +362,18 @@ Sandbox.create(canvas, {
264
362
 
265
363
  The error object includes useful details:
266
364
 
267
- - `error.code` — error type (`SHADER_COMPILATION_FAILED`, `PROGRAM_LINK_FAILED`, etc.)
268
- - `error.lines` — line numbers where errors occurred (for compilation errors)
365
+ - `error.code` — error category (see table below)
366
+ - `error.lines` — line numbers where errors occurred (for shader compilation errors)
269
367
  - `error.shaderType` — which shader failed (`vertex` or `fragment`)
270
368
 
271
- Error codes: `WEBGL_NOT_SUPPORTED`, `SHADER_COMPILATION_FAILED`, `PROGRAM_LINK_FAILED`, `SHADER_VERSION_MISMATCH`
369
+ | Code | When |
370
+ | ------------------ | ------------------------------------------------------------------------------------- |
371
+ | `CONTEXT_ERROR` | WebGL not supported or context creation failed |
372
+ | `SHADER_ERROR` | Shader compilation failed, version mismatch, import syntax error, or missing function |
373
+ | `PROGRAM_ERROR` | Shader program linking failed |
374
+ | `VALIDATION_ERROR` | Vertex/fragment shader version mismatch |
375
+ | `MODULE_ERROR` | Module not found, method not found, forbidden name, or duplicate definition |
376
+ | `UNKNOWN_ERROR` | Unexpected error in callbacks (onLoad, hooks) |
272
377
 
273
378
  ## Vue integration
274
379
 
@@ -337,6 +442,7 @@ interface SandboxOptions {
337
442
  onBeforeRender?: HookCallback | null;
338
443
  onAfterRender?: HookCallback | null;
339
444
  uniforms?: UniformSchema;
445
+ modules?: Record<string, Record<string, AnyUniformValue>>;
340
446
  }
341
447
  ```
342
448
 
@@ -355,6 +461,7 @@ interface SandboxOptions {
355
461
  | `onBeforeRender` | — | Hook before each frame |
356
462
  | `onAfterRender` | — | Hook after each frame |
357
463
  | `uniforms` | — | Initial uniform values |
464
+ | `modules` | — | Configure module options per imported function |
358
465
 
359
466
  ## Limitations (by design)
360
467
 
@@ -0,0 +1,6 @@
1
+ export type SandboxErrorCode = "CONTEXT_ERROR" | "VALIDATION_ERROR" | "PROGRAM_ERROR" | "SHADER_ERROR" | "MODULE_ERROR" | "UNKNOWN_ERROR";
2
+ export declare class SandboxError extends Error {
3
+ readonly code: SandboxErrorCode;
4
+ readonly name: string;
5
+ constructor(message: string, code: SandboxErrorCode);
6
+ }
@@ -0,0 +1,7 @@
1
+ import { SandboxError } from "./base";
2
+ export declare class SandboxWebGLNotSupportedError extends SandboxError {
3
+ constructor();
4
+ }
5
+ export declare class SandboxContextCreationError extends SandboxError {
6
+ constructor();
7
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./base";
2
+ export * from "./context";
3
+ export * from "./shader";
4
+ export * from "./module";
5
+ export * from "./program";
6
+ export * from "./unknown";
@@ -0,0 +1,42 @@
1
+ import { SandboxError } from "./base";
2
+ export declare class SandboxModuleNotFoundError extends SandboxError {
3
+ readonly moduleName: string;
4
+ constructor(moduleName: string);
5
+ }
6
+ export declare class SandboxModuleMethodNotFoundError extends SandboxError {
7
+ readonly moduleName: string;
8
+ readonly methodName: string;
9
+ constructor(moduleName: string, methodName: string);
10
+ }
11
+ export declare class SandboxAttemptedToImportMainFunctionError extends SandboxError {
12
+ readonly moduleName: string;
13
+ constructor(moduleName: string);
14
+ }
15
+ export declare class SandboxAttemptedToImportDefaultFunctionError extends SandboxError {
16
+ readonly moduleName: string;
17
+ constructor(moduleName: string);
18
+ }
19
+ export declare class SandboxForbiddenModuleNameError extends SandboxError {
20
+ readonly moduleName: string;
21
+ constructor(moduleName: string);
22
+ }
23
+ export declare class SandboxOverwriteModuleError extends SandboxError {
24
+ readonly moduleName: string;
25
+ constructor(moduleName: string);
26
+ }
27
+ export declare class SandboxMentionUniformNotFoundError extends SandboxError {
28
+ readonly moduleName: string;
29
+ readonly functionName: string;
30
+ readonly uniformName: string;
31
+ constructor(moduleName: string, functionName: string, uniformName: string);
32
+ }
33
+ export declare class SandboxMentionFunctionNotFoundError extends SandboxError {
34
+ readonly functionName: string;
35
+ readonly uniformName: string;
36
+ constructor(functionName: string, uniformName: string);
37
+ }
38
+ export declare class SandboxMentionCouldNotBeReplacedError extends SandboxError {
39
+ readonly mentionName: string;
40
+ readonly calledInFunction: string;
41
+ constructor(mentionName: string, calledInFunction: string);
42
+ }
@@ -0,0 +1,5 @@
1
+ import { SandboxError } from "./base";
2
+ export declare class SandboxProgramError extends SandboxError {
3
+ readonly infoLog: string;
4
+ constructor(infoLog: string);
5
+ }
@@ -0,0 +1,34 @@
1
+ import { SandboxError } from "./base";
2
+ export declare class SandboxShaderVersionMismatchError extends SandboxError {
3
+ readonly vertexVersion: number;
4
+ readonly fragmentVersion: number;
5
+ constructor(vertexVersion: number, fragmentVersion: number);
6
+ }
7
+ export declare class SandboxGLSLShaderCompilationError extends SandboxError {
8
+ readonly shaderType: "vertex" | "fragment";
9
+ readonly source: string;
10
+ readonly infoLog: string;
11
+ readonly lines: number[];
12
+ constructor(shaderType: "vertex" | "fragment", source: string, infoLog: string);
13
+ private static parseErrorLines;
14
+ }
15
+ export declare class SandboxShaderRequirementMismatchError extends SandboxError {
16
+ readonly requirement: "uniform" | "function";
17
+ readonly name: string;
18
+ readonly expectedType: string;
19
+ readonly actualType: string;
20
+ constructor(requirement: "uniform" | "function", name: string, expectedType: string, actualType: string);
21
+ }
22
+ export declare class SandboxShaderWithoutFunctionError extends SandboxError {
23
+ constructor();
24
+ }
25
+ export declare class SandboxShaderImportSyntaxError extends SandboxError {
26
+ readonly line: number;
27
+ readonly details: string;
28
+ constructor(line: number, details: string);
29
+ }
30
+ export declare class SandboxShaderDuplicateImportNameError extends SandboxError {
31
+ readonly name: string;
32
+ readonly line: number;
33
+ constructor(name: string, line: number);
34
+ }
@@ -0,0 +1,7 @@
1
+ import { SandboxError } from "./base";
2
+ export declare class SandboxOnLoadCallbackError extends SandboxError {
3
+ constructor(message: string);
4
+ }
5
+ export declare class SandboxOnHookCallbackError extends SandboxError {
6
+ constructor(id: string, message: string);
7
+ }
@@ -0,0 +1,17 @@
1
+ import ModuleRegistry from "./tools/module_registry";
2
+ /**
3
+ * Default modules bundled with Sandbox.
4
+ * These modules are available for import in shader source without needing to be registered manually.
5
+ * This registry will grow when more modules are defined
6
+ */
7
+ export declare const modules: ModuleRegistry;
8
+ /**
9
+ * A global registry of modules that are currently in use by the webGL context.
10
+ * This is flushed on every shader switch.
11
+ */
12
+ export declare const runtime_modules: ModuleRegistry;
13
+ /**
14
+ * Global uniforms that are automatically provided by Sandbox.
15
+ * These uniforms will NOT be renamed during preprocessing.
16
+ */
17
+ export declare const uniforms: Map<string, import("./types").GLSLType>;