@rosalana/sandbox 0.0.1

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.
@@ -0,0 +1,54 @@
1
+ import type { ClockState, HookCallback } from "../types";
2
+ /**
3
+ * High-precision animation timing using requestAnimationFrame.
4
+ * Tracks elapsed time, delta time, and frame count.
5
+ */
6
+ export default class Clock {
7
+ /** Total elapsed time in seconds */
8
+ time: number;
9
+ /** Delta time since last frame in seconds */
10
+ delta: number;
11
+ /** Frame counter */
12
+ frame: number;
13
+ /** Is clock running */
14
+ running: boolean;
15
+ private startTime;
16
+ private lastTime;
17
+ private rafId;
18
+ private callback;
19
+ constructor();
20
+ /**
21
+ * Start the animation loop with a render callback.
22
+ */
23
+ start(callback: HookCallback): this;
24
+ /**
25
+ * Stop the animation loop.
26
+ * Clock state is preserved for resume.
27
+ */
28
+ stop(): this;
29
+ /**
30
+ * Reset clock to initial state.
31
+ */
32
+ reset(): this;
33
+ /**
34
+ * Get current clock state snapshot.
35
+ */
36
+ getState(): ClockState;
37
+ /**
38
+ * Advance clock by one tick (for single-shot rendering).
39
+ * Useful when autoplay is disabled.
40
+ */
41
+ tick(dt?: number): this;
42
+ /**
43
+ * Set time directly (for deterministic rendering).
44
+ */
45
+ setTime(time: number): this;
46
+ /**
47
+ * Cleanup.
48
+ */
49
+ destroy(): void;
50
+ /**
51
+ * Internal animation frame handler.
52
+ */
53
+ private loop;
54
+ }
@@ -0,0 +1,62 @@
1
+ import type { WebGLContext } from "../types";
2
+ import Program from "./program";
3
+ /**
4
+ * Manages vertex buffers and geometry for rendering.
5
+ * Handles WebGL1 vs WebGL2 VAO differences automatically.
6
+ */
7
+ export default class Geometry {
8
+ private gl;
9
+ private vao;
10
+ private vbo;
11
+ private ibo;
12
+ private vertexCount;
13
+ private indexCount;
14
+ private useIndices;
15
+ private vaoExt;
16
+ private isWebGL2;
17
+ constructor(gl: WebGLContext);
18
+ /**
19
+ * Create a fullscreen quad geometry.
20
+ * This is the most common use case for shader effects.
21
+ */
22
+ static fullscreenQuad(gl: WebGLContext): Geometry;
23
+ /**
24
+ * Setup geometry from vertex and index data.
25
+ */
26
+ setup(vertices: Float32Array, indices?: Uint16Array): this;
27
+ /**
28
+ * Link vertex attributes to shader program.
29
+ * Call this after compiling shaders.
30
+ */
31
+ linkAttributes(program: Program): this;
32
+ /**
33
+ * Bind geometry for rendering.
34
+ */
35
+ bind(): this;
36
+ /**
37
+ * Unbind geometry.
38
+ */
39
+ unbind(): this;
40
+ /**
41
+ * Draw the geometry.
42
+ */
43
+ draw(): this;
44
+ /**
45
+ * Cleanup all GPU resources.
46
+ */
47
+ destroy(): void;
48
+ /**
49
+ * Get position attribute location.
50
+ * Tries common naming conventions.
51
+ */
52
+ private getPositionLocation;
53
+ /**
54
+ * Get texcoord attribute location.
55
+ * Tries common naming conventions.
56
+ */
57
+ private getTexcoordLocation;
58
+ private createVAO;
59
+ private bindVAO;
60
+ private unbindVAO;
61
+ private deleteVAO;
62
+ }
@@ -0,0 +1,12 @@
1
+ import { ClockState, HookCallback } from "../types";
2
+ export default class Hooks {
3
+ private hooks;
4
+ private id;
5
+ /** Add a new hook, returns a removal function */
6
+ add(hook: HookCallback): () => void;
7
+ /** Remove a hook by its ID */
8
+ remove(id: string): void;
9
+ /** Run all hooks with the given state */
10
+ run(state: ClockState): void;
11
+ destroy(): void;
12
+ }
@@ -0,0 +1,11 @@
1
+ type EventMap<T extends EventTarget> = T extends Window ? WindowEventMap : T extends Document ? DocumentEventMap : T extends HTMLElement ? HTMLElementEventMap : Record<string, Event>;
2
+ export default class Listener<T extends EventTarget = EventTarget, K extends keyof EventMap<T> = keyof EventMap<T>> {
3
+ private target;
4
+ private type;
5
+ private listener;
6
+ private options?;
7
+ constructor(target: T, type: K, listener: (event: EventMap<T>[K]) => void, options?: (boolean | AddEventListenerOptions) | undefined);
8
+ remove(): void;
9
+ static on<T extends EventTarget, K extends keyof EventMap<T>>(target: T, type: K, listener: (event: EventMap<T>[K]) => void, options?: AddEventListenerOptions): () => void;
10
+ }
11
+ export {};
@@ -0,0 +1,58 @@
1
+ import type { WebGLContext, WebGLVersion } from "../types";
2
+ /**
3
+ * Manages shader compilation and program linking.
4
+ * Automatically detects WebGL version from shader source.
5
+ */
6
+ export default class Program {
7
+ private gl;
8
+ private program;
9
+ private vertexShader;
10
+ private fragmentShader;
11
+ private version;
12
+ constructor(gl: WebGLContext);
13
+ /**
14
+ * Detect WebGL version from shader source.
15
+ * Looks for "#version 300 es" directive.
16
+ */
17
+ static detectVersion(source: string): WebGLVersion;
18
+ /**
19
+ * Compile shaders and link program.
20
+ * @throws ShaderCompilationError if compilation fails
21
+ * @throws ProgramLinkError if linking fails
22
+ */
23
+ compile(vertexSource: string, fragmentSource: string): this;
24
+ /**
25
+ * Bind this program for rendering.
26
+ */
27
+ use(): this;
28
+ /**
29
+ * Get the compiled WebGL program.
30
+ */
31
+ getProgram(): WebGLProgram | null;
32
+ /**
33
+ * Get detected WebGL version.
34
+ */
35
+ getVersion(): WebGLVersion;
36
+ /**
37
+ * Get attribute location.
38
+ */
39
+ getAttribLocation(name: string): number;
40
+ /**
41
+ * Get uniform location.
42
+ */
43
+ getUniformLocation(name: string): WebGLUniformLocation | null;
44
+ /**
45
+ * Cleanup all GPU resources.
46
+ */
47
+ destroy(): void;
48
+ /**
49
+ * Compile a single shader.
50
+ * @throws ShaderCompilationError if compilation fails
51
+ */
52
+ private compileShader;
53
+ /**
54
+ * Link vertex and fragment shaders into a program.
55
+ * @throws ProgramLinkError if linking fails
56
+ */
57
+ private linkProgram;
58
+ }
@@ -0,0 +1,42 @@
1
+ import type { AnyUniformValue, UniformMethod, WebGLContext } from "../types";
2
+ /**
3
+ * Handles a single uniform variable.
4
+ * Caches WebGL location and infers the correct setter method from value type.
5
+ */
6
+ export default class Uniform {
7
+ readonly name: string;
8
+ readonly method: UniformMethod;
9
+ readonly isArray: boolean;
10
+ readonly isMatrix: boolean;
11
+ private location;
12
+ private locationResolved;
13
+ private value;
14
+ constructor(name: string, value: AnyUniformValue);
15
+ /**
16
+ * Infer WebGL method and metadata from value type.
17
+ */
18
+ private static inferMethodInfo;
19
+ /**
20
+ * Resolve and cache uniform location from program.
21
+ * Returns null if uniform doesn't exist (optimized out by compiler, etc.)
22
+ */
23
+ resolveLocation(gl: WebGLContext, program: WebGLProgram): WebGLUniformLocation | null;
24
+ /**
25
+ * Invalidate cached location (call when program changes).
26
+ */
27
+ invalidateLocation(): void;
28
+ /**
29
+ * Update value (doesn't upload to GPU until upload() is called).
30
+ */
31
+ setValue(value: AnyUniformValue): void;
32
+ /**
33
+ * Get current value.
34
+ */
35
+ getValue(): AnyUniformValue;
36
+ /**
37
+ * Upload current value to GPU.
38
+ * @param gl - WebGL context
39
+ * @param program - Current WebGL program (for location resolution)
40
+ */
41
+ upload(gl: WebGLContext, program: WebGLProgram): void;
42
+ }
@@ -0,0 +1,65 @@
1
+ import type { AnyUniformValue, ClockState, UniformSchema, Vec2, WebGLContext } from "../types";
2
+ /**
3
+ * Manages a collection of uniforms with fluent API.
4
+ * Handles built-in uniforms (u_resolution, u_time, etc.) automatically.
5
+ */
6
+ export default class Uniforms {
7
+ private gl;
8
+ private program;
9
+ private uniforms;
10
+ /** Built-in uniform names that are handled automatically */
11
+ private static readonly BUILT_INS;
12
+ constructor(gl: WebGLContext);
13
+ /**
14
+ * Attach to a WebGL program.
15
+ * Invalidates all cached locations since they're program-specific.
16
+ */
17
+ attachProgram(program: WebGLProgram): this;
18
+ /**
19
+ * Set a uniform value.
20
+ * Creates the uniform if it doesn't exist, updates if it does.
21
+ */
22
+ set<T extends AnyUniformValue>(name: string, value: T): this;
23
+ /**
24
+ * Set multiple uniforms at once.
25
+ */
26
+ setMany<T extends UniformSchema>(values: T): this;
27
+ /**
28
+ * Get current uniform value.
29
+ */
30
+ get<T extends AnyUniformValue>(name: string): T | undefined;
31
+ /**
32
+ * Check if uniform exists.
33
+ */
34
+ has(name: string): boolean;
35
+ /**
36
+ * Remove a uniform.
37
+ */
38
+ delete(name: string): boolean;
39
+ /**
40
+ * Upload all uniforms to GPU.
41
+ * Requires a program to be attached.
42
+ */
43
+ uploadAll(): this;
44
+ /**
45
+ * Upload only built-in uniforms (u_resolution, u_time, u_delta, u_mouse, u_frame).
46
+ * Call this every frame with current values.
47
+ */
48
+ uploadBuiltIns(clock: ClockState, resolution: Vec2, mouse: Vec2): this;
49
+ /**
50
+ * Clear all uniforms.
51
+ */
52
+ clear(): void;
53
+ /**
54
+ * Cleanup.
55
+ */
56
+ destroy(): void;
57
+ /**
58
+ * Get all uniform names.
59
+ */
60
+ keys(): IterableIterator<string>;
61
+ /**
62
+ * Get uniform count.
63
+ */
64
+ get size(): number;
65
+ }
@@ -0,0 +1,93 @@
1
+ import type { AnyUniformValue, ResolvedSandboxOptions, UniformSchema, WebGLContext, WebGLVersion } from "../types";
2
+ import Hooks from "./hooks";
3
+ /**
4
+ * Main WebGL orchestrator.
5
+ * Manages context, program, geometry, uniforms, and rendering loop.
6
+ */
7
+ export default class WebGL {
8
+ private canvas;
9
+ private gl;
10
+ private options;
11
+ onBeforeHooks: Hooks;
12
+ onAfterHooks: Hooks;
13
+ private _program;
14
+ private _geometry;
15
+ private _uniforms;
16
+ private _clock;
17
+ private _resolution;
18
+ private _mouse;
19
+ private _version;
20
+ playing: boolean;
21
+ private constructor();
22
+ /**
23
+ * Factory method to create and setup WebGL instance.
24
+ */
25
+ static setup(canvas: HTMLCanvasElement, options: ResolvedSandboxOptions): WebGL;
26
+ /**
27
+ * Initialize WebGL context.
28
+ * Tries WebGL2 first, falls back to WebGL1.
29
+ * Context errors are fatal but still reported via onError.
30
+ */
31
+ private initContext;
32
+ /**
33
+ * Enable useful WebGL extensions.
34
+ */
35
+ private enableExtensions;
36
+ /**
37
+ * Set viewport dimensions.
38
+ */
39
+ viewport(x: number, y: number, width: number, height: number): this;
40
+ /**
41
+ * Set the clock time
42
+ */
43
+ clock(time: number): this;
44
+ /**
45
+ * Update mouse position.
46
+ */
47
+ mouse(x: number, y: number): this;
48
+ /**
49
+ * Set a uniform value.
50
+ */
51
+ uniform<T extends AnyUniformValue>(name: string, value: T): this;
52
+ /**
53
+ * Set multiple uniforms.
54
+ */
55
+ uniforms<T extends UniformSchema>(values: T): this;
56
+ /**
57
+ * Get current uniform value.
58
+ */
59
+ getUniform<T extends AnyUniformValue>(name: string): T | undefined;
60
+ /**
61
+ * Compile and link shaders.
62
+ * Errors are handled via onError callback, never thrown.
63
+ */
64
+ shader(vertex: string, fragment: string): this;
65
+ /**
66
+ * Start animation loop.
67
+ */
68
+ play(): this;
69
+ /**
70
+ * Stop animation loop.
71
+ */
72
+ pause(): this;
73
+ /**
74
+ * Render a single frame.
75
+ */
76
+ render(): this;
77
+ /**
78
+ * Get WebGL context.
79
+ */
80
+ getContext(): WebGLContext;
81
+ /**
82
+ * Get detected WebGL version.
83
+ */
84
+ getVersion(): WebGLVersion;
85
+ /**
86
+ * Cleanup all resources.
87
+ */
88
+ destroy(): void;
89
+ /**
90
+ * Internal render callback.
91
+ */
92
+ private onRender;
93
+ }
@@ -0,0 +1,111 @@
1
+ import type { SandboxError } from "./errors";
2
+ /** Sandbox configuration options */
3
+ export interface SandboxOptions {
4
+ /** Vertex shader source code */
5
+ vertex?: string;
6
+ /** Fragment shader source code */
7
+ fragment?: string;
8
+ /** Auto-play the sandbox on creation (default: true) */
9
+ autoplay?: boolean;
10
+ /** Pause rendering when canvas not visible (default: true) */
11
+ pauseWhenHidden?: boolean;
12
+ /** Device pixel ratio - "auto" uses window.devicePixelRatio (default: "auto") */
13
+ dpr?: number | "auto";
14
+ /** Preserve drawing buffer for screenshots (default: false) */
15
+ preserveDrawingBuffer?: boolean;
16
+ /** Enable antialiasing (default: true) */
17
+ antialias?: boolean;
18
+ /** Error callback for shader compilation or runtime errors */
19
+ onError?: (error: SandboxError) => void;
20
+ /** Callback when sandbox is ready */
21
+ onLoad?: () => void;
22
+ /** Callback called each frame before render */
23
+ onBeforeRender?: HookCallback | null;
24
+ /** Callback called each frame after render */
25
+ onAfterRender?: HookCallback | null;
26
+ /** Initial uniforms to set */
27
+ uniforms?: UniformSchema;
28
+ }
29
+ /** Resolved sandbox options with all defaults applied */
30
+ export type ResolvedSandboxOptions = Required<SandboxOptions>;
31
+ /** WebGL version (1 = WebGL, 2 = WebGL2) */
32
+ export type WebGLVersion = 1 | 2;
33
+ /** Union of WebGL context types */
34
+ export type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
35
+ /** Vector types as tuples */
36
+ export type Vec2 = [number, number];
37
+ export type Vec3 = [number, number, number];
38
+ export type Vec4 = [number, number, number, number];
39
+ /** Matrix types (column-major, as WebGL expects) */
40
+ export type Mat2 = [number, number, number, number];
41
+ export type Mat3 = [
42
+ number,
43
+ number,
44
+ number,
45
+ number,
46
+ number,
47
+ number,
48
+ number,
49
+ number,
50
+ number
51
+ ];
52
+ export type Mat4 = [
53
+ number,
54
+ number,
55
+ number,
56
+ number,
57
+ number,
58
+ number,
59
+ number,
60
+ number,
61
+ number,
62
+ number,
63
+ number,
64
+ number,
65
+ number,
66
+ number,
67
+ number,
68
+ number
69
+ ];
70
+ /** Single uniform value types (scalars and vectors) */
71
+ export type UniformValue = number | boolean | Vec2 | Vec3 | Vec4 | Mat2 | Mat3 | Mat4;
72
+ /** Array uniform types (for u_colors[10], u_positions[10], etc.) */
73
+ export type UniformArrayValue = number[] | Vec2[] | Vec3[] | Vec4[];
74
+ /** All valid uniform value types */
75
+ export type AnyUniformValue = UniformValue | UniformArrayValue;
76
+ /**
77
+ * Helper type for defining uniform schemas.
78
+ * @example
79
+ * interface GradientUniforms extends UniformSchema {
80
+ * u_time: number;
81
+ * u_resolution: Vec2;
82
+ * u_colors: Vec3[];
83
+ * }
84
+ */
85
+ export interface UniformSchema {
86
+ [key: string]: AnyUniformValue;
87
+ }
88
+ /** WebGL uniform setter method names */
89
+ export type UniformMethod = "uniform1f" | "uniform1i" | "uniform1fv" | "uniform2fv" | "uniform3fv" | "uniform4fv" | "uniformMatrix2fv" | "uniformMatrix3fv" | "uniformMatrix4fv";
90
+ /** Clock state passed to render callbacks */
91
+ export interface ClockState {
92
+ /** Total elapsed time in seconds */
93
+ time: number;
94
+ /** Delta time since last frame in seconds */
95
+ delta: number;
96
+ /** Frame counter */
97
+ frame: number;
98
+ }
99
+ /** Internal uniform entry for caching */
100
+ export interface UniformEntry {
101
+ name: string;
102
+ location: WebGLUniformLocation | null;
103
+ method: UniformMethod;
104
+ value: AnyUniformValue;
105
+ isArray: boolean;
106
+ needsTranspose: boolean;
107
+ }
108
+ /** Geometry draw mode */
109
+ export type DrawMode = "TRIANGLES" | "TRIANGLE_STRIP" | "TRIANGLE_FAN";
110
+ /** Render callback signature */
111
+ export type HookCallback = (clock: ClockState) => void | false;
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@rosalana/sandbox",
3
+ "version": "0.0.1",
4
+ "description": "Lightweight WebGL wrapper for simple, beautiful shader effects",
5
+ "keywords": [
6
+ "webgl",
7
+ "shader",
8
+ "glsl",
9
+ "canvas",
10
+ "graphics",
11
+ "animation",
12
+ "fragment-shader",
13
+ "webgl2",
14
+ "gpu",
15
+ "effects"
16
+ ],
17
+ "private": false,
18
+ "type": "module",
19
+ "main": "./dist/index.cjs.js",
20
+ "module": "./dist/index.es.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "import": "./dist/index.es.js",
25
+ "require": "./dist/index.cjs.js",
26
+ "types": "./dist/index.d.ts"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "dev:vite": "vite build --watch",
34
+ "dev:tsc": "tsc --declaration --emitDeclarationOnly --watch",
35
+ "typecheck": "tsc --noEmit",
36
+ "dev": "npm run build -- --watch",
37
+ "build": "vite build && tsc --declaration --emitDeclarationOnly",
38
+ "clean": "rm -rf dist"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/rosalana/sandbox.git"
43
+ },
44
+ "homepage": "https://github.com/rosalana/sandbox#readme",
45
+ "bugs": {
46
+ "url": "https://github.com/rosalana/sandbox/issues"
47
+ },
48
+ "author": "michaelbany <mobany526@gmail.com>",
49
+ "license": "MIT",
50
+ "devDependencies": {
51
+ "typescript": "^5.4.0",
52
+ "vite": "^5.2.0"
53
+ }
54
+ }