@stevejtrettel/shader-sandbox 0.1.2 → 0.1.3
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 +84 -257
- package/package.json +16 -1
package/README.md
CHANGED
|
@@ -1,105 +1,76 @@
|
|
|
1
1
|
# Shader Sandbox
|
|
2
2
|
|
|
3
|
-
A lightweight, Shadertoy-compatible GLSL shader
|
|
3
|
+
A lightweight, Shadertoy-compatible GLSL shader development environment. Copy shaders directly from Shadertoy and run them locally with live editing.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Shadertoy Compatibility** - Copy/paste shaders directly from Shadertoy
|
|
8
8
|
- **Full Shadertoy Uniforms** - `iTime`, `iResolution`, `iFrame`, `iMouse`, `iTimeDelta`, `iChannel0-3`
|
|
9
9
|
- **Multi-Buffer Rendering** - BufferA-D passes with correct ping-pong semantics
|
|
10
|
-
- **Texture Support** - Load
|
|
10
|
+
- **Texture Support** - Load images with configurable filtering and wrapping
|
|
11
11
|
- **Keyboard Input** - Full keyboard state via Shadertoy-compatible texture
|
|
12
|
+
- **Live Code Editing** - Edit shaders in the browser with instant recompilation
|
|
13
|
+
- **Multiple Layouts** - Fullscreen, split-view, or tabbed code display
|
|
12
14
|
- **Playback Controls** - Play/pause, reset, and screenshot capture
|
|
13
|
-
- **Multiple Layout Modes** - Fullscreen, default, split-view, or tabbed code display
|
|
14
|
-
- **Zero Runtime Dependencies** - Pure WebGL2
|
|
15
|
-
- **Tiny Builds** - ~26KB JS (gzipped)
|
|
16
15
|
|
|
17
16
|
## Quick Start
|
|
18
17
|
|
|
19
18
|
```bash
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
npm run dev:demo my-shader
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Open `http://localhost:3000` to see your shader.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Use as NPM Package
|
|
30
|
-
|
|
31
|
-
Create your own shader collection:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
# Create a new project (does everything in one step)
|
|
35
|
-
npx shader-sandbox create my-shaders
|
|
19
|
+
# Create a new shader project
|
|
20
|
+
npx @stevejtrettel/shader-sandbox create my-shaders
|
|
36
21
|
|
|
37
|
-
#
|
|
22
|
+
# Enter the project
|
|
38
23
|
cd my-shaders
|
|
24
|
+
|
|
25
|
+
# Run an example shader
|
|
39
26
|
shader dev example-gradient
|
|
40
27
|
```
|
|
41
28
|
|
|
42
|
-
|
|
29
|
+
Open http://localhost:3000 to see your shader running.
|
|
43
30
|
|
|
44
|
-
|
|
31
|
+
## CLI Commands
|
|
45
32
|
|
|
46
33
|
```bash
|
|
47
|
-
shader create <name> # Create a new shader project
|
|
48
|
-
shader
|
|
49
|
-
shader list # List all shaders
|
|
50
|
-
shader dev <name> # Run shader in development mode
|
|
34
|
+
shader create <name> # Create a new shader project
|
|
35
|
+
shader dev <name> # Run shader with live reload
|
|
51
36
|
shader build <name> # Build shader for production
|
|
52
37
|
shader new <name> # Create a new shader
|
|
38
|
+
shader list # List all shaders
|
|
39
|
+
shader init # Initialize shaders in current directory
|
|
53
40
|
```
|
|
54
41
|
|
|
55
|
-
|
|
42
|
+
## Project Structure
|
|
56
43
|
|
|
57
|
-
After `shader create`:
|
|
44
|
+
After running `shader create my-shaders`:
|
|
58
45
|
|
|
59
46
|
```
|
|
60
47
|
my-shaders/
|
|
61
48
|
├── shaders/
|
|
62
49
|
│ ├── example-gradient/
|
|
63
|
-
│ │ ├── image.glsl
|
|
64
|
-
│ │ └── config.json
|
|
50
|
+
│ │ ├── image.glsl # Main shader code
|
|
51
|
+
│ │ └── config.json # Optional configuration
|
|
65
52
|
│ └── example-buffer/
|
|
66
|
-
│ ├── image.glsl
|
|
67
|
-
│ ├── bufferA.glsl
|
|
53
|
+
│ ├── image.glsl # Final output
|
|
54
|
+
│ ├── bufferA.glsl # Feedback buffer
|
|
68
55
|
│ └── config.json
|
|
69
|
-
├── main.ts
|
|
70
|
-
├── vite.config.js
|
|
56
|
+
├── main.ts # Entry point
|
|
57
|
+
├── vite.config.js # Vite configuration
|
|
71
58
|
└── package.json
|
|
72
59
|
```
|
|
73
60
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
shader new my-cool-shader
|
|
78
|
-
# Creates shaders/my-cool-shader/image.glsl
|
|
79
|
-
|
|
80
|
-
shader dev my-cool-shader
|
|
81
|
-
# Opens browser with live reload
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
61
|
+
## Creating Shaders
|
|
85
62
|
|
|
86
|
-
|
|
63
|
+
### Simple Shader
|
|
87
64
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
The simplest setup - no config needed.
|
|
65
|
+
Create a new shader with just an image pass:
|
|
91
66
|
|
|
92
67
|
```bash
|
|
93
|
-
|
|
68
|
+
shader new my-shader
|
|
69
|
+
shader dev my-shader
|
|
94
70
|
```
|
|
95
71
|
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
demos/my-shader/
|
|
99
|
-
└── image.glsl
|
|
100
|
-
```
|
|
72
|
+
Edit `shaders/my-shader/image.glsl`:
|
|
101
73
|
|
|
102
|
-
**image.glsl:**
|
|
103
74
|
```glsl
|
|
104
75
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
105
76
|
vec2 uv = fragCoord / iResolution.xy;
|
|
@@ -108,45 +79,26 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
108
79
|
}
|
|
109
80
|
```
|
|
110
81
|
|
|
111
|
-
|
|
82
|
+
### Copy from Shadertoy
|
|
112
83
|
|
|
113
|
-
|
|
84
|
+
1. Find a shader on [Shadertoy](https://www.shadertoy.com)
|
|
85
|
+
2. Copy the code from the "Image" tab
|
|
86
|
+
3. Paste into `shaders/my-shader/image.glsl`
|
|
87
|
+
4. Run `shader dev my-shader`
|
|
114
88
|
|
|
115
|
-
|
|
89
|
+
Most single-pass shaders work immediately. For multi-buffer shaders, you'll need to create the buffer files and config.
|
|
116
90
|
|
|
117
|
-
|
|
118
|
-
npm run new my-shader 1
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Files:**
|
|
122
|
-
```
|
|
123
|
-
demos/my-shader/
|
|
124
|
-
├── bufferA.glsl
|
|
125
|
-
├── image.glsl
|
|
126
|
-
└── config.json
|
|
127
|
-
```
|
|
91
|
+
### Multi-Buffer Shaders
|
|
128
92
|
|
|
129
|
-
|
|
130
|
-
```json
|
|
131
|
-
{
|
|
132
|
-
"BufferA": {
|
|
133
|
-
"iChannel0": "BufferA"
|
|
134
|
-
},
|
|
135
|
-
"Image": {
|
|
136
|
-
"iChannel0": "BufferA"
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
```
|
|
93
|
+
For feedback effects (trails, fluid, etc.), create a buffer:
|
|
140
94
|
|
|
141
|
-
**bufferA.glsl:**
|
|
95
|
+
**shaders/my-effect/bufferA.glsl:**
|
|
142
96
|
```glsl
|
|
143
97
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
144
98
|
vec2 uv = fragCoord / iResolution.xy;
|
|
99
|
+
vec4 prev = texture(iChannel0, uv) * 0.98; // Previous frame with fade
|
|
145
100
|
|
|
146
|
-
//
|
|
147
|
-
vec4 prev = texture(iChannel0, uv) * 0.98;
|
|
148
|
-
|
|
149
|
-
// Draw at mouse
|
|
101
|
+
// Draw at mouse position
|
|
150
102
|
vec2 mouse = iMouse.xy / iResolution.xy;
|
|
151
103
|
float d = length(uv - mouse);
|
|
152
104
|
float spot = smoothstep(0.05, 0.0, d);
|
|
@@ -155,7 +107,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
155
107
|
}
|
|
156
108
|
```
|
|
157
109
|
|
|
158
|
-
**image.glsl:**
|
|
110
|
+
**shaders/my-effect/image.glsl:**
|
|
159
111
|
```glsl
|
|
160
112
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
161
113
|
vec2 uv = fragCoord / iResolution.xy;
|
|
@@ -163,60 +115,23 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
163
115
|
}
|
|
164
116
|
```
|
|
165
117
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
### 3. Multiple Buffers (interacting simulations)
|
|
169
|
-
|
|
170
|
-
For reaction-diffusion, fluid dynamics, etc. All buffers can read all other buffers.
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
npm run new my-shader 2
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Files:**
|
|
177
|
-
```
|
|
178
|
-
demos/my-shader/
|
|
179
|
-
├── bufferA.glsl
|
|
180
|
-
├── bufferB.glsl
|
|
181
|
-
├── image.glsl
|
|
182
|
-
└── config.json
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**config.json:**
|
|
118
|
+
**shaders/my-effect/config.json:**
|
|
186
119
|
```json
|
|
187
120
|
{
|
|
188
121
|
"BufferA": {
|
|
189
|
-
"iChannel0": "BufferA"
|
|
190
|
-
"iChannel1": "BufferB"
|
|
191
|
-
},
|
|
192
|
-
"BufferB": {
|
|
193
|
-
"iChannel0": "BufferA",
|
|
194
|
-
"iChannel1": "BufferB"
|
|
122
|
+
"iChannel0": "BufferA"
|
|
195
123
|
},
|
|
196
124
|
"Image": {
|
|
197
|
-
"iChannel0": "BufferA"
|
|
198
|
-
"iChannel1": "BufferB"
|
|
125
|
+
"iChannel0": "BufferA"
|
|
199
126
|
}
|
|
200
127
|
}
|
|
201
128
|
```
|
|
202
129
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
---
|
|
130
|
+
### Using Textures
|
|
206
131
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
Load an image and process it.
|
|
210
|
-
|
|
211
|
-
**Files:**
|
|
212
|
-
```
|
|
213
|
-
demos/my-shader/
|
|
214
|
-
├── image.glsl
|
|
215
|
-
├── photo.jpg
|
|
216
|
-
└── config.json
|
|
217
|
-
```
|
|
132
|
+
Place an image in your shader folder and reference it in config:
|
|
218
133
|
|
|
219
|
-
**config.json:**
|
|
134
|
+
**shaders/my-shader/config.json:**
|
|
220
135
|
```json
|
|
221
136
|
{
|
|
222
137
|
"Image": {
|
|
@@ -225,166 +140,78 @@ demos/my-shader/
|
|
|
225
140
|
}
|
|
226
141
|
```
|
|
227
142
|
|
|
228
|
-
**image.glsl:**
|
|
143
|
+
**shaders/my-shader/image.glsl:**
|
|
229
144
|
```glsl
|
|
230
145
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
231
146
|
vec2 uv = fragCoord / iResolution.xy;
|
|
232
147
|
vec4 img = texture(iChannel0, uv);
|
|
233
|
-
|
|
234
|
-
// Example: grayscale
|
|
235
|
-
float gray = dot(img.rgb, vec3(0.299, 0.587, 0.114));
|
|
236
|
-
|
|
237
|
-
fragColor = vec4(vec3(gray), 1.0);
|
|
148
|
+
fragColor = img;
|
|
238
149
|
}
|
|
239
150
|
```
|
|
240
151
|
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
{ "texture": "photo.jpg", "filter": "linear", "wrap": "repeat", "type": "2d" }
|
|
244
|
-
```
|
|
245
|
-
- `filter`: `"linear"` (smooth, default) or `"nearest"` (pixelated)
|
|
246
|
-
- `wrap`: `"repeat"` (tile, default) or `"clamp"` (stretch edges)
|
|
247
|
-
- `type`: `"2d"` (standard, default) or `"cubemap"` (equirectangular environment map)
|
|
248
|
-
|
|
249
|
-
**Cubemap textures:** Use `"type": "cubemap"` for equirectangular environment maps (360° panoramas). The engine will automatically convert 3D direction lookups to 2D coordinates:
|
|
250
|
-
```json
|
|
251
|
-
{ "texture": "environment.jpg", "type": "cubemap" }
|
|
252
|
-
```
|
|
253
|
-
```glsl
|
|
254
|
-
// In your shader, sample with a 3D direction:
|
|
255
|
-
vec3 dir = normalize(rayDirection);
|
|
256
|
-
vec4 sky = texture(iChannel0, dir); // Automatically converted
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
---
|
|
260
|
-
|
|
261
|
-
### 5. Texture + Buffers (image + feedback)
|
|
262
|
-
|
|
263
|
-
Combine textures with buffer feedback for effects like painting on an image.
|
|
264
|
-
|
|
265
|
-
**Files:**
|
|
266
|
-
```
|
|
267
|
-
demos/my-shader/
|
|
268
|
-
├── bufferA.glsl
|
|
269
|
-
├── image.glsl
|
|
270
|
-
├── photo.jpg
|
|
271
|
-
└── config.json
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
**config.json:**
|
|
152
|
+
Texture options:
|
|
275
153
|
```json
|
|
276
154
|
{
|
|
277
|
-
"BufferA": {
|
|
278
|
-
"iChannel0": "BufferA",
|
|
279
|
-
"iChannel1": "photo.jpg"
|
|
280
|
-
},
|
|
281
155
|
"Image": {
|
|
282
|
-
"iChannel0":
|
|
283
|
-
|
|
156
|
+
"iChannel0": {
|
|
157
|
+
"texture": "photo.jpg",
|
|
158
|
+
"filter": "linear",
|
|
159
|
+
"wrap": "repeat"
|
|
160
|
+
}
|
|
284
161
|
}
|
|
285
162
|
}
|
|
286
163
|
```
|
|
287
164
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
291
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
292
|
-
|
|
293
|
-
vec4 prev = texture(iChannel0, uv); // Previous frame
|
|
294
|
-
vec4 img = texture(iChannel1, uv); // Original image
|
|
295
|
-
|
|
296
|
-
// Paint with mouse
|
|
297
|
-
vec2 mouse = iMouse.xy / iResolution.xy;
|
|
298
|
-
float d = length(uv - mouse);
|
|
299
|
-
float brush = smoothstep(0.05, 0.0, d);
|
|
300
|
-
|
|
301
|
-
// Blend: painted areas persist, unpainted fade to original
|
|
302
|
-
fragColor = mix(mix(prev, img, 0.01), prev + brush, brush);
|
|
303
|
-
}
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
## Buffer Execution & Frame Timing
|
|
309
|
-
|
|
310
|
-
**Execution order:** BufferA → BufferB → BufferC → BufferD → Image
|
|
311
|
-
|
|
312
|
-
All buffer reads default to the **previous frame**. This is safe for all cases:
|
|
313
|
-
- Self-reference (feedback effects)
|
|
314
|
-
- Reading buffers that haven't run yet this frame
|
|
315
|
-
- Reading buffers that have already run (you get their latest output)
|
|
316
|
-
|
|
317
|
-
Use `{ "buffer": "BufferA", "current": true }` only if you specifically need the in-progress current frame (rare).
|
|
318
|
-
|
|
319
|
-
---
|
|
165
|
+
- `filter`: `"linear"` (smooth) or `"nearest"` (pixelated)
|
|
166
|
+
- `wrap`: `"repeat"` (tile) or `"clamp"` (stretch edges)
|
|
320
167
|
|
|
321
168
|
## Layouts
|
|
322
169
|
|
|
323
|
-
Control how the shader is displayed
|
|
170
|
+
Control how the shader is displayed in `config.json`:
|
|
324
171
|
|
|
325
172
|
```json
|
|
326
173
|
{
|
|
327
|
-
"layout": "split"
|
|
328
|
-
"BufferA": { ... },
|
|
329
|
-
"Image": { ... }
|
|
174
|
+
"layout": "split"
|
|
330
175
|
}
|
|
331
176
|
```
|
|
332
177
|
|
|
333
|
-
| Layout | Description |
|
|
334
|
-
|
|
335
|
-
| `fullscreen` | Canvas fills
|
|
336
|
-
| `default` |
|
|
337
|
-
| `tabbed` | Tabs to switch between shader and code |
|
|
338
|
-
| `split` | Side-by-side
|
|
339
|
-
|
|
340
|
-
**`fullscreen`** - No chrome, canvas fills the screen:
|
|
341
|
-
```json
|
|
342
|
-
{ "layout": "fullscreen" }
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
**`default`** - Clean centered view with rounded corners:
|
|
346
|
-
```json
|
|
347
|
-
{ "layout": "default" }
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
**`tabbed`** - Click tabs to switch between live shader and source code:
|
|
351
|
-
```json
|
|
352
|
-
{ "layout": "tabbed" }
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
**`split`** - See shader and code simultaneously (code panel has tabs for multi-file projects):
|
|
356
|
-
```json
|
|
357
|
-
{ "layout": "split" }
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
---
|
|
178
|
+
| Layout | Description |
|
|
179
|
+
|--------|-------------|
|
|
180
|
+
| `fullscreen` | Canvas fills the viewport |
|
|
181
|
+
| `default` | Centered canvas with controls |
|
|
182
|
+
| `tabbed` | Tabs to switch between shader and code |
|
|
183
|
+
| `split` | Side-by-side shader and code editor |
|
|
361
184
|
|
|
362
185
|
## Keyboard Shortcuts
|
|
363
186
|
|
|
364
187
|
| Key | Action |
|
|
365
188
|
|-----|--------|
|
|
366
189
|
| **S** | Save screenshot (PNG) |
|
|
367
|
-
| **Space** | Play/Pause
|
|
368
|
-
| **R** | Reset to frame 0
|
|
190
|
+
| **Space** | Play/Pause |
|
|
191
|
+
| **R** | Reset to frame 0 |
|
|
369
192
|
|
|
370
|
-
|
|
193
|
+
## Shadertoy Uniforms
|
|
371
194
|
|
|
372
|
-
|
|
195
|
+
All standard Shadertoy uniforms are supported:
|
|
373
196
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
197
|
+
| Uniform | Type | Description |
|
|
198
|
+
|---------|------|-------------|
|
|
199
|
+
| `iResolution` | `vec3` | Viewport resolution (width, height, 1) |
|
|
200
|
+
| `iTime` | `float` | Elapsed time in seconds |
|
|
201
|
+
| `iTimeDelta` | `float` | Time since last frame |
|
|
202
|
+
| `iFrame` | `int` | Frame counter |
|
|
203
|
+
| `iMouse` | `vec4` | Mouse position and click state |
|
|
204
|
+
| `iChannel0-3` | `sampler2D` | Input textures/buffers |
|
|
205
|
+
| `iChannelResolution[4]` | `vec3[]` | Resolution of each channel |
|
|
206
|
+
| `iDate` | `vec4` | Year, month, day, time in seconds |
|
|
379
207
|
|
|
380
|
-
|
|
208
|
+
## Building for Production
|
|
381
209
|
|
|
382
|
-
|
|
210
|
+
```bash
|
|
211
|
+
shader build my-shader
|
|
212
|
+
```
|
|
383
213
|
|
|
384
|
-
|
|
385
|
-
- [Buffers and Channels](docs/learn/buffers-and-channels.md) - Multi-pass rendering
|
|
386
|
-
- [Configuration](docs/learn/configuration.md) - Full config reference
|
|
387
|
-
- [Architecture](docs/dev/architecture.md) - How the engine works
|
|
214
|
+
Output is in `dist/` - a single HTML file with embedded JavaScript that can be hosted anywhere.
|
|
388
215
|
|
|
389
216
|
## License
|
|
390
217
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevejtrettel/shader-sandbox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Local Shadertoy-compatible GLSL shader development environment with live editing",
|
|
5
|
+
"author": "Steve Trettel",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/stevejtrettel/shader-sandbox",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/stevejtrettel/shader-sandbox.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"shader",
|
|
14
|
+
"glsl",
|
|
15
|
+
"shadertoy",
|
|
16
|
+
"webgl",
|
|
17
|
+
"graphics",
|
|
18
|
+
"creative-coding"
|
|
19
|
+
],
|
|
5
20
|
"type": "module",
|
|
6
21
|
"main": "./dist-lib/index.js",
|
|
7
22
|
"types": "./dist-lib/index.d.ts",
|