@viji-dev/core 0.3.24 → 0.3.25
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/dist/artist-js-ambient.d.ts +2 -35
- package/dist/artist-jsdoc.d.ts +406 -13
- package/dist/docs-api.d.ts +7 -0
- package/dist/docs-api.js +83 -35
- package/package.json +1 -1
|
@@ -1,37 +1,4 @@
|
|
|
1
1
|
// JavaScript IntelliSense Support - Auto-generated
|
|
2
2
|
// This file provides type hints for JavaScript without requiring @ts-check
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @fileoverview Viji Artist API - JavaScript IntelliSense Support
|
|
7
|
-
* Auto-generated from TypeScript definitions - DO NOT EDIT MANUALLY
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Global viji object available in artist code
|
|
12
|
-
* @type {VijiAPI}
|
|
13
|
-
*/
|
|
14
|
-
declare const viji;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Type alias for render function
|
|
18
|
-
* @typedef {function(VijiAPI): void} Render
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Type alias for setup function
|
|
23
|
-
* @typedef {function(VijiAPI): void} Setup
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Artist render function - called every frame
|
|
28
|
-
* @param {VijiAPI} viji - The viji API object with all capabilities
|
|
29
|
-
*/
|
|
30
|
-
declare function render(viji): void;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Artist setup function - called once at initialization
|
|
34
|
-
* @param {VijiAPI} viji - The viji API object with all capabilities
|
|
35
|
-
*/
|
|
36
|
-
declare function setup(viji): void;
|
|
37
|
-
|
|
3
|
+
// All types are defined in artist-jsdoc.d.ts — this file re-exports them.
|
|
4
|
+
/// <reference path="artist-jsdoc.d.ts" />
|
package/dist/artist-jsdoc.d.ts
CHANGED
|
@@ -4,29 +4,422 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
7
|
+
* Configuration for slider parameters
|
|
8
|
+
* @typedef {Object} SliderConfig
|
|
9
|
+
* @property {number} [min] - min property
|
|
10
|
+
* @property {number} [max] - max property
|
|
11
|
+
* @property {number} [step] - step property
|
|
12
|
+
* @property {string} label - label property
|
|
13
|
+
* @property {string} [description] - description property
|
|
14
|
+
* @property {string} [group] - group property
|
|
15
|
+
* @property {ParameterCategory} [category] - category property
|
|
9
16
|
*/
|
|
10
|
-
declare const viji;
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @typedef {
|
|
19
|
+
* Configuration for color parameters
|
|
20
|
+
* @typedef {Object} ColorConfig
|
|
21
|
+
* @property {string} label - label property
|
|
22
|
+
* @property {string} [description] - description property
|
|
23
|
+
* @property {string} [group] - group property
|
|
24
|
+
* @property {ParameterCategory} [category] - category property
|
|
15
25
|
*/
|
|
16
26
|
|
|
17
27
|
/**
|
|
18
|
-
*
|
|
19
|
-
* @typedef {
|
|
28
|
+
* Configuration for toggle parameters
|
|
29
|
+
* @typedef {Object} ToggleConfig
|
|
30
|
+
* @property {string} label - label property
|
|
31
|
+
* @property {string} [description] - description property
|
|
32
|
+
* @property {string} [group] - group property
|
|
33
|
+
* @property {ParameterCategory} [category] - category property
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Configuration for select parameters
|
|
38
|
+
* @typedef {Object} SelectConfig
|
|
39
|
+
* @property {string[] | number[]} options - options property
|
|
40
|
+
* @property {string} label - label property
|
|
41
|
+
* @property {string} [description] - description property
|
|
42
|
+
* @property {string} [group] - group property
|
|
43
|
+
* @property {ParameterCategory} [category] - category property
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Configuration for text parameters
|
|
48
|
+
* @typedef {Object} TextConfig
|
|
49
|
+
* @property {string} label - label property
|
|
50
|
+
* @property {string} [description] - description property
|
|
51
|
+
* @property {string} [group] - group property
|
|
52
|
+
* @property {ParameterCategory} [category] - category property
|
|
53
|
+
* @property {number} [maxLength] - maxLength property
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Configuration for number parameters
|
|
58
|
+
* @typedef {Object} NumberConfig
|
|
59
|
+
* @property {number} [min] - min property
|
|
60
|
+
* @property {number} [max] - max property
|
|
61
|
+
* @property {number} [step] - step property
|
|
62
|
+
* @property {string} label - label property
|
|
63
|
+
* @property {string} [description] - description property
|
|
64
|
+
* @property {string} [group] - group property
|
|
65
|
+
* @property {ParameterCategory} [category] - category property
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Configuration for image parameters
|
|
70
|
+
* @typedef {Object} ImageConfig
|
|
71
|
+
* @property {string} label - label property
|
|
72
|
+
* @property {string} [description] - description property
|
|
73
|
+
* @property {string} [group] - group property
|
|
74
|
+
* @property {ParameterCategory} [category] - category property
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Configuration for button parameters
|
|
79
|
+
* @typedef {Object} ButtonConfig
|
|
80
|
+
* @property {string} label - label property
|
|
81
|
+
* @property {string} [description] - description property
|
|
82
|
+
* @property {string} [group] - group property
|
|
83
|
+
* @property {ParameterCategory} [category] - category property
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parameter object for slider parameters
|
|
88
|
+
* @typedef {Object} SliderParameter
|
|
89
|
+
* @property {number} value - value property
|
|
90
|
+
* @property {number} min - min property
|
|
91
|
+
* @property {number} max - max property
|
|
92
|
+
* @property {number} step - step property
|
|
93
|
+
* @property {string} label - label property
|
|
94
|
+
* @property {string} [description] - description property
|
|
95
|
+
* @property {string} group - group property
|
|
96
|
+
* @property {ParameterCategory} category - category property
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Parameter object for color parameters
|
|
101
|
+
* @typedef {Object} ColorParameter
|
|
102
|
+
* @property {string} value - value property
|
|
103
|
+
* @property {string} label - label property
|
|
104
|
+
* @property {string} [description] - description property
|
|
105
|
+
* @property {string} group - group property
|
|
106
|
+
* @property {ParameterCategory} category - category property
|
|
20
107
|
*/
|
|
21
108
|
|
|
22
109
|
/**
|
|
23
|
-
*
|
|
24
|
-
* @
|
|
110
|
+
* Parameter object for toggle parameters
|
|
111
|
+
* @typedef {Object} ToggleParameter
|
|
112
|
+
* @property {boolean} value - value property
|
|
113
|
+
* @property {string} label - label property
|
|
114
|
+
* @property {string} [description] - description property
|
|
115
|
+
* @property {string} group - group property
|
|
116
|
+
* @property {ParameterCategory} category - category property
|
|
25
117
|
*/
|
|
26
|
-
declare function render(viji): void;
|
|
27
118
|
|
|
28
119
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @
|
|
120
|
+
* Parameter object for select parameters
|
|
121
|
+
* @typedef {Object} SelectParameter
|
|
122
|
+
* @property {string | number} value - value property
|
|
123
|
+
* @property {string[] | number[]} options - options property
|
|
124
|
+
* @property {string} label - label property
|
|
125
|
+
* @property {string} [description] - description property
|
|
126
|
+
* @property {string} group - group property
|
|
127
|
+
* @property {ParameterCategory} category - category property
|
|
31
128
|
*/
|
|
32
|
-
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Parameter object for text parameters
|
|
132
|
+
* @typedef {Object} TextParameter
|
|
133
|
+
* @property {string} value - value property
|
|
134
|
+
* @property {number} [maxLength] - maxLength property
|
|
135
|
+
* @property {string} label - label property
|
|
136
|
+
* @property {string} [description] - description property
|
|
137
|
+
* @property {string} group - group property
|
|
138
|
+
* @property {ParameterCategory} category - category property
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parameter object for number parameters
|
|
143
|
+
* @typedef {Object} NumberParameter
|
|
144
|
+
* @property {number} value - value property
|
|
145
|
+
* @property {number} min - min property
|
|
146
|
+
* @property {number} max - max property
|
|
147
|
+
* @property {number} step - step property
|
|
148
|
+
* @property {string} label - label property
|
|
149
|
+
* @property {string} [description] - description property
|
|
150
|
+
* @property {string} group - group property
|
|
151
|
+
* @property {ParameterCategory} category - category property
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Parameter object for image parameters
|
|
156
|
+
* @typedef {Object} ImageParameter
|
|
157
|
+
* @property {ImageBitmap |null} value - value property
|
|
158
|
+
* @property {string} label - label property
|
|
159
|
+
* @property {string} [description] - description property
|
|
160
|
+
* @property {string} group - group property
|
|
161
|
+
* @property {ParameterCategory} category - category property
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parameter object for button parameters
|
|
166
|
+
* @typedef {Object} ButtonParameter
|
|
167
|
+
* @property {boolean} value - value property
|
|
168
|
+
* @property {string} label - label property
|
|
169
|
+
* @property {string} [description] - description property
|
|
170
|
+
* @property {string} group - group property
|
|
171
|
+
* @property {ParameterCategory} category - category property
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Audio analysis API - provides real-time audio data and frequency analysis
|
|
176
|
+
* @typedef {Object} AudioAPI
|
|
177
|
+
* @property {boolean} isConnected - isConnected property
|
|
178
|
+
* @property {Object} volume - volume property
|
|
179
|
+
* @property {number} volume.current - current property
|
|
180
|
+
* @property {number} volume.peak - peak property
|
|
181
|
+
* @property {number} volume.smoothed - smoothed property
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Video frame API - provides access to video stream and computer vision data
|
|
186
|
+
* @typedef {Object} VideoAPI
|
|
187
|
+
* @property {boolean} isConnected - isConnected property
|
|
188
|
+
* @property {OffscreenCanvas | ImageBitmap |null} currentFrame - currentFrame property
|
|
189
|
+
* @property {number} frameWidth - frameWidth property
|
|
190
|
+
* @property {number} frameHeight - frameHeight property
|
|
191
|
+
* @property {number} frameRate - frameRate property
|
|
192
|
+
* @property {() => ImageData |null} getFrameData - getFrameData property
|
|
193
|
+
* @property {FaceData[]} faces - faces property
|
|
194
|
+
* @property {HandData[]} hands - hands property
|
|
195
|
+
* @property {PoseData |null} pose - pose property
|
|
196
|
+
* @property {SegmentationData |null} segmentation - segmentation property
|
|
197
|
+
* @property {Object} cv - cv property
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Mouse interaction API
|
|
202
|
+
* @typedef {Object} MouseAPI
|
|
203
|
+
* @property {number} x - x property
|
|
204
|
+
* @property {number} y - y property
|
|
205
|
+
* @property {boolean} isInCanvas - isInCanvas property
|
|
206
|
+
* @property {boolean} isPressed - isPressed property
|
|
207
|
+
* @property {boolean} leftButton - leftButton property
|
|
208
|
+
* @property {boolean} rightButton - rightButton property
|
|
209
|
+
* @property {boolean} middleButton - middleButton property
|
|
210
|
+
* @property {number} deltaX - deltaX property
|
|
211
|
+
* @property {number} deltaY - deltaY property
|
|
212
|
+
* @property {number} wheelDelta - wheelDelta property
|
|
213
|
+
* @property {number} wheelX - wheelX property
|
|
214
|
+
* @property {number} wheelY - wheelY property
|
|
215
|
+
* @property {boolean} wasPressed - wasPressed property
|
|
216
|
+
* @property {boolean} wasReleased - wasReleased property
|
|
217
|
+
* @property {boolean} wasMoved - wasMoved property
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Keyboard interaction API
|
|
222
|
+
* @typedef {Object} KeyboardAPI
|
|
223
|
+
* @property {Set<string>} activeKeys - activeKeys property
|
|
224
|
+
* @property {Set<string>} pressedThisFrame - pressedThisFrame property
|
|
225
|
+
* @property {Set<string>} releasedThisFrame - releasedThisFrame property
|
|
226
|
+
* @property {string} lastKeyPressed - lastKeyPressed property
|
|
227
|
+
* @property {string} lastKeyReleased - lastKeyReleased property
|
|
228
|
+
* @property {boolean} shift - shift property
|
|
229
|
+
* @property {boolean} ctrl - ctrl property
|
|
230
|
+
* @property {boolean} alt - alt property
|
|
231
|
+
* @property {boolean} meta - meta property
|
|
232
|
+
* @property {Uint8Array} textureData - textureData property
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Touch interaction API
|
|
237
|
+
* @typedef {Object} TouchAPI
|
|
238
|
+
* @property {TouchPoint[]} points - points property
|
|
239
|
+
* @property {number} count - count property
|
|
240
|
+
* @property {TouchPoint[]} started - started property
|
|
241
|
+
* @property {TouchPoint[]} moved - moved property
|
|
242
|
+
* @property {TouchPoint[]} ended - ended property
|
|
243
|
+
* @property {TouchPoint |null} primary - primary property
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Main Viji Artist API - provides access to canvas, timing, audio, video, and interactions
|
|
248
|
+
* @typedef {Object} VijiAPI
|
|
249
|
+
* @property {OffscreenCanvas} canvas - canvas property
|
|
250
|
+
* @property {OffscreenCanvasRenderingContext2D} [ctx] - ctx property
|
|
251
|
+
* @property {WebGLRenderingContext | WebGL2RenderingContext} [gl] - gl property
|
|
252
|
+
* @property {number} width - width property
|
|
253
|
+
* @property {number} height - height property
|
|
254
|
+
* @property {number} time - time property
|
|
255
|
+
* @property {number} deltaTime - deltaTime property
|
|
256
|
+
* @property {number} frameCount - frameCount property
|
|
257
|
+
* @property {number} fps - fps property
|
|
258
|
+
* @property {AudioAPI} audio - audio property
|
|
259
|
+
* @property {VideoAPI} video - video property
|
|
260
|
+
* @property {VideoAPI[]} streams - streams property
|
|
261
|
+
* @property {MouseAPI} mouse - mouse property
|
|
262
|
+
* @property {KeyboardAPI} keyboard - keyboard property
|
|
263
|
+
* @property {TouchAPI} touches - touches property
|
|
264
|
+
* @property {PointerAPI} pointer - pointer property
|
|
265
|
+
* @property {DeviceSensorState} device - device property
|
|
266
|
+
* @property {DeviceState[]} devices - devices property
|
|
267
|
+
* @property {(defaultValue: number, config: SliderConfig) => SliderParameter} slider - slider property
|
|
268
|
+
* @property {(defaultValue: string, config: ColorConfig) => ColorParameter} color - color property
|
|
269
|
+
* @property {(defaultValue: boolean, config: ToggleConfig) => ToggleParameter} toggle - toggle property
|
|
270
|
+
* @property {(defaultValue: string | number, config: SelectConfig) => SelectParameter} select - select property
|
|
271
|
+
* @property {(defaultValue: string, config: TextConfig) => TextParameter} text - text property
|
|
272
|
+
* @property {(defaultValue: number, config: NumberConfig) => NumberParameter} number - number property
|
|
273
|
+
* @property {(defaultValue: null, config: ImageConfig) => ImageParameter} image - image property
|
|
274
|
+
* @property {(config: ButtonConfig) => ButtonParameter} button - button property
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* FaceData interface
|
|
279
|
+
* @typedef {Object} FaceData
|
|
280
|
+
* @property {number} id - id property
|
|
281
|
+
* @property {Object} bounds - bounds property
|
|
282
|
+
* @property {number} bounds.x - x property
|
|
283
|
+
* @property {number} bounds.y - y property
|
|
284
|
+
* @property {number} bounds.width - width property
|
|
285
|
+
* @property {number} bounds.height - height property
|
|
286
|
+
*/
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* FaceBlendshapes interface
|
|
290
|
+
* @typedef {Object} FaceBlendshapes
|
|
291
|
+
* @property {number} browDownLeft - browDownLeft property
|
|
292
|
+
* @property {number} browDownRight - browDownRight property
|
|
293
|
+
* @property {number} browInnerUp - browInnerUp property
|
|
294
|
+
* @property {number} browOuterUpLeft - browOuterUpLeft property
|
|
295
|
+
* @property {number} browOuterUpRight - browOuterUpRight property
|
|
296
|
+
* @property {number} cheekPuff - cheekPuff property
|
|
297
|
+
* @property {number} cheekSquintLeft - cheekSquintLeft property
|
|
298
|
+
* @property {number} cheekSquintRight - cheekSquintRight property
|
|
299
|
+
* @property {number} eyeBlinkLeft - eyeBlinkLeft property
|
|
300
|
+
* @property {number} eyeBlinkRight - eyeBlinkRight property
|
|
301
|
+
* @property {number} eyeLookDownLeft - eyeLookDownLeft property
|
|
302
|
+
* @property {number} eyeLookDownRight - eyeLookDownRight property
|
|
303
|
+
* @property {number} eyeLookInLeft - eyeLookInLeft property
|
|
304
|
+
* @property {number} eyeLookInRight - eyeLookInRight property
|
|
305
|
+
* @property {number} eyeLookOutLeft - eyeLookOutLeft property
|
|
306
|
+
* @property {number} eyeLookOutRight - eyeLookOutRight property
|
|
307
|
+
* @property {number} eyeLookUpLeft - eyeLookUpLeft property
|
|
308
|
+
* @property {number} eyeLookUpRight - eyeLookUpRight property
|
|
309
|
+
* @property {number} eyeSquintLeft - eyeSquintLeft property
|
|
310
|
+
* @property {number} eyeSquintRight - eyeSquintRight property
|
|
311
|
+
* @property {number} eyeWideLeft - eyeWideLeft property
|
|
312
|
+
* @property {number} eyeWideRight - eyeWideRight property
|
|
313
|
+
* @property {number} jawForward - jawForward property
|
|
314
|
+
* @property {number} jawLeft - jawLeft property
|
|
315
|
+
* @property {number} jawOpen - jawOpen property
|
|
316
|
+
* @property {number} jawRight - jawRight property
|
|
317
|
+
* @property {number} mouthClose - mouthClose property
|
|
318
|
+
* @property {number} mouthDimpleLeft - mouthDimpleLeft property
|
|
319
|
+
* @property {number} mouthDimpleRight - mouthDimpleRight property
|
|
320
|
+
* @property {number} mouthFrownLeft - mouthFrownLeft property
|
|
321
|
+
* @property {number} mouthFrownRight - mouthFrownRight property
|
|
322
|
+
* @property {number} mouthFunnel - mouthFunnel property
|
|
323
|
+
* @property {number} mouthLeft - mouthLeft property
|
|
324
|
+
* @property {number} mouthLowerDownLeft - mouthLowerDownLeft property
|
|
325
|
+
* @property {number} mouthLowerDownRight - mouthLowerDownRight property
|
|
326
|
+
* @property {number} mouthPressLeft - mouthPressLeft property
|
|
327
|
+
* @property {number} mouthPressRight - mouthPressRight property
|
|
328
|
+
* @property {number} mouthPucker - mouthPucker property
|
|
329
|
+
* @property {number} mouthRight - mouthRight property
|
|
330
|
+
* @property {number} mouthRollLower - mouthRollLower property
|
|
331
|
+
* @property {number} mouthRollUpper - mouthRollUpper property
|
|
332
|
+
* @property {number} mouthShrugLower - mouthShrugLower property
|
|
333
|
+
* @property {number} mouthShrugUpper - mouthShrugUpper property
|
|
334
|
+
* @property {number} mouthSmileLeft - mouthSmileLeft property
|
|
335
|
+
* @property {number} mouthSmileRight - mouthSmileRight property
|
|
336
|
+
* @property {number} mouthStretchLeft - mouthStretchLeft property
|
|
337
|
+
* @property {number} mouthStretchRight - mouthStretchRight property
|
|
338
|
+
* @property {number} mouthUpperUpLeft - mouthUpperUpLeft property
|
|
339
|
+
* @property {number} mouthUpperUpRight - mouthUpperUpRight property
|
|
340
|
+
* @property {number} noseSneerLeft - noseSneerLeft property
|
|
341
|
+
* @property {number} noseSneerRight - noseSneerRight property
|
|
342
|
+
* @property {number} tongueOut - tongueOut property
|
|
343
|
+
*/
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* HandData interface
|
|
347
|
+
* @typedef {Object} HandData
|
|
348
|
+
* @property {number} id - id property
|
|
349
|
+
* @property {'left' | 'right'} handedness - handedness property
|
|
350
|
+
* @property {number} confidence - confidence property
|
|
351
|
+
* @property {Object} bounds - bounds property
|
|
352
|
+
* @property {number} bounds.x - x property
|
|
353
|
+
* @property {number} bounds.y - y property
|
|
354
|
+
* @property {number} bounds.width - width property
|
|
355
|
+
* @property {number} bounds.height - height property
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* PoseData interface
|
|
360
|
+
* @typedef {Object} PoseData
|
|
361
|
+
* @property {number} confidence - confidence property
|
|
362
|
+
* @property {Object} landmarks - landmarks property
|
|
363
|
+
* @property {number} landmarks.x - x property
|
|
364
|
+
* @property {number} landmarks.y - y property
|
|
365
|
+
* @property {number} landmarks.z - z property
|
|
366
|
+
* @property {number} landmarks.visibility - visibility property
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* SegmentationData interface
|
|
371
|
+
* @typedef {Object} SegmentationData
|
|
372
|
+
* @property {Uint8Array} mask - mask property
|
|
373
|
+
* @property {number} width - width property
|
|
374
|
+
* @property {number} height - height property
|
|
375
|
+
*/
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* DeviceMotionData interface
|
|
379
|
+
* @typedef {Object} DeviceMotionData
|
|
380
|
+
* @property {Object} acceleration - Acceleration without gravity (m/s²)
|
|
381
|
+
* @property {number |null} acceleration.x - x property
|
|
382
|
+
* @property {number |null} acceleration.y - y property
|
|
383
|
+
* @property {number |null} acceleration.z - z property
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* DeviceOrientationData interface
|
|
388
|
+
* @typedef {Object} DeviceOrientationData
|
|
389
|
+
* @property {number |null} alpha - Rotation around Z-axis (0-360 degrees, compass heading)
|
|
390
|
+
* @property {number |null} beta - Rotation around X-axis (-180 to 180 degrees, front-to-back tilt)
|
|
391
|
+
* @property {number |null} gamma - Rotation around Y-axis (-90 to 90 degrees, left-to-right tilt)
|
|
392
|
+
* @property {boolean} absolute - True if using magnetometer (compass) for absolute orientation
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* DeviceSensorState interface
|
|
397
|
+
* @typedef {Object} DeviceSensorState
|
|
398
|
+
* @property {DeviceMotionData |null} motion - motion property
|
|
399
|
+
* @property {DeviceOrientationData |null} orientation - orientation property
|
|
400
|
+
*/
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* DeviceState interface
|
|
404
|
+
* @typedef {Object} DeviceState
|
|
405
|
+
* @property {string} id - Unique device identifier
|
|
406
|
+
* @property {string} name - User-friendly device name
|
|
407
|
+
* @property {VideoAPI |null} video - Device camera video (null if not available)
|
|
408
|
+
*/
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* @typedef {function(VijiAPI): void} Render
|
|
412
|
+
*/
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @typedef {function(VijiAPI): void} Setup
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
/** Global viji object available in artist code */
|
|
419
|
+
declare const viji: VijiAPI;
|
|
420
|
+
|
|
421
|
+
/** Artist render function - called every frame */
|
|
422
|
+
declare function render(viji: VijiAPI): void;
|
|
423
|
+
|
|
424
|
+
/** Artist setup function - called once at initialization */
|
|
425
|
+
declare function setup(viji: VijiAPI): void;
|
package/dist/docs-api.d.ts
CHANGED
|
@@ -40,9 +40,16 @@ export interface LiveExampleBlock {
|
|
|
40
40
|
description?: string;
|
|
41
41
|
sceneCode: string;
|
|
42
42
|
sceneFile: string;
|
|
43
|
+
capabilities?: LiveExampleCapabilities;
|
|
43
44
|
assets?: LiveExampleAssets;
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
export interface LiveExampleCapabilities {
|
|
48
|
+
audio?: true;
|
|
49
|
+
video?: true;
|
|
50
|
+
interaction?: true;
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
export interface LiveExampleAssets {
|
|
47
54
|
audio?: string;
|
|
48
55
|
video?: string;
|
package/dist/docs-api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const docsApi = {
|
|
2
2
|
"version": "1.0.0",
|
|
3
|
-
"coreVersion": "0.3.
|
|
4
|
-
"generatedAt": "2026-03-
|
|
3
|
+
"coreVersion": "0.3.24",
|
|
4
|
+
"generatedAt": "2026-03-27T11:44:58.441Z",
|
|
5
5
|
"navigation": [
|
|
6
6
|
{
|
|
7
7
|
"id": "getting-started",
|
|
@@ -846,7 +846,7 @@ export const docsApi = {
|
|
|
846
846
|
},
|
|
847
847
|
{
|
|
848
848
|
"type": "text",
|
|
849
|
-
"markdown": "> [!NOTE]\n> The Viji shader renderer automatically injects `precision mediump float;` and all `uniform` declarations. Write only your helper functions and `void main() { ... }`. Do NOT redeclare `precision` or
|
|
849
|
+
"markdown": "> [!NOTE]\n> The Viji shader renderer automatically injects `precision mediump float;` and all `uniform` declarations — both built-in uniforms (`u_resolution`, `u_time`, etc.) and parameter uniforms from `@viji-*` directives. Write only your helper functions and `void main() { ... }`. Do NOT redeclare `precision` or any uniforms — they will conflict.\n\n**Key characteristics:**\n\n- **Fragment shader only.** Viji renders a fullscreen quad; your shader defines the color of every pixel.\n- **GLSL ES 1.00 by default.** If you add `#version 300 es` as the first line, Viji switches to WebGL 2. Note that ES 3.00 requires `out vec4` for output instead of `gl_FragColor`, and `texture()` instead of `texture2D()`. ES 1.00 is recommended for maximum compatibility.\n- **Built-in uniforms** like `u_time`, `u_resolution`, `u_mouse`, `u_audioVolume`, `u_video`, and many more are always available — no declaration needed.\n- **Parameters via comments.** Declare parameters with `// @viji-TYPE:uniformName key:value` syntax. They become uniforms automatically.\n- **Accumulators for smooth animation.** Use `// @viji-accumulator:phase rate:speed` instead of `u_time * speed` — the value grows smoothly without jumping when the rate parameter changes.\n- **No `u_` prefix for your parameters.** The `u_` prefix is reserved for Viji's built-in uniforms. Name your parameters descriptively: `speed`, `colorMix`, `intensity`.\n\nIf you have existing Shadertoy shaders, see [Shadertoy Compatibility](/shader/shadertoy) for a compatibility layer that lets you paste code with minimal changes.\n\n---\n\n## Comparison\n\n| | Native | P5.js | Shader |\n|---|--------|-------|--------|\n| **Language** | JavaScript / TypeScript | JavaScript with P5 API | GLSL ES 1.00 (or 3.00 with `#version 300 es`) |\n| **Directive** | None (default) | `// @renderer p5` | `// @renderer shader` |\n| **Entry point** | `render(viji)` | `render(viji, p5)` | `void main()` |\n| **Setup** | Top-level code + `await` | Optional `setup(viji, p5)` | N/A |\n| **Canvas access** | [`viji.useContext('2d'/'webgl'/'webgl2')`](/native/canvas-context) | P5 drawing functions | Automatic fullscreen quad |\n| **External libraries** | Yes (`await import(...)`) | P5.js only | No |\n| **Best for** | Full control, WebGL, Three.js | Familiar P5 workflows | GPU effects, raymarching |\n| **Parameters** | [`viji.slider()`](/native/parameters/slider), etc. | [`viji.slider()`](/native/parameters/slider), etc. | `// @viji-slider:name ...` |\n\n## Next Steps\n\n- [Native Quick Start](/native/quickstart) — build your first native scene\n- [P5 Quick Start](/p5/quickstart) — build your first P5.js scene\n- [Shader Quick Start](/shader/quickstart) — build your first shader\n- [Best Practices](../best-practices/) — essential patterns all artists should follow"
|
|
850
850
|
}
|
|
851
851
|
]
|
|
852
852
|
},
|
|
@@ -868,7 +868,7 @@ export const docsApi = {
|
|
|
868
868
|
"content": [
|
|
869
869
|
{
|
|
870
870
|
"type": "text",
|
|
871
|
-
"markdown": "# Common Mistakes\r\n\r\nThis page collects the most frequent mistakes artists make when writing Viji scenes. Each section shows the wrong approach and the correct alternative.\r\n\r\n---\r\n\r\n## Using DOM APIs\r\n\r\nScenes run in a Web Worker. There is no DOM.\r\n\r\n```javascript\r\n// Wrong — DOM APIs don't exist in workers\r\nconst img = new Image();\r\nimg.src = 'photo.jpg';\r\n\r\ndocument.createElement('canvas');\r\nwindow.innerWidth;\r\nlocalStorage.setItem('key', 'value');\r\n```\r\n\r\n```javascript\r\n// Right — use Viji's API for inputs\r\nconst photo = viji.image(null, { label: 'Photo' });\r\n\r\n// Use viji.canvas, viji.width, viji.height instead\r\n// Use fetch() for loading external data:\r\nconst data = await fetch('https://cdn.example.com/data.json').then(r => r.json());\r\n```\r\n\r\n---\r\n\r\n## Declaring Parameters Inside `render()`\r\n\r\nParameter functions register UI controls with the host. Calling them in `render()` re-registers the parameter every frame, resetting its value to the default and making user changes ineffective.\r\n\r\n```javascript\r\n// Wrong — re-registers the slider every frame, resetting its value\r\nfunction render(viji) {\r\n const speed = viji.slider(1, { min: 0, max: 5, label: 'Speed' });\r\n // ...\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — declare once at top level, read .value in render()\r\nconst speed = viji.slider(1, { min: 0, max: 5, label: 'Speed' });\r\n\r\nfunction render(viji) {\r\n const s = speed.value;\r\n // ...\r\n}\r\n```\r\n\r\n---\r\n\r\n## Forgetting `.value` on Parameters\r\n\r\nParameter objects are not raw values. You need to access `.value` to get the current value.\r\n\r\n```javascript\r\n// Wrong — uses the parameter object, not its value\r\nconst radius = viji.slider(50, { min: 10, max: 200, label: 'Radius' });\r\n\r\nfunction render(viji) {\r\n ctx.arc(x, y, radius, 0, Math.PI * 2); // radius is an object, not a number\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — access .value\r\nfunction render(viji) {\r\n ctx.arc(x, y, radius.value, 0, Math.PI * 2);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Hardcoding Pixel Values\r\n\r\nThe host controls your scene's resolution. Hardcoded values break at different sizes.\r\n\r\n```javascript\r\n// Wrong — only looks right at one specific resolution\r\nfunction render(viji) {\r\n ctx.arc(960, 540, 50, 0, Math.PI * 2);\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — adapts to any resolution\r\nfunction render(viji) {\r\n const cx = viji.width / 2;\r\n const cy = viji.height / 2;\r\n const r = Math.min(viji.width, viji.height) * 0.05;\r\n ctx.arc(cx, cy, r, 0, Math.PI * 2);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Frame-Rate-Dependent Animation\r\n\r\nCounting frames or using fixed increments makes animation speed depend on the device's frame rate.\r\n\r\n```javascript\r\n// Wrong — faster on 120Hz displays, slower on 30Hz\r\nlet angle = 0;\r\nfunction render(viji) {\r\n angle += 0.02;\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — use viji.time for consistent speed regardless of FPS\r\nfunction render(viji) {\r\n const angle = viji.time * speed.value;\r\n}\r\n\r\n// Or use viji.deltaTime for accumulation\r\nlet position = 0;\r\nfunction render(viji) {\r\n position += velocity * viji.deltaTime;\r\n}\r\n```\r\n\r\n---\r\n\r\n## Allocating Objects in `render()`\r\n\r\nCreating new objects every frame causes garbage collection pauses.\r\n\r\n```javascript\r\n// Wrong — new object every frame\r\nfunction render(viji) {\r\n const particles = [];\r\n for (let i = 0; i < 100; i++) {\r\n particles.push({ x: Math.random() * viji.width, y: Math.random() * viji.height });\r\n }\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — pre-allocate and reuse\r\nconst particles = Array.from({ length: 100 }, () => ({ x: 0, y: 0 }));\r\n\r\nfunction render(viji) {\r\n for (const p of particles) {\r\n p.x = Math.random() * viji.width;\r\n p.y = Math.random() * viji.height;\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Missing the `p5.` Prefix\r\n\r\nViji runs P5 in **instance mode**. All P5 functions must be called on the `p5` object.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — global P5 functions don't exist\r\nfunction render(viji, p5) {\r\n background(0); // ReferenceError\r\n fill(255, 0, 0); // ReferenceError\r\n circle(width / 2, height / 2, 100); // ReferenceError\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — use p5. prefix for P5 functions, viji.* for dimensions\r\nfunction render(viji, p5) {\r\n p5.background(0);\r\n p5.fill(255, 0, 0);\r\n p5.circle(viji.width / 2, viji.height / 2, 100);\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Using `draw()` Instead of `render()`\r\n\r\nP5's built-in draw loop is disabled in Viji. Your function must be named `render`, not `draw`.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — Viji never calls draw()\r\nfunction draw(viji, p5) {\r\n p5.background(0);\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — Viji calls render() every frame\r\nfunction render(viji, p5) {\r\n p5.background(0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Calling `createCanvas()`\r\n\r\nThe canvas is created and managed by Viji. Calling `createCanvas()` creates a second canvas that won't be displayed.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — creates a separate, invisible canvas\r\nfunction setup(viji, p5) {\r\n p5.createCanvas(800, 600);\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — canvas is already provided, just configure settings\r\nfunction setup(viji, p5) {\r\n p5.colorMode(p5.HSB);\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Using Event Callbacks\r\n\r\nP5 event callbacks like `mousePressed()`, `keyPressed()`, `touchStarted()` do not work in Viji's worker environment. Use Viji's interaction APIs instead.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — these callbacks are never called\r\nfunction mousePressed() {\r\n console.log('clicked');\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — check Viji's interaction state in render()\r\nfunction render(viji, p5) {\r\n if (viji.pointer.wasPressed) {\r\n console.log('clicked');\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## Shader: Redeclaring Auto-Injected Code\r\n\r\nViji auto-injects `precision` and all built-in uniform declarations. Redeclaring them causes conflicts.\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Wrong — these are already injected by Viji\r\nprecision mediump float;\r\nuniform vec2 u_resolution;\r\nuniform float u_time;\r\n\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n gl_FragColor = vec4(uv, sin(u_time), 1.0);\r\n}\r\n```\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Right — just write your code, uniforms are available automatically\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n gl_FragColor = vec4(uv, sin(u_time), 1.0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Shader: Using `u_` Prefix for Parameters\r\n\r\nThe `u_` prefix is reserved for Viji's built-in uniforms. Using it for your parameters risks naming collisions.\r\n\r\n```glsl\r\n// Wrong — u_ prefix is reserved\r\n// @viji-slider:u_speed label:\"Speed\" default:1.0\r\n```\r\n\r\n```glsl\r\n// Right — use descriptive names without u_ prefix\r\n// @viji-slider:speed label:\"Speed\" default:1.0\r\n```\r\n\r\n---\r\n\r\n## Shader: Missing `@renderer shader`\r\n\r\nWithout the directive, your GLSL code is treated as JavaScript and will throw syntax errors.\r\n\r\n```glsl\r\n// Wrong — no directive, treated as JavaScript\r\nvoid main() {\r\n gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n}\r\n```\r\n\r\n```glsl\r\n// Right — directive tells Viji to use the shader renderer\r\n// @renderer shader\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Shader: Using Block Comments for `@viji-*` Parameters\r\n\r\nThe `@viji-*` parameter declarations only work with single-line `//` comments. Block comments `/* */` are silently ignored.\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Wrong — block comments are not parsed for parameters\r\n/* @viji-slider:speed label:\"Speed\" default:1.0 min:0.0 max:5.0 */\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(speed, 0.0, 0.0, 1.0); // speed is undefined\r\n}\r\n```\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Right — use single-line comments for parameter declarations\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.0 max:5.0\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(speed, 0.0, 0.0, 1.0);\r\n}\r\n```\r\n\r\n> [!NOTE]\r\n> The `@renderer` directive supports both `//` and `/* */` styles, but `@viji-*` parameter declarations require `//`.\r\n\r\n---\r\n\r\n## Shader: Using `u_time * speed` for Parameter-Driven Animation\r\n\r\nMultiplying `u_time` by a parameter causes the entire phase to jump when the slider moves, because the full history is recalculated instantly.\r\n\r\n```glsl\r\n// Wrong — animation jumps when speed slider changes\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.1 max:5.0\r\nvoid main() {\r\n float wave = sin(u_time * speed);\r\n gl_FragColor = vec4(vec3(wave * 0.5 + 0.5), 1.0);\r\n}\r\n```\r\n\r\n```glsl\r\n// Right — accumulator integrates smoothly, no jumps\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.1 max:5.0\r\n// @viji-accumulator:phase rate:speed\r\nvoid main() {\r\n float wave = sin(phase);\r\n gl_FragColor = vec4(vec3(wave * 0.5 + 0.5), 1.0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Not Checking `isConnected` for Audio/Video\r\n\r\nAudio and video streams may not be available. Accessing their properties without checking `isConnected` gives meaningless zero values with no indication that something is missing.\r\n\r\n```javascript\r\n// Wrong — no guard, silently uses zero values\r\nfunction render(viji) {\r\n const bass = viji.audio.bands.low;\r\n ctx.drawImage(viji.video.currentFrame, 0, 0);\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — check connection state first\r\nfunction render(viji) {\r\n if (viji.audio.isConnected) {\r\n const bass = viji.audio.bands.low;\r\n // ... react to audio\r\n }\r\n\r\n if (viji.video.isConnected && viji.video.currentFrame) {\r\n ctx.drawImage(viji.video.currentFrame, 0, 0, viji.width, viji.height);\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## Enabling All CV Features by Default\r\n\r\nEnabling CV features without user consent wastes resources on devices that can't handle it, and risks WebGL context loss.\r\n\r\n```javascript\r\n// Wrong — activates expensive CV on every device\r\nawait viji.video.cv.enableFaceDetection(true);\r\nawait viji.video.cv.enableHandTracking(true);\r\nawait viji.video.cv.enablePoseDetection(true);\r\nawait viji.video.cv.enableBodySegmentation(true);\r\n```\r\n\r\n```javascript\r\n// Right — let the user opt in\r\nconst useFace = viji.toggle(false, { label: 'Enable Face Tracking', category: 'video' });\r\nconst useHands = viji.toggle(false, { label: 'Enable Hand Tracking', category: 'video' });\r\n\r\nfunction render(viji) {\r\n if (useFace.value) await viji.video.cv.enableFaceDetection(true);\r\n else await viji.video.cv.enableFaceDetection(false);\r\n\r\n if (useHands.value) await viji.video.cv.enableHandTracking(true);\r\n else await viji.video.cv.enableHandTracking(false);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Related\r\n\r\n- [Best Practices](../best-practices/) — positive guidance for writing robust scenes\r\n- [Performance](/advanced/performance) — deep dive into optimization\r\n- [Renderers Overview](../renderers-overview/) — choosing the right renderer"
|
|
871
|
+
"markdown": "# Common Mistakes\r\n\r\nThis page collects the most frequent mistakes artists make when writing Viji scenes. Each section shows the wrong approach and the correct alternative.\r\n\r\n---\r\n\r\n## Using DOM APIs\r\n\r\nScenes run in a Web Worker. There is no DOM.\r\n\r\n```javascript\r\n// Wrong — DOM APIs don't exist in workers\r\nconst img = new Image();\r\nimg.src = 'photo.jpg';\r\n\r\ndocument.createElement('canvas');\r\nwindow.innerWidth;\r\nlocalStorage.setItem('key', 'value');\r\n```\r\n\r\n```javascript\r\n// Right — use Viji's API for inputs\r\nconst photo = viji.image(null, { label: 'Photo' });\r\n\r\n// Use viji.canvas, viji.width, viji.height instead\r\n// Use fetch() for loading external data:\r\nconst data = await fetch('https://cdn.example.com/data.json').then(r => r.json());\r\n```\r\n\r\n---\r\n\r\n## Declaring Parameters Inside `render()`\r\n\r\nParameter functions register UI controls with the host. Calling them in `render()` re-registers the parameter every frame, resetting its value to the default and making user changes ineffective.\r\n\r\n```javascript\r\n// Wrong — re-registers the slider every frame, resetting its value\r\nfunction render(viji) {\r\n const speed = viji.slider(1, { min: 0, max: 5, label: 'Speed' });\r\n // ...\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — declare once at top level, read .value in render()\r\nconst speed = viji.slider(1, { min: 0, max: 5, label: 'Speed' });\r\n\r\nfunction render(viji) {\r\n const s = speed.value;\r\n // ...\r\n}\r\n```\r\n\r\n---\r\n\r\n## Forgetting `.value` on Parameters\r\n\r\nParameter objects are not raw values. You need to access `.value` to get the current value.\r\n\r\n```javascript\r\n// Wrong — uses the parameter object, not its value\r\nconst radius = viji.slider(50, { min: 10, max: 200, label: 'Radius' });\r\n\r\nfunction render(viji) {\r\n ctx.arc(x, y, radius, 0, Math.PI * 2); // radius is an object, not a number\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — access .value\r\nfunction render(viji) {\r\n ctx.arc(x, y, radius.value, 0, Math.PI * 2);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Hardcoding Pixel Values\r\n\r\nThe host controls your scene's resolution. Hardcoded values break at different sizes.\r\n\r\n```javascript\r\n// Wrong — only looks right at one specific resolution\r\nfunction render(viji) {\r\n ctx.arc(960, 540, 50, 0, Math.PI * 2);\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — adapts to any resolution\r\nfunction render(viji) {\r\n const cx = viji.width / 2;\r\n const cy = viji.height / 2;\r\n const r = Math.min(viji.width, viji.height) * 0.05;\r\n ctx.arc(cx, cy, r, 0, Math.PI * 2);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Frame-Rate-Dependent Animation\r\n\r\nCounting frames or using fixed increments makes animation speed depend on the device's frame rate.\r\n\r\n```javascript\r\n// Wrong — faster on 120Hz displays, slower on 30Hz\r\nlet angle = 0;\r\nfunction render(viji) {\r\n angle += 0.02;\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — use viji.time for consistent speed regardless of FPS\r\nfunction render(viji) {\r\n const angle = viji.time * speed.value;\r\n}\r\n\r\n// Or use viji.deltaTime for accumulation\r\nlet position = 0;\r\nfunction render(viji) {\r\n position += velocity * viji.deltaTime;\r\n}\r\n```\r\n\r\n---\r\n\r\n## Allocating Objects in `render()`\r\n\r\nCreating new objects every frame causes garbage collection pauses.\r\n\r\n```javascript\r\n// Wrong — new object every frame\r\nfunction render(viji) {\r\n const particles = [];\r\n for (let i = 0; i < 100; i++) {\r\n particles.push({ x: Math.random() * viji.width, y: Math.random() * viji.height });\r\n }\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — pre-allocate and reuse\r\nconst particles = Array.from({ length: 100 }, () => ({ x: 0, y: 0 }));\r\n\r\nfunction render(viji) {\r\n for (const p of particles) {\r\n p.x = Math.random() * viji.width;\r\n p.y = Math.random() * viji.height;\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Missing the `p5.` Prefix\r\n\r\nViji runs P5 in **instance mode**. All P5 functions must be called on the `p5` object.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — global P5 functions don't exist\r\nfunction render(viji, p5) {\r\n background(0); // ReferenceError\r\n fill(255, 0, 0); // ReferenceError\r\n circle(width / 2, height / 2, 100); // ReferenceError\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — use p5. prefix for P5 functions, viji.* for dimensions\r\nfunction render(viji, p5) {\r\n p5.background(0);\r\n p5.fill(255, 0, 0);\r\n p5.circle(viji.width / 2, viji.height / 2, 100);\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Using `draw()` Instead of `render()`\r\n\r\nP5's built-in draw loop is disabled in Viji. Your function must be named `render`, not `draw`.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — Viji never calls draw()\r\nfunction draw(viji, p5) {\r\n p5.background(0);\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — Viji calls render() every frame\r\nfunction render(viji, p5) {\r\n p5.background(0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Calling `createCanvas()`\r\n\r\nThe canvas is created and managed by Viji. Calling `createCanvas()` creates a second canvas that won't be displayed.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — creates a separate, invisible canvas\r\nfunction setup(viji, p5) {\r\n p5.createCanvas(800, 600);\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — canvas is already provided, just configure settings\r\nfunction setup(viji, p5) {\r\n p5.colorMode(p5.HSB);\r\n}\r\n```\r\n\r\n---\r\n\r\n## P5: Using Event Callbacks\r\n\r\nP5 event callbacks like `mousePressed()`, `keyPressed()`, `touchStarted()` do not work in Viji's worker environment. Use Viji's interaction APIs instead.\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Wrong — these callbacks are never called\r\nfunction mousePressed() {\r\n console.log('clicked');\r\n}\r\n```\r\n\r\n```javascript\r\n// @renderer p5\r\n\r\n// Right — check Viji's interaction state in render()\r\nfunction render(viji, p5) {\r\n if (viji.pointer.wasPressed) {\r\n console.log('clicked');\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## Shader: Redeclaring Auto-Injected Code\r\n\r\nViji auto-injects `precision`, all built-in uniform declarations, and all parameter uniforms from `@viji-*` directives. Redeclaring any of them causes compilation errors.\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Wrong — these are already injected by Viji\r\nprecision mediump float;\r\nuniform vec2 u_resolution;\r\nuniform float u_time;\r\n\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n gl_FragColor = vec4(uv, sin(u_time), 1.0);\r\n}\r\n```\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Right — just write your code, uniforms are available automatically\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n gl_FragColor = vec4(uv, sin(u_time), 1.0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Shader: Using `u_` Prefix for Parameters\r\n\r\nThe `u_` prefix is reserved for Viji's built-in uniforms. Using it for your parameters risks naming collisions.\r\n\r\n```glsl\r\n// Wrong — u_ prefix is reserved\r\n// @viji-slider:u_speed label:\"Speed\" default:1.0\r\n```\r\n\r\n```glsl\r\n// Right — use descriptive names without u_ prefix\r\n// @viji-slider:speed label:\"Speed\" default:1.0\r\n```\r\n\r\n---\r\n\r\n## Shader: Missing `@renderer shader`\r\n\r\nWithout the directive, your GLSL code is treated as JavaScript and will throw syntax errors.\r\n\r\n```glsl\r\n// Wrong — no directive, treated as JavaScript\r\nvoid main() {\r\n gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n}\r\n```\r\n\r\n```glsl\r\n// Right — directive tells Viji to use the shader renderer\r\n// @renderer shader\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Shader: Using Block Comments for `@viji-*` Parameters\r\n\r\nThe `@viji-*` parameter declarations only work with single-line `//` comments. Block comments `/* */` are silently ignored.\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Wrong — block comments are not parsed for parameters\r\n/* @viji-slider:speed label:\"Speed\" default:1.0 min:0.0 max:5.0 */\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(speed, 0.0, 0.0, 1.0); // speed is undefined\r\n}\r\n```\r\n\r\n```glsl\r\n// @renderer shader\r\n\r\n// Right — use single-line comments for parameter declarations\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.0 max:5.0\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(speed, 0.0, 0.0, 1.0);\r\n}\r\n```\r\n\r\n> [!NOTE]\r\n> The `@renderer` directive supports both `//` and `/* */` styles, but `@viji-*` parameter declarations require `//`.\r\n\r\n---\r\n\r\n## Shader: Using `u_time * speed` for Parameter-Driven Animation\r\n\r\nMultiplying `u_time` by a parameter causes the entire phase to jump when the slider moves, because the full history is recalculated instantly.\r\n\r\n```glsl\r\n// Wrong — animation jumps when speed slider changes\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.1 max:5.0\r\nvoid main() {\r\n float wave = sin(u_time * speed);\r\n gl_FragColor = vec4(vec3(wave * 0.5 + 0.5), 1.0);\r\n}\r\n```\r\n\r\n```glsl\r\n// Right — accumulator integrates smoothly, no jumps\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.1 max:5.0\r\n// @viji-accumulator:phase rate:speed\r\nvoid main() {\r\n float wave = sin(phase);\r\n gl_FragColor = vec4(vec3(wave * 0.5 + 0.5), 1.0);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Not Checking `isConnected` for Audio/Video\r\n\r\nAudio and video streams may not be available. Accessing their properties without checking `isConnected` gives meaningless zero values with no indication that something is missing.\r\n\r\n```javascript\r\n// Wrong — no guard, silently uses zero values\r\nfunction render(viji) {\r\n const bass = viji.audio.bands.low;\r\n ctx.drawImage(viji.video.currentFrame, 0, 0);\r\n}\r\n```\r\n\r\n```javascript\r\n// Right — check connection state first\r\nfunction render(viji) {\r\n if (viji.audio.isConnected) {\r\n const bass = viji.audio.bands.low;\r\n // ... react to audio\r\n }\r\n\r\n if (viji.video.isConnected && viji.video.currentFrame) {\r\n ctx.drawImage(viji.video.currentFrame, 0, 0, viji.width, viji.height);\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n## Enabling All CV Features by Default\r\n\r\nEnabling CV features without user consent wastes resources on devices that can't handle it, and risks WebGL context loss.\r\n\r\n```javascript\r\n// Wrong — activates expensive CV on every device\r\nawait viji.video.cv.enableFaceDetection(true);\r\nawait viji.video.cv.enableHandTracking(true);\r\nawait viji.video.cv.enablePoseDetection(true);\r\nawait viji.video.cv.enableBodySegmentation(true);\r\n```\r\n\r\n```javascript\r\n// Right — let the user opt in\r\nconst useFace = viji.toggle(false, { label: 'Enable Face Tracking', category: 'video' });\r\nconst useHands = viji.toggle(false, { label: 'Enable Hand Tracking', category: 'video' });\r\n\r\nfunction render(viji) {\r\n if (useFace.value) await viji.video.cv.enableFaceDetection(true);\r\n else await viji.video.cv.enableFaceDetection(false);\r\n\r\n if (useHands.value) await viji.video.cv.enableHandTracking(true);\r\n else await viji.video.cv.enableHandTracking(false);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Related\r\n\r\n- [Best Practices](../best-practices/) — positive guidance for writing robust scenes\r\n- [Performance](/advanced/performance) — deep dive into optimization\r\n- [Renderers Overview](../renderers-overview/) — choosing the right renderer"
|
|
872
872
|
}
|
|
873
873
|
]
|
|
874
874
|
},
|
|
@@ -1222,7 +1222,11 @@ export const docsApi = {
|
|
|
1222
1222
|
"type": "live-example",
|
|
1223
1223
|
"title": "Parameter Categories",
|
|
1224
1224
|
"sceneCode": "const baseColor = viji.color('#4488ff', { label: 'Base Color', category: 'general' });\r\nconst pulseAmount = viji.slider(0.3, { min: 0, max: 1, step: 0.01, label: 'Audio Pulse', category: 'audio' });\r\nconst showMouse = viji.toggle(true, { label: 'Mouse Trail', category: 'interaction' });\r\n\r\nlet angle = 0;\r\nlet mouseTrailX = 0;\r\nlet mouseTrailY = 0;\r\n\r\nfunction render(viji) {\r\n const ctx = viji.useContext('2d');\r\n const w = viji.width;\r\n const h = viji.height;\r\n\r\n ctx.fillStyle = 'rgba(10, 10, 30, 0.15)';\r\n ctx.fillRect(0, 0, w, h);\r\n\r\n angle += viji.deltaTime;\r\n\r\n const r = parseInt(baseColor.value.slice(1, 3), 16);\r\n const g = parseInt(baseColor.value.slice(3, 5), 16);\r\n const b = parseInt(baseColor.value.slice(5, 7), 16);\r\n\r\n let pulse = 0;\r\n if (viji.audio.isConnected) {\r\n pulse = viji.audio.volume.current * pulseAmount.value;\r\n }\r\n\r\n const baseR = Math.min(w, h) * (0.1 + pulse * 0.15);\r\n const cx = w / 2 + Math.cos(angle) * w * 0.2;\r\n const cy = h / 2 + Math.sin(angle * 0.7) * h * 0.2;\r\n\r\n ctx.beginPath();\r\n ctx.arc(cx, cy, baseR, 0, Math.PI * 2);\r\n ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;\r\n ctx.fill();\r\n\r\n if (showMouse.value && viji.mouse.isInCanvas) {\r\n mouseTrailX += (viji.mouse.x - mouseTrailX) * 0.1;\r\n mouseTrailY += (viji.mouse.y - mouseTrailY) * 0.1;\r\n ctx.beginPath();\r\n ctx.arc(mouseTrailX, mouseTrailY, Math.min(w, h) * 0.02, 0, Math.PI * 2);\r\n ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';\r\n ctx.fill();\r\n }\r\n}\r\n",
|
|
1225
|
-
"sceneFile": "categories-demo.scene.js"
|
|
1225
|
+
"sceneFile": "categories-demo.scene.js",
|
|
1226
|
+
"capabilities": {
|
|
1227
|
+
"audio": true,
|
|
1228
|
+
"interaction": true
|
|
1229
|
+
}
|
|
1226
1230
|
},
|
|
1227
1231
|
{
|
|
1228
1232
|
"type": "text",
|
|
@@ -1243,7 +1247,10 @@ export const docsApi = {
|
|
|
1243
1247
|
"type": "live-example",
|
|
1244
1248
|
"title": "Pointer — Drag Trail",
|
|
1245
1249
|
"sceneCode": "const ctx = viji.useContext('2d');\nconst trail = [];\nconst maxTrail = 80;\n\nfunction render(viji) {\n const w = viji.width, h = viji.height;\n const p = viji.pointer;\n\n ctx.fillStyle = 'rgba(10, 10, 30, 0.15)';\n ctx.fillRect(0, 0, w, h);\n\n if (p.isDown) {\n trail.push({ x: p.x, y: p.y });\n if (trail.length > maxTrail) trail.shift();\n } else if (trail.length > 0) {\n trail.shift();\n }\n\n for (let i = 0; i < trail.length; i++) {\n const t = i / trail.length;\n const radius = 3 + t * 12;\n ctx.beginPath();\n ctx.arc(trail[i].x, trail[i].y, radius, 0, Math.PI * 2);\n ctx.fillStyle = `hsla(${200 + t * 60}, 80%, 65%, ${t * 0.8})`;\n ctx.fill();\n }\n\n const size = Math.min(w, h);\n ctx.fillStyle = p.isDown ? 'rgba(100, 220, 255, 0.9)' : 'rgba(200, 200, 200, 0.5)';\n ctx.beginPath();\n ctx.arc(p.x, p.y, size * 0.015, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = 'rgba(255,255,255,0.5)';\n ctx.font = `${size * 0.025}px monospace`;\n ctx.textAlign = 'left';\n ctx.fillText(`pointer: (${Math.round(p.x)}, ${Math.round(p.y)}) type: ${p.type}`, size * 0.03, h - size * 0.03);\n ctx.fillText(`isDown: ${p.isDown} inCanvas: ${p.isInCanvas}`, size * 0.03, h - size * 0.06);\n}\n",
|
|
1246
|
-
"sceneFile": "pointer-demo.scene.js"
|
|
1250
|
+
"sceneFile": "pointer-demo.scene.js",
|
|
1251
|
+
"capabilities": {
|
|
1252
|
+
"interaction": true
|
|
1253
|
+
}
|
|
1247
1254
|
},
|
|
1248
1255
|
{
|
|
1249
1256
|
"type": "text",
|
|
@@ -1264,7 +1271,10 @@ export const docsApi = {
|
|
|
1264
1271
|
"type": "live-example",
|
|
1265
1272
|
"title": "Mouse — Buttons & Wheel",
|
|
1266
1273
|
"sceneCode": "const ctx = viji.useContext('2d');\nlet hue = 200;\nlet zoom = 1;\nlet prevRight = false;\n\nfunction render(viji) {\n const w = viji.width, h = viji.height;\n const m = viji.mouse;\n const size = Math.min(w, h);\n\n if (m.rightButton && !prevRight) hue = (hue + 50) % 360;\n prevRight = m.rightButton;\n\n zoom -= m.wheelDelta * 0.001;\n zoom = Math.max(0.3, Math.min(5, zoom));\n\n ctx.fillStyle = 'rgba(10, 10, 30, 0.2)';\n ctx.fillRect(0, 0, w, h);\n\n const speed = Math.sqrt(m.deltaX ** 2 + m.deltaY ** 2);\n const radius = (size * 0.02 + speed * 1.5) * zoom;\n\n if (m.isInCanvas) {\n ctx.beginPath();\n ctx.arc(m.x, m.y, radius, 0, Math.PI * 2);\n const alpha = m.isPressed ? 0.9 : 0.4;\n ctx.fillStyle = `hsla(${hue}, 80%, 65%, ${alpha})`;\n ctx.fill();\n\n if (m.leftButton) {\n ctx.strokeStyle = `hsla(${hue}, 80%, 75%, 0.6)`;\n ctx.lineWidth = 2;\n ctx.stroke();\n }\n }\n\n ctx.fillStyle = 'rgba(255,255,255,0.5)';\n ctx.font = `${size * 0.022}px monospace`;\n ctx.textAlign = 'left';\n const y0 = h - size * 0.15;\n ctx.fillText(`pos: (${Math.round(m.x)}, ${Math.round(m.y)}) inCanvas: ${m.isInCanvas}`, size * 0.03, y0);\n ctx.fillText(`buttons: L[${m.leftButton ? '\\u25A0' : '\\u25A1'}] R[${m.rightButton ? '\\u25A0' : '\\u25A1'}] M[${m.middleButton ? '\\u25A0' : '\\u25A1'}]`, size * 0.03, y0 + size * 0.03);\n ctx.fillText(`delta: (${m.deltaX.toFixed(0)}, ${m.deltaY.toFixed(0)}) wheel: ${m.wheelDelta.toFixed(1)}`, size * 0.03, y0 + size * 0.06);\n ctx.fillText(`zoom: ${zoom.toFixed(2)} hue: ${hue}`, size * 0.03, y0 + size * 0.09);\n}\n",
|
|
1267
|
-
"sceneFile": "mouse-demo.scene.js"
|
|
1274
|
+
"sceneFile": "mouse-demo.scene.js",
|
|
1275
|
+
"capabilities": {
|
|
1276
|
+
"interaction": true
|
|
1277
|
+
}
|
|
1268
1278
|
},
|
|
1269
1279
|
{
|
|
1270
1280
|
"type": "text",
|
|
@@ -1285,7 +1295,10 @@ export const docsApi = {
|
|
|
1285
1295
|
"type": "live-example",
|
|
1286
1296
|
"title": "Keyboard — Movement & State",
|
|
1287
1297
|
"sceneCode": "const ctx = viji.useContext('2d');\nlet px, py;\n\nfunction render(viji) {\n const w = viji.width, h = viji.height;\n const size = Math.min(w, h);\n const kb = viji.keyboard;\n\n if (px === undefined) { px = w / 2; py = h / 2; }\n\n const speed = size * 0.4 * viji.deltaTime * (kb.shift ? 2.5 : 1);\n if (kb.isPressed('w') || kb.isPressed('arrowup')) py -= speed;\n if (kb.isPressed('s') || kb.isPressed('arrowdown')) py += speed;\n if (kb.isPressed('a') || kb.isPressed('arrowleft')) px -= speed;\n if (kb.isPressed('d') || kb.isPressed('arrowright')) px += speed;\n px = Math.max(0, Math.min(w, px));\n py = Math.max(0, Math.min(h, py));\n\n ctx.fillStyle = 'rgba(10, 10, 30, 0.15)';\n ctx.fillRect(0, 0, w, h);\n\n const r = size * 0.03;\n ctx.beginPath();\n ctx.arc(px, py, r, 0, Math.PI * 2);\n ctx.fillStyle = `hsl(${(viji.time * 40) % 360}, 80%, 65%)`;\n ctx.fill();\n\n ctx.fillStyle = 'rgba(255,255,255,0.5)';\n ctx.font = `${size * 0.022}px monospace`;\n ctx.textAlign = 'left';\n const y0 = h - size * 0.12;\n const keys = [...kb.activeKeys];\n ctx.fillText(`active: [${keys.join(', ')}]`, size * 0.03, y0);\n ctx.fillText(`mods: ${kb.shift ? '[Shift] ' : ''}${kb.ctrl ? '[Ctrl] ' : ''}${kb.alt ? '[Alt] ' : ''}${kb.meta ? '[Meta]' : ''}${!kb.shift && !kb.ctrl && !kb.alt && !kb.meta ? 'none' : ''}`, size * 0.03, y0 + size * 0.03);\n ctx.fillText(`last pressed: \"${kb.lastKeyPressed}\" released: \"${kb.lastKeyReleased}\"`, size * 0.03, y0 + size * 0.06);\n\n ctx.fillStyle = 'rgba(255,255,255,0.3)';\n ctx.textAlign = 'center';\n ctx.fillText('WASD / Arrows to move \\u2022 Shift for speed', w / 2, size * 0.04);\n}\n",
|
|
1288
|
-
"sceneFile": "keyboard-demo.scene.js"
|
|
1298
|
+
"sceneFile": "keyboard-demo.scene.js",
|
|
1299
|
+
"capabilities": {
|
|
1300
|
+
"interaction": true
|
|
1301
|
+
}
|
|
1289
1302
|
},
|
|
1290
1303
|
{
|
|
1291
1304
|
"type": "text",
|
|
@@ -1306,7 +1319,10 @@ export const docsApi = {
|
|
|
1306
1319
|
"type": "live-example",
|
|
1307
1320
|
"title": "Touch — Multi-Point Tracker",
|
|
1308
1321
|
"sceneCode": "const ctx = viji.useContext('2d');\nconst ripples = [];\n\nfunction render(viji) {\n const w = viji.width, h = viji.height;\n const size = Math.min(w, h);\n const touch = viji.touches;\n const dt = viji.deltaTime;\n\n for (const pt of touch.started) {\n ripples.push({ x: pt.x, y: pt.y, r: 0, alpha: 1 });\n }\n\n ctx.fillStyle = 'rgba(10, 10, 30, 0.2)';\n ctx.fillRect(0, 0, w, h);\n\n for (let i = ripples.length - 1; i >= 0; i--) {\n const rp = ripples[i];\n rp.r += size * 0.3 * dt;\n rp.alpha -= dt * 0.8;\n if (rp.alpha <= 0) { ripples.splice(i, 1); continue; }\n ctx.beginPath();\n ctx.arc(rp.x, rp.y, rp.r, 0, Math.PI * 2);\n ctx.strokeStyle = `hsla(200, 80%, 70%, ${rp.alpha})`;\n ctx.lineWidth = 2;\n ctx.stroke();\n }\n\n for (let i = 0; i < touch.count; i++) {\n const pt = touch.points[i];\n const r = size * 0.02 + pt.pressure * size * 0.04;\n\n ctx.beginPath();\n ctx.arc(pt.x, pt.y, r, 0, Math.PI * 2);\n ctx.fillStyle = `hsla(${120 + i * 60}, 80%, 65%, 0.8)`;\n ctx.fill();\n\n const speed = Math.sqrt(pt.velocity.x ** 2 + pt.velocity.y ** 2);\n if (speed > 10) {\n const len = Math.min(speed * 0.05, size * 0.08);\n const angle = Math.atan2(pt.velocity.y, pt.velocity.x);\n ctx.beginPath();\n ctx.moveTo(pt.x, pt.y);\n ctx.lineTo(pt.x + Math.cos(angle) * len, pt.y + Math.sin(angle) * len);\n ctx.strokeStyle = `hsla(${120 + i * 60}, 80%, 75%, 0.5)`;\n ctx.lineWidth = 2;\n ctx.stroke();\n }\n\n ctx.fillStyle = 'rgba(255,255,255,0.6)';\n ctx.font = `${size * 0.02}px monospace`;\n ctx.textAlign = 'center';\n ctx.fillText(`T${pt.id}`, pt.x, pt.y - r - size * 0.01);\n }\n\n ctx.fillStyle = 'rgba(255,255,255,0.4)';\n ctx.font = `${size * 0.022}px monospace`;\n ctx.textAlign = 'left';\n ctx.fillText(`touches: ${touch.count} primary: ${touch.primary ? 'T' + touch.primary.id : '-'}`, size * 0.03, h - size * 0.03);\n}\n",
|
|
1309
|
-
"sceneFile": "touch-demo.scene.js"
|
|
1322
|
+
"sceneFile": "touch-demo.scene.js",
|
|
1323
|
+
"capabilities": {
|
|
1324
|
+
"interaction": true
|
|
1325
|
+
}
|
|
1310
1326
|
},
|
|
1311
1327
|
{
|
|
1312
1328
|
"type": "text",
|
|
@@ -1632,7 +1648,11 @@ export const docsApi = {
|
|
|
1632
1648
|
"type": "live-example",
|
|
1633
1649
|
"title": "P5 Parameter Categories",
|
|
1634
1650
|
"sceneCode": "// @renderer p5\r\n\r\nconst baseColor = viji.color('#4488ff', { label: 'Base Color', category: 'general' });\r\nconst pulseAmount = viji.slider(0.3, { min: 0, max: 1, step: 0.01, label: 'Audio Pulse', category: 'audio' });\r\nconst showMouse = viji.toggle(true, { label: 'Mouse Dot', category: 'interaction' });\r\n\r\nlet angle = 0;\r\n\r\nfunction render(viji, p5) {\r\n p5.background(10, 10, 30, 40);\r\n\r\n angle += viji.deltaTime;\r\n\r\n const r = parseInt(baseColor.value.slice(1, 3), 16);\r\n const g = parseInt(baseColor.value.slice(3, 5), 16);\r\n const b = parseInt(baseColor.value.slice(5, 7), 16);\r\n\r\n let pulse = 0;\r\n if (viji.audio.isConnected) {\r\n pulse = viji.audio.volume.current * pulseAmount.value;\r\n }\r\n\r\n const baseR = Math.min(viji.width, viji.height) * (0.1 + pulse * 0.15);\r\n const cx = viji.width / 2 + p5.cos(angle) * viji.width * 0.2;\r\n const cy = viji.height / 2 + p5.sin(angle * 0.7) * viji.height * 0.2;\r\n\r\n p5.noStroke();\r\n p5.fill(r, g, b);\r\n p5.circle(cx, cy, baseR * 2);\r\n\r\n if (showMouse.value && viji.mouse.isInCanvas) {\r\n p5.fill(255, 255, 255, 200);\r\n p5.circle(viji.mouse.x, viji.mouse.y, Math.min(viji.width, viji.height) * 0.04);\r\n }\r\n}\r\n",
|
|
1635
|
-
"sceneFile": "categories-demo.scene.js"
|
|
1651
|
+
"sceneFile": "categories-demo.scene.js",
|
|
1652
|
+
"capabilities": {
|
|
1653
|
+
"audio": true,
|
|
1654
|
+
"interaction": true
|
|
1655
|
+
}
|
|
1636
1656
|
},
|
|
1637
1657
|
{
|
|
1638
1658
|
"type": "text",
|
|
@@ -1653,7 +1673,10 @@ export const docsApi = {
|
|
|
1653
1673
|
"type": "live-example",
|
|
1654
1674
|
"title": "Pointer — Drag Trail",
|
|
1655
1675
|
"sceneCode": "// @renderer p5\n\nconst trail = [];\nconst maxTrail = 80;\n\nfunction setup(viji, p5) {\n p5.colorMode(p5.HSB, 360, 100, 100, 100);\n}\n\nfunction render(viji, p5) {\n const ptr = viji.pointer;\n\n p5.background(240, 10, 8, 15);\n\n if (ptr.isDown) {\n trail.push({ x: ptr.x, y: ptr.y });\n if (trail.length > maxTrail) trail.shift();\n } else if (trail.length > 0) {\n trail.shift();\n }\n\n p5.noStroke();\n for (let i = 0; i < trail.length; i++) {\n const t = i / trail.length;\n const r = 3 + t * 12;\n p5.fill(200 + t * 60, 80, 65, t * 80);\n p5.ellipse(trail[i].x, trail[i].y, r * 2);\n }\n\n const cursorSize = Math.min(viji.width, viji.height) * 0.03;\n p5.fill(ptr.isDown ? p5.color(200, 80, 90) : p5.color(0, 0, 80, 50));\n p5.ellipse(ptr.x, ptr.y, cursorSize);\n\n p5.fill(0, 0, 100, 50);\n p5.textSize(Math.min(viji.width, viji.height) * 0.025);\n p5.textAlign(p5.LEFT);\n p5.text(`pointer: (${Math.round(ptr.x)}, ${Math.round(ptr.y)}) type: ${ptr.type}`, viji.width * 0.03, viji.height - viji.height * 0.06);\n p5.text(`isDown: ${ptr.isDown} inCanvas: ${ptr.isInCanvas}`, viji.width * 0.03, viji.height - viji.height * 0.03);\n}\n",
|
|
1656
|
-
"sceneFile": "pointer-p5-demo.scene.js"
|
|
1676
|
+
"sceneFile": "pointer-p5-demo.scene.js",
|
|
1677
|
+
"capabilities": {
|
|
1678
|
+
"interaction": true
|
|
1679
|
+
}
|
|
1657
1680
|
},
|
|
1658
1681
|
{
|
|
1659
1682
|
"type": "text",
|
|
@@ -1674,7 +1697,10 @@ export const docsApi = {
|
|
|
1674
1697
|
"type": "live-example",
|
|
1675
1698
|
"title": "Mouse — Buttons & Wheel",
|
|
1676
1699
|
"sceneCode": "// @renderer p5\n\nlet hue = 200;\nlet zoom = 1;\nlet prevRight = false;\n\nfunction setup(viji, p5) {\n p5.colorMode(p5.HSB, 360, 100, 100, 100);\n}\n\nfunction render(viji, p5) {\n const m = viji.mouse;\n const size = Math.min(viji.width, viji.height);\n\n if (m.rightButton && !prevRight) hue = (hue + 50) % 360;\n prevRight = m.rightButton;\n\n zoom -= m.wheelDelta * 0.001;\n zoom = Math.max(0.3, Math.min(5, zoom));\n\n p5.background(240, 10, 8, 20);\n\n const speed = Math.sqrt(m.deltaX ** 2 + m.deltaY ** 2);\n const radius = (size * 0.02 + speed * 1.5) * zoom;\n\n if (m.isInCanvas) {\n p5.noStroke();\n p5.fill(hue, 80, 65, m.isPressed ? 90 : 40);\n p5.ellipse(m.x, m.y, radius * 2);\n\n if (m.leftButton) {\n p5.noFill();\n p5.stroke(hue, 80, 75, 60);\n p5.strokeWeight(2);\n p5.ellipse(m.x, m.y, radius * 2);\n }\n }\n\n p5.noStroke();\n p5.fill(0, 0, 100, 50);\n p5.textSize(size * 0.022);\n p5.textFont('monospace');\n p5.textAlign(p5.LEFT);\n const y0 = viji.height - size * 0.12;\n p5.text(`pos: (${Math.round(m.x)}, ${Math.round(m.y)}) inCanvas: ${m.isInCanvas}`, size * 0.03, y0);\n p5.text(`buttons: L[${m.leftButton ? '\\u25A0' : '\\u25A1'}] R[${m.rightButton ? '\\u25A0' : '\\u25A1'}] M[${m.middleButton ? '\\u25A0' : '\\u25A1'}]`, size * 0.03, y0 + size * 0.03);\n p5.text(`wheel: ${m.wheelDelta.toFixed(1)} zoom: ${zoom.toFixed(2)}`, size * 0.03, y0 + size * 0.06);\n}\n",
|
|
1677
|
-
"sceneFile": "mouse-p5-demo.scene.js"
|
|
1700
|
+
"sceneFile": "mouse-p5-demo.scene.js",
|
|
1701
|
+
"capabilities": {
|
|
1702
|
+
"interaction": true
|
|
1703
|
+
}
|
|
1678
1704
|
},
|
|
1679
1705
|
{
|
|
1680
1706
|
"type": "text",
|
|
@@ -1695,7 +1721,10 @@ export const docsApi = {
|
|
|
1695
1721
|
"type": "live-example",
|
|
1696
1722
|
"title": "Keyboard — Movement & State",
|
|
1697
1723
|
"sceneCode": "// @renderer p5\n\nlet px, py;\n\nfunction setup(viji, p5) {\n p5.colorMode(p5.HSB, 360, 100, 100, 100);\n}\n\nfunction render(viji, p5) {\n const w = viji.width, h = viji.height;\n const size = Math.min(w, h);\n const kb = viji.keyboard;\n\n if (px === undefined) { px = w / 2; py = h / 2; }\n\n const speed = size * 0.4 * viji.deltaTime * (kb.shift ? 2.5 : 1);\n if (kb.isPressed('w') || kb.isPressed('arrowup')) py -= speed;\n if (kb.isPressed('s') || kb.isPressed('arrowdown')) py += speed;\n if (kb.isPressed('a') || kb.isPressed('arrowleft')) px -= speed;\n if (kb.isPressed('d') || kb.isPressed('arrowright')) px += speed;\n px = Math.max(0, Math.min(w, px));\n py = Math.max(0, Math.min(h, py));\n\n p5.background(240, 10, 8, 15);\n\n const r = size * 0.03;\n p5.noStroke();\n p5.fill((viji.time * 40) % 360, 80, 65);\n p5.ellipse(px, py, r * 2);\n\n p5.fill(0, 0, 100, 50);\n p5.textSize(size * 0.022);\n p5.textFont('monospace');\n p5.textAlign(p5.LEFT);\n const y0 = h - size * 0.09;\n p5.text(`active: [${[...kb.activeKeys].join(', ')}]`, size * 0.03, y0);\n p5.text(`last: \"${kb.lastKeyPressed}\" mods: ${kb.shift ? 'Shift ' : ''}${kb.ctrl ? 'Ctrl ' : ''}${kb.alt ? 'Alt ' : ''}${!kb.shift && !kb.ctrl && !kb.alt ? 'none' : ''}`, size * 0.03, y0 + size * 0.03);\n\n p5.fill(0, 0, 100, 30);\n p5.textAlign(p5.CENTER);\n p5.text('WASD / Arrows to move \\u2022 Shift for speed', w / 2, size * 0.04);\n}\n",
|
|
1698
|
-
"sceneFile": "keyboard-p5-demo.scene.js"
|
|
1724
|
+
"sceneFile": "keyboard-p5-demo.scene.js",
|
|
1725
|
+
"capabilities": {
|
|
1726
|
+
"interaction": true
|
|
1727
|
+
}
|
|
1699
1728
|
},
|
|
1700
1729
|
{
|
|
1701
1730
|
"type": "text",
|
|
@@ -1716,7 +1745,10 @@ export const docsApi = {
|
|
|
1716
1745
|
"type": "live-example",
|
|
1717
1746
|
"title": "Touch — Multi-Point Tracker",
|
|
1718
1747
|
"sceneCode": "// @renderer p5\n\nconst ripples = [];\n\nfunction setup(viji, p5) {\n p5.colorMode(p5.HSB, 360, 100, 100, 100);\n}\n\nfunction render(viji, p5) {\n const size = Math.min(viji.width, viji.height);\n const touch = viji.touches;\n const dt = viji.deltaTime;\n\n for (const pt of touch.started) {\n ripples.push({ x: pt.x, y: pt.y, r: 0, alpha: 100 });\n }\n\n p5.background(240, 10, 8, 20);\n\n p5.noFill();\n for (let i = ripples.length - 1; i >= 0; i--) {\n const rp = ripples[i];\n rp.r += size * 0.3 * dt;\n rp.alpha -= dt * 80;\n if (rp.alpha <= 0) { ripples.splice(i, 1); continue; }\n p5.stroke(200, 80, 70, rp.alpha);\n p5.strokeWeight(2);\n p5.ellipse(rp.x, rp.y, rp.r * 2);\n }\n\n p5.noStroke();\n for (let i = 0; i < touch.count; i++) {\n const pt = touch.points[i];\n const r = size * 0.02 + pt.pressure * size * 0.04;\n\n p5.fill(120 + i * 60, 80, 65, 80);\n p5.ellipse(pt.x, pt.y, r * 2);\n\n const speed = Math.sqrt(pt.velocity.x ** 2 + pt.velocity.y ** 2);\n if (speed > 10) {\n const len = Math.min(speed * 0.05, size * 0.08);\n const angle = Math.atan2(pt.velocity.y, pt.velocity.x);\n p5.stroke(120 + i * 60, 80, 75, 50);\n p5.strokeWeight(2);\n p5.line(pt.x, pt.y, pt.x + Math.cos(angle) * len, pt.y + Math.sin(angle) * len);\n p5.noStroke();\n }\n\n p5.fill(0, 0, 100, 60);\n p5.textSize(size * 0.02);\n p5.textAlign(p5.CENTER);\n p5.text('T' + pt.id, pt.x, pt.y - r - size * 0.01);\n }\n\n p5.noStroke();\n p5.fill(0, 0, 100, 40);\n p5.textSize(size * 0.022);\n p5.textFont('monospace');\n p5.textAlign(p5.LEFT);\n p5.text(`touches: ${touch.count} primary: ${touch.primary ? 'T' + touch.primary.id : '-'}`, size * 0.03, viji.height - size * 0.03);\n}\n",
|
|
1719
|
-
"sceneFile": "touch-p5-demo.scene.js"
|
|
1748
|
+
"sceneFile": "touch-p5-demo.scene.js",
|
|
1749
|
+
"capabilities": {
|
|
1750
|
+
"interaction": true
|
|
1751
|
+
}
|
|
1720
1752
|
},
|
|
1721
1753
|
{
|
|
1722
1754
|
"type": "text",
|
|
@@ -1741,7 +1773,7 @@ export const docsApi = {
|
|
|
1741
1773
|
},
|
|
1742
1774
|
{
|
|
1743
1775
|
"type": "text",
|
|
1744
|
-
"markdown": "### What's Happening\r\n\r\n**Comment directives — parsed before compilation:**\r\n\r\n- `// @renderer shader` tells Viji to use the shader renderer.\r\n- `// @viji-slider:speed ...` declares a parameter. Viji generates a `uniform float speed;` automatically.\r\n- `// @viji-color:tint ...` declares a color parameter. Viji generates a `uniform vec3 tint;`.\r\n- `// @viji-accumulator:phase rate:speed` creates a CPU-side accumulator that adds `speed × deltaTime` every frame. The result is a `uniform float phase;` that increases smoothly — no jumps when the slider changes.\r\n\r\n**`void main()` — runs for every pixel, every frame:**\r\n\r\n- `gl_FragCoord.xy / u_resolution` gives normalized UV coordinates (0–1).\r\n- `phase` is the accumulator — use it instead of `u_time * speed` for smooth, slider-driven animation.\r\n- `speed`, `scale`, `tint` are your parameter uniforms — updated live as the user adjusts controls.\r\n- `gl_FragColor` sets the output color for each pixel.\r\n\r\n> [!NOTE]\r\n> Parameter declarations use **single-line `//` comments only**. Block comments `/* */` are not parsed for `@viji-*` directives.\r\n\r\n## Scene Structure\r\n\r\nA shader scene is a GLSL fragment shader with comment directives:\r\n\r\n```glsl\r\n// @renderer shader\r\n// @viji-slider:brightness label:\"Brightness\" default:1.0 min:0.0 max:2.0\r\n\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n gl_FragColor = vec4(uv * brightness, 0.5, 1.0);\r\n}\r\n```\r\n\r\n- **No `precision` or `uniform` declarations needed.** Viji auto-injects `precision mediump float;` and all uniform declarations.\r\n- **No vertex shader.** Viji renders a fullscreen quad; your fragment shader defines the color of every pixel.\r\n- **Parameters become uniforms.** `// @viji-slider:name` becomes `uniform float name;` automatically.\r\n\r\n> [!NOTE]\r\n> The Viji shader renderer automatically injects `precision mediump float;` and all `uniform` declarations. Write only your helper functions and `void main() { ... }`. Do NOT redeclare `precision` or
|
|
1776
|
+
"markdown": "### What's Happening\r\n\r\n**Comment directives — parsed before compilation:**\r\n\r\n- `// @renderer shader` tells Viji to use the shader renderer.\r\n- `// @viji-slider:speed ...` declares a parameter. Viji generates a `uniform float speed;` automatically.\r\n- `// @viji-color:tint ...` declares a color parameter. Viji generates a `uniform vec3 tint;`.\r\n- `// @viji-accumulator:phase rate:speed` creates a CPU-side accumulator that adds `speed × deltaTime` every frame. The result is a `uniform float phase;` that increases smoothly — no jumps when the slider changes.\r\n\r\n**`void main()` — runs for every pixel, every frame:**\r\n\r\n- `gl_FragCoord.xy / u_resolution` gives normalized UV coordinates (0–1).\r\n- `phase` is the accumulator — use it instead of `u_time * speed` for smooth, slider-driven animation.\r\n- `speed`, `scale`, `tint` are your parameter uniforms — updated live as the user adjusts controls.\r\n- `gl_FragColor` sets the output color for each pixel.\r\n\r\n> [!NOTE]\r\n> Parameter declarations use **single-line `//` comments only**. Block comments `/* */` are not parsed for `@viji-*` directives.\r\n\r\n## Scene Structure\r\n\r\nA shader scene is a GLSL fragment shader with comment directives:\r\n\r\n```glsl\r\n// @renderer shader\r\n// @viji-slider:brightness label:\"Brightness\" default:1.0 min:0.0 max:2.0\r\n\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n gl_FragColor = vec4(uv * brightness, 0.5, 1.0);\r\n}\r\n```\r\n\r\n- **No `precision` or `uniform` declarations needed.** Viji auto-injects `precision mediump float;` and all uniform declarations.\r\n- **No vertex shader.** Viji renders a fullscreen quad; your fragment shader defines the color of every pixel.\r\n- **Parameters become uniforms.** `// @viji-slider:name` becomes `uniform float name;` automatically.\r\n\r\n> [!NOTE]\r\n> The Viji shader renderer automatically injects `precision mediump float;` and all `uniform` declarations — both built-in uniforms (`u_resolution`, `u_time`, etc.) and parameter uniforms from `@viji-*` directives. Write only your helper functions and `void main() { ... }`. Do NOT redeclare `precision` or any uniforms — they will conflict. If you use `#version 300 es`, Viji will handle its placement automatically.\r\n\r\n## Parameter Types\r\n\r\nDeclare parameters with `// @viji-TYPE:uniformName key:value` syntax:\r\n\r\n| Type | Uniform | Example |\r\n|------|---------|---------|\r\n| `slider` | `float` | `// @viji-slider:speed label:\"Speed\" default:1.0 min:0.0 max:5.0` |\r\n| `number` | `float` | `// @viji-number:count label:\"Count\" default:10.0 min:1.0 max:100.0` |\r\n| `color` | `vec3` | `// @viji-color:tint label:\"Tint\" default:#ff6600` |\r\n| `toggle` | `bool` | `// @viji-toggle:invert label:\"Invert\" default:false` |\r\n| `select` | `int` | `// @viji-select:mode label:\"Mode\" default:0 options:[\"Wave\",\"Spiral\",\"Grid\"]` |\r\n| `image` | `sampler2D` | `// @viji-image:tex label:\"Texture\"` |\r\n| `button` | `bool` | `// @viji-button:trigger label:\"Trigger\"` |\r\n| `accumulator` | `float` | `// @viji-accumulator:phase rate:speed` |\r\n\r\nConfig keys: `label`, `default`, `min`, `max`, `step`, `description`, `group`, `category`.\r\n\r\n### Accumulators\r\n\r\nAccumulators solve the \"jumping animation\" problem. When you write `u_time * speed`, changing the `speed` slider causes a visible jump because the entire phase is recalculated instantly. Accumulators integrate the rate over time on the CPU side:\r\n\r\n```glsl\r\n// @viji-slider:speed label:\"Speed\" default:1.0 min:0.1 max:5.0\r\n// @viji-accumulator:phase rate:speed\r\n```\r\n\r\n- `phase` increases by `speed × deltaTime` each frame — changing `speed` only affects future growth, never jumps backward.\r\n- `rate` can reference any declared parameter name or be a numeric constant (e.g., `rate:1.5`).\r\n- Accumulators have no UI control — they are internal uniform values.\r\n- Optional `default` sets the starting value (defaults to 0).\r\n\r\n> [!WARNING]\r\n> Do not use the `u_` prefix for your parameter uniform names — it is reserved for built-in Viji uniforms. Use descriptive names like `speed`, `colorMix`, `intensity` instead.\r\n\r\n## Built-in Uniforms\r\n\r\nThese are always available — no declaration needed:\r\n\r\n| Uniform | Type | Description |\r\n|---------|------|-------------|\r\n| `u_resolution` | `vec2` | Canvas width and height in pixels |\r\n| `u_time` | `float` | Elapsed seconds since scene start |\r\n| `u_deltaTime` | `float` | Seconds since last frame |\r\n| `u_frame` | `int` | Current frame number |\r\n| `u_pointer` | `vec2` | Unified input position — mouse or touch (pixels) |\r\n| `u_pointerDown` | `bool` | Unified input active (left-click or touch) |\r\n| `u_mouse` | `vec2` | Mouse position in pixels |\r\n| `u_mousePressed` | `bool` | Any mouse button is pressed |\r\n| `u_audioVolume` | `float` | Overall audio volume (0–1) |\r\n| `u_audioLow` | `float` | Low frequency energy (0–1) |\r\n| `u_audioMid` | `float` | Mid frequency energy (0–1) |\r\n| `u_audioHigh` | `float` | High frequency energy (0–1) |\r\n| `u_audioKick` | `float` | Kick beat detection (0–1) |\r\n| `u_video` | `sampler2D` | Current video frame |\r\n\r\nSee [API Reference](/shader/api-reference) for the complete list of 100+ built-in uniforms.\r\n\r\n## Essential Patterns\r\n\r\n**Normalized coordinates:**\r\n\r\n```glsl\r\nvec2 uv = gl_FragCoord.xy / u_resolution; // 0..1\r\nvec2 centered = uv - 0.5; // -0.5..0.5\r\ncentered.x *= u_resolution.x / u_resolution.y; // aspect-corrected\r\n```\r\n\r\n**Distance fields:**\r\n\r\n```glsl\r\nfloat d = length(centered); // distance from center\r\nfloat circle = smoothstep(0.3, 0.29, d); // anti-aliased circle\r\n```\r\n\r\n> [!NOTE]\r\n> Always use `u_resolution` for positioning and sizing and `u_time` / `u_deltaTime` for animation. This keeps your shader resolution-agnostic and frame-rate-independent.\r\n\r\n## GLSL Version\r\n\r\nBy default, shaders use **GLSL ES 1.00** (WebGL 1). If you need WebGL 2 features, add `#version 300 es` as the first line:\r\n\r\n```glsl\r\n#version 300 es\r\n// @renderer shader\r\n\r\n// ES 3.00 requires explicit output declaration\r\nout vec4 fragColor;\r\n\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n fragColor = vec4(uv, sin(u_time), 1.0);\r\n}\r\n```\r\n\r\nES 3.00 differences: `gl_FragColor` → `out vec4`, `texture2D()` → `texture()`. Use ES 1.00 for maximum compatibility.\r\n\r\n## Backbuffer (Previous Frame)\r\n\r\nViji gives you access to the previous frame as a texture — just reference `backbuffer` in your code and it's automatically enabled:\r\n\r\n```glsl\r\nvoid main() {\r\n vec2 uv = gl_FragCoord.xy / u_resolution;\r\n vec4 prev = texture2D(backbuffer, uv); // previous frame\r\n vec3 current = vec3(/* ... your effect ... */);\r\n gl_FragColor = vec4(mix(prev.rgb, current, 0.1), 1.0); // 90% trail\r\n}\r\n```\r\n\r\nThis enables feedback effects, trails, motion blur, and accumulation buffers. No setup needed — Viji detects the `backbuffer` reference and creates the ping-pong framebuffers automatically.\r\n\r\nSee [Backbuffer](/shader/backbuffer) for detailed patterns and techniques.\r\n\r\n## Shadertoy Compatibility\r\n\r\nIf you have existing Shadertoy shaders, see [Shadertoy Compatibility](/shader/shadertoy) for a mapping of Shadertoy uniforms to Viji equivalents.\r\n\r\n## Next Steps\r\n\r\n- [Shader Basics](/shader/basics) — uniforms, coordinate systems, techniques\r\n- [Parameters](/shader/parameters) — all parameter types for shaders\r\n- [Audio Uniforms](/shader/audio) — react to music in GLSL\r\n- [Backbuffer](/shader/backbuffer) — feedback effects using the previous frame\r\n- [API Reference](/shader/api-reference) — complete list of built-in uniforms\r\n- [Best Practices](/getting-started/best-practices) — essential patterns for all renderers"
|
|
1745
1777
|
}
|
|
1746
1778
|
]
|
|
1747
1779
|
},
|
|
@@ -1762,7 +1794,7 @@ export const docsApi = {
|
|
|
1762
1794
|
},
|
|
1763
1795
|
{
|
|
1764
1796
|
"type": "text",
|
|
1765
|
-
"markdown": "## Auto-Injection\n\n> [!NOTE]\n> The Viji shader renderer automatically injects `precision mediump float;` and all `uniform` declarations. Write only your helper functions and `void main() { ... }`. Do NOT redeclare `precision` or
|
|
1797
|
+
"markdown": "## Auto-Injection\n\n> [!NOTE]\n> The Viji shader renderer automatically injects `precision mediump float;` and all `uniform` declarations — both built-in uniforms (`u_resolution`, `u_time`, etc.) and parameter uniforms from `@viji-*` directives. Write only your helper functions and `void main() { ... }`. Do NOT redeclare `precision` or any uniforms — they will conflict. If you use `#version 300 es`, Viji will handle its placement automatically.\n\nWhen your shader is compiled, Viji prepends the following before your code:\n\n1. `#extension GL_OES_standard_derivatives : enable` — only if your code uses `fwidth` (GLSL ES 1.00 only)\n2. `precision mediump float;`\n3. All built-in uniform declarations (`u_resolution`, `u_time`, `u_deltaTime`, etc.)\n4. All `@viji-*` parameter uniform declarations\n\n**You must not redeclare any of these.** Writing `precision mediump float;` or `uniform vec2 u_resolution;` in your code will cause a compilation error.\n\n### What You Write vs What Viji Adds\n\n```glsl\n// What you write:\n// @renderer shader\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n gl_FragColor = vec4(uv, sin(u_time), 1.0);\n}\n\n// What Viji compiles (conceptual):\nprecision mediump float;\nuniform vec2 u_resolution;\nuniform float u_time;\nuniform float u_deltaTime;\nuniform int u_frame;\nuniform float u_fps;\n// ... (100+ more built-in uniforms)\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n gl_FragColor = vec4(uv, sin(u_time), 1.0);\n}\n```\n\n## Key Built-in Uniforms\n\nThese are always available — a brief overview (each has a dedicated page):\n\n| Uniform | Type | Description | Details |\n|---------|------|-------------|---------|\n| [`u_resolution`](/shader/resolution) | `vec2` | Canvas width and height in pixels | [Resolution & Coordinates](/shader/resolution) |\n| [`u_time`](/shader/timing) | `float` | Elapsed seconds since scene start | [Timing & Animation](/shader/timing) |\n| [`u_deltaTime`](/shader/timing) | `float` | Seconds since last frame | [Timing & Animation](/shader/timing) |\n| [`u_frame`](/shader/timing) | `int` | Current frame number | [Timing & Animation](/shader/timing) |\n| [`u_fps`](/shader/timing) | `float` | Target FPS | [Timing & Animation](/shader/timing) |\n| `u_mouse` | `vec2` | Mouse position in pixels | [Mouse Uniforms](/shader/mouse) |\n\nSee [API Reference](/shader/api-reference) for the complete list of 100+ built-in uniforms.\n\n## GLSL Versions\n\n### GLSL ES 1.00 (Default)\n\nThe default. No `#version` declaration needed. Maximum browser compatibility. Uses `gl_FragColor` for output and `texture2D()` for texture sampling:\n\n```glsl\n// @renderer shader\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n gl_FragColor = vec4(uv, 0.5, 1.0);\n}\n```\n\n### GLSL ES 3.00 (WebGL 2)\n\nFor WebGL 2 features, add `#version 300 es` as the very first line (before the `@renderer` directive). Viji extracts it, places it at the top of the compiled output, and requests a WebGL 2 context:"
|
|
1766
1798
|
},
|
|
1767
1799
|
{
|
|
1768
1800
|
"type": "live-example",
|
|
@@ -1856,17 +1888,17 @@ export const docsApi = {
|
|
|
1856
1888
|
"content": [
|
|
1857
1889
|
{
|
|
1858
1890
|
"type": "text",
|
|
1859
|
-
"markdown": "# @viji-slider\n\n```glsl\n// @viji-slider:speed label:\"Speed\" default:1 min:0 max:5 step:0.1\nuniform float speed;\n```\n\nDeclares a numeric slider parameter. The host renders it as a draggable slider control. The value is injected as a `uniform float`.\n\n## Directive Syntax\n\n```\n// @viji-slider:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial value |\n| `min` | No | `0` | Minimum allowed value |\n| `max` | No | `100` | Maximum allowed value |\n| `step` | No | `1` | Increment between values |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe slider value is always injected as a `float`:\n\n```glsl\n// @viji-slider:count label:\"Count\" default:8 min:1 max:20 step:1\nuniform float count; // always float, use int(count) if needed\n```\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-slider:zoom label:\"Zoom\" default:1 min:0.1 max:5 step:0.1\n// @viji-slider:rotation label:\"Rotation\" default:0 min:0 max:6.2832 step:0.01\n\
|
|
1891
|
+
"markdown": "# @viji-slider\n\n```glsl\n// @viji-slider:speed label:\"Speed\" default:1 min:0 max:5 step:0.1\nuniform float speed;\n```\n\nDeclares a numeric slider parameter. The host renders it as a draggable slider control. The value is injected as a `uniform float`.\n\n## Directive Syntax\n\n```\n// @viji-slider:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial value |\n| `min` | No | `0` | Minimum allowed value |\n| `max` | No | `100` | Maximum allowed value |\n| `step` | No | `1` | Increment between values |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe slider value is always injected as a `float`:\n\n```glsl\n// @viji-slider:count label:\"Count\" default:8 min:1 max:20 step:1\nuniform float count; // always float, use int(count) if needed\n```\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-slider:zoom label:\"Zoom\" default:1 min:0.1 max:5 step:0.1\n// @viji-slider:rotation label:\"Rotation\" default:0 min:0 max:6.2832 step:0.01\n\nvoid main() {\n vec2 uv = (gl_FragCoord.xy / u_resolution - 0.5) * zoom;\n float c = cos(rotation), s = sin(rotation);\n uv = mat2(c, -s, s, c) * uv;\n float d = length(uv);\n float ring = sin(d * 20.0 - u_time * 3.0) * 0.5 + 0.5;\n gl_FragColor = vec4(vec3(ring), 1.0);\n}\n```\n\n> [!WARNING]\n> The directive must use `//` comments only. Block comments (`/* */`) are not parsed.\n\n> [!NOTE]\n> Viji auto-injects all `uniform` declarations — both built-in uniforms and parameter uniforms from directives. Do **not** redeclare them in your shader code; duplicate declarations cause compilation errors."
|
|
1860
1892
|
},
|
|
1861
1893
|
{
|
|
1862
1894
|
"type": "live-example",
|
|
1863
1895
|
"title": "Slider Controls",
|
|
1864
|
-
"sceneCode": "// @renderer shader\n// @viji-slider:zoom label:\"Zoom\" default:1 min:0.1 max:5 step:0.1\n// @viji-slider:speed label:\"Speed\" default:3 min:0 max:10 step:0.5\n// @viji-color:ringColor label:\"Color\" default:#44ddff\n\
|
|
1896
|
+
"sceneCode": "// @renderer shader\n// @viji-slider:zoom label:\"Zoom\" default:1 min:0.1 max:5 step:0.1\n// @viji-slider:speed label:\"Speed\" default:3 min:0 max:10 step:0.5\n// @viji-color:ringColor label:\"Color\" default:#44ddff\n\nvoid main() {\n vec2 uv = (gl_FragCoord.xy / u_resolution - 0.5) * zoom;\n float aspect = u_resolution.x / u_resolution.y;\n uv.x *= aspect;\n\n float d = length(uv);\n float ring = sin(d * 20.0 - u_time * speed) * 0.5 + 0.5;\n vec3 col = ringColor * ring;\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
1865
1897
|
"sceneFile": "slider-shader.scene.glsl"
|
|
1866
1898
|
},
|
|
1867
1899
|
{
|
|
1868
1900
|
"type": "text",
|
|
1869
|
-
"markdown": "## Slider vs Number in Shaders\n\nBoth `@viji-slider` and `@viji-number` produce a `uniform float` and accept the same config keys (`min`, `max`, `step`, `default`). The only difference is the host UI:\n\n| | @viji-slider | @viji-number |\n|--|--------------|--------------|\n| UI | Draggable track | Text input field |\n| Best for | Continuous ranges, visual tuning | Precise values, integer counts |\n\n## Smooth Animation with Accumulators\n\nA common pattern is using a slider to control animation speed:\n\n```glsl\nfloat phase = u_time * speed; // jumps when speed changes mid-animation\n```\n\nThe problem: if the user changes `speed` from `1.0` to `3.0` at `u_time = 10`, the phase jumps from `10` to `30` instantly. The [`@viji-accumulator`](../accumulator/) solves this by integrating the rate over time — changing the rate only affects future growth, never jumps:\n\n```glsl\n// @viji-slider:speed label:\"Speed\" default:1 min:0 max:5 step:0.1\n// @viji-accumulator:phase rate:speed\n
|
|
1901
|
+
"markdown": "## Slider vs Number in Shaders\n\nBoth `@viji-slider` and `@viji-number` produce a `uniform float` and accept the same config keys (`min`, `max`, `step`, `default`). The only difference is the host UI:\n\n| | @viji-slider | @viji-number |\n|--|--------------|--------------|\n| UI | Draggable track | Text input field |\n| Best for | Continuous ranges, visual tuning | Precise values, integer counts |\n\n## Smooth Animation with Accumulators\n\nA common pattern is using a slider to control animation speed:\n\n```glsl\nfloat phase = u_time * speed; // jumps when speed changes mid-animation\n```\n\nThe problem: if the user changes `speed` from `1.0` to `3.0` at `u_time = 10`, the phase jumps from `10` to `30` instantly. The [`@viji-accumulator`](../accumulator/) solves this by integrating the rate over time — changing the rate only affects future growth, never jumps:\n\n```glsl\n// @viji-slider:speed label:\"Speed\" default:1 min:0 max:5 step:0.1\n// @viji-accumulator:phase rate:speed\n// Generates: uniform float speed; and uniform float phase;\n// phase grows by speed × deltaTime each frame, no jumps\n```\n\nSee [Accumulator](../accumulator/) for full details and examples.\n\n## Rules\n\n- Numeric values have no quotes: `default:1`, `min:0`, `max:5`\n- String values use quotes: `label:\"Speed\"`\n- The `label` and `default` keys are required\n\n## Related\n\n- [Shader Basics](/shader/basics) — shader file structure and directives\n- [Number](../number/) — numeric input `uniform float`\n- [Color](../color/) — color picker `uniform vec3`\n- [Toggle](../toggle/) — boolean `uniform bool`\n- [Select](../select/) — dropdown `uniform int`\n- [Accumulator](../accumulator/) — frame-persistent state driven by slider values\n- [Native Slider](/native/parameters/slider) — equivalent for the Native renderer\n- [P5 Slider](/p5/parameters/slider) — equivalent for the P5 renderer"
|
|
1870
1902
|
}
|
|
1871
1903
|
]
|
|
1872
1904
|
},
|
|
@@ -1877,12 +1909,12 @@ export const docsApi = {
|
|
|
1877
1909
|
"content": [
|
|
1878
1910
|
{
|
|
1879
1911
|
"type": "text",
|
|
1880
|
-
"markdown": "# @viji-color\n\n```glsl\n// @viji-color:myColor label:\"Color\" default:#ff6600\nuniform vec3 myColor;\n```\n\nDeclares a color picker parameter. The host renders a color swatch that opens a full picker when clicked. The hex value is converted to a `vec3` uniform with RGB components normalized to `0.0–1.0`.\n\n## Directive Syntax\n\n```\n// @viji-color:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial hex color (e.g., `#ff6600`) — **no quotes** |\n| `description` | No | — | Tooltip text (use quotes: `description:\"Help text\"`) |\n| `group` | No | `general` | Group name (use quotes: `group:\"colors\"`) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe color is injected as a `vec3`:\n\n| Component | Value | Range |\n|-----------|-------|-------|\n| `.r` | Red channel | 0.0 – 1.0 |\n| `.g` | Green channel | 0.0 – 1.0 |\n| `.b` | Blue channel | 0.0 – 1.0 |\n\nFor hex `#ff8040`: `.r = 1.0`, `.g ≈ 0.502`, `.b ≈ 0.251`.\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-color:bgColor label:\"Background\" default:#0f0f1a\n// @viji-color:accent label:\"Accent\" default:#ff4488\n\
|
|
1912
|
+
"markdown": "# @viji-color\n\n```glsl\n// @viji-color:myColor label:\"Color\" default:#ff6600\nuniform vec3 myColor;\n```\n\nDeclares a color picker parameter. The host renders a color swatch that opens a full picker when clicked. The hex value is converted to a `vec3` uniform with RGB components normalized to `0.0–1.0`.\n\n## Directive Syntax\n\n```\n// @viji-color:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial hex color (e.g., `#ff6600`) — **no quotes** |\n| `description` | No | — | Tooltip text (use quotes: `description:\"Help text\"`) |\n| `group` | No | `general` | Group name (use quotes: `group:\"colors\"`) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe color is injected as a `vec3`:\n\n| Component | Value | Range |\n|-----------|-------|-------|\n| `.r` | Red channel | 0.0 – 1.0 |\n| `.g` | Green channel | 0.0 – 1.0 |\n| `.b` | Blue channel | 0.0 – 1.0 |\n\nFor hex `#ff8040`: `.r = 1.0`, `.g ≈ 0.502`, `.b ≈ 0.251`.\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-color:bgColor label:\"Background\" default:#0f0f1a\n// @viji-color:accent label:\"Accent\" default:#ff4488\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n float d = distance(uv, vec2(0.5));\n vec3 col = mix(accent, bgColor, smoothstep(0.1, 0.4, d));\n gl_FragColor = vec4(col, 1.0);\n}\n```\n\n> [!WARNING]\n> The directive must use `//` comments only. Block comments (`/* */`) are not parsed.\n\n> [!NOTE]\n> Viji auto-injects all `uniform` declarations — both built-in uniforms and parameter uniforms from directives. Do **not** redeclare them in your shader code; duplicate declarations cause compilation errors."
|
|
1881
1913
|
},
|
|
1882
1914
|
{
|
|
1883
1915
|
"type": "live-example",
|
|
1884
1916
|
"title": "Color Blending",
|
|
1885
|
-
"sceneCode": "// @renderer shader\n// @viji-color:bgColor label:\"Background\" default:#0f0f1a\n// @viji-color:color1 label:\"Color 1\" default:#ff4488 group:\"colors\"\n// @viji-color:color2 label:\"Color 2\" default:#4488ff group:\"colors\"\n\
|
|
1917
|
+
"sceneCode": "// @renderer shader\n// @viji-color:bgColor label:\"Background\" default:#0f0f1a\n// @viji-color:color1 label:\"Color 1\" default:#ff4488 group:\"colors\"\n// @viji-color:color2 label:\"Color 2\" default:#4488ff group:\"colors\"\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n float t = sin(uv.x * 6.2832 + u_time) * 0.5 + 0.5;\n vec3 gradient = mix(color1, color2, t);\n float d = distance(uv, vec2(0.5));\n vec3 col = mix(gradient, bgColor, smoothstep(0.2, 0.5, d));\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
1886
1918
|
"sceneFile": "color-shader.scene.glsl"
|
|
1887
1919
|
},
|
|
1888
1920
|
{
|
|
@@ -1898,12 +1930,12 @@ export const docsApi = {
|
|
|
1898
1930
|
"content": [
|
|
1899
1931
|
{
|
|
1900
1932
|
"type": "text",
|
|
1901
|
-
"markdown": "# @viji-toggle\n\n```glsl\n// @viji-toggle:showGrid label:\"Show Grid\" default:true\nuniform bool showGrid;\n```\n\nDeclares a boolean toggle parameter. The host renders it as an on/off switch. The value is injected as a `uniform bool`.\n\n## Directive Syntax\n\n```\n// @viji-toggle:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial state: `true` or `false` |\n| `description` | No | — | Tooltip text (use quotes: `description:\"Help text\"`) |\n| `group` | No | `general` | Group name (use quotes: `group:\"effects\"`) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe toggle is injected as a `bool`:\n\n| Value | GLSL |\n|-------|------|\n| On | `true` |\n| Off | `false` |\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-toggle:invert label:\"Invert Colors\" default:false\n\
|
|
1933
|
+
"markdown": "# @viji-toggle\n\n```glsl\n// @viji-toggle:showGrid label:\"Show Grid\" default:true\nuniform bool showGrid;\n```\n\nDeclares a boolean toggle parameter. The host renders it as an on/off switch. The value is injected as a `uniform bool`.\n\n## Directive Syntax\n\n```\n// @viji-toggle:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial state: `true` or `false` |\n| `description` | No | — | Tooltip text (use quotes: `description:\"Help text\"`) |\n| `group` | No | `general` | Group name (use quotes: `group:\"effects\"`) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe toggle is injected as a `bool`:\n\n| Value | GLSL |\n|-------|------|\n| On | `true` |\n| Off | `false` |\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-toggle:invert label:\"Invert Colors\" default:false\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec3 col = vec3(uv.x, uv.y, sin(u_time) * 0.5 + 0.5);\n if (invert) {\n col = 1.0 - col;\n }\n gl_FragColor = vec4(col, 1.0);\n}\n```\n\n> [!WARNING]\n> The directive must use `//` comments only. Block comments (`/* */`) are not parsed.\n\n> [!NOTE]\n> Viji auto-injects all `uniform` declarations — both built-in uniforms and parameter uniforms from directives. Do **not** redeclare them in your shader code; duplicate declarations cause compilation errors."
|
|
1902
1934
|
},
|
|
1903
1935
|
{
|
|
1904
1936
|
"type": "live-example",
|
|
1905
1937
|
"title": "Toggle Inversion",
|
|
1906
|
-
"sceneCode": "// @renderer shader\n// @viji-toggle:invert label:\"Invert\" default:false\n// @viji-toggle:animate label:\"Animate\" default:true\n// @viji-color:baseColor label:\"Base Color\" default:#4488ff\n\
|
|
1938
|
+
"sceneCode": "// @renderer shader\n// @viji-toggle:invert label:\"Invert\" default:false\n// @viji-toggle:animate label:\"Animate\" default:true\n// @viji-color:baseColor label:\"Base Color\" default:#4488ff\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n float t = animate ? u_time : 0.0;\n float pattern = sin(uv.x * 10.0 + t * 2.0) * sin(uv.y * 10.0 + t * 1.5);\n vec3 col = baseColor * (pattern * 0.5 + 0.5);\n if (invert) {\n col = 1.0 - col;\n }\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
1907
1939
|
"sceneFile": "toggle-shader.scene.glsl"
|
|
1908
1940
|
},
|
|
1909
1941
|
{
|
|
@@ -1919,12 +1951,12 @@ export const docsApi = {
|
|
|
1919
1951
|
"content": [
|
|
1920
1952
|
{
|
|
1921
1953
|
"type": "text",
|
|
1922
|
-
"markdown": "# @viji-select\n\n```glsl\n// @viji-select:mode label:\"Mode\" options:[\"Wave\",\"Circles\",\"Grid\"] default:0\nuniform int mode;\n```\n\nDeclares a dropdown selection parameter. The host renders it as a dropdown menu. The selected option's **index** is injected as a `uniform int`.\n\n## Directive Syntax\n\n```\n// @viji-select:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `options` | Yes | — | Array of string choices: `options:[\"A\",\"B\",\"C\"]` |\n| `default` | Yes | — | Initially selected index (0-based integer) |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe select value is injected as an `int` — the **zero-based index** of the selected option:\n\n| Selected Option | GLSL Value |\n|-----------------|------------|\n| First option | `0` |\n| Second option | `1` |\n| Third option | `2` |\n\nThis differs from Native and P5 renderers, where `.value` is the option string itself.\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-select:pattern label:\"Pattern\" options:[\"Stripes\",\"Dots\",\"Checker\"] default:0\n\
|
|
1954
|
+
"markdown": "# @viji-select\n\n```glsl\n// @viji-select:mode label:\"Mode\" options:[\"Wave\",\"Circles\",\"Grid\"] default:0\nuniform int mode;\n```\n\nDeclares a dropdown selection parameter. The host renders it as a dropdown menu. The selected option's **index** is injected as a `uniform int`.\n\n## Directive Syntax\n\n```\n// @viji-select:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `options` | Yes | — | Array of string choices: `options:[\"A\",\"B\",\"C\"]` |\n| `default` | Yes | — | Initially selected index (0-based integer) |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe select value is injected as an `int` — the **zero-based index** of the selected option:\n\n| Selected Option | GLSL Value |\n|-----------------|------------|\n| First option | `0` |\n| Second option | `1` |\n| Third option | `2` |\n\nThis differs from Native and P5 renderers, where `.value` is the option string itself.\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-select:pattern label:\"Pattern\" options:[\"Stripes\",\"Dots\",\"Checker\"] default:0\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n float v = 0.0;\n\n if (pattern == 0) {\n v = step(0.5, fract(uv.x * 10.0 + u_time));\n } else if (pattern == 1) {\n v = 1.0 - step(0.3, length(fract(uv * 5.0) - 0.5));\n } else if (pattern == 2) {\n v = mod(floor(uv.x * 8.0) + floor(uv.y * 8.0), 2.0);\n }\n\n gl_FragColor = vec4(vec3(v), 1.0);\n}\n```\n\n> [!WARNING]\n> The directive must use `//` comments only. Block comments (`/* */`) are not parsed.\n\n> [!NOTE]\n> Viji auto-injects all `uniform` declarations — both built-in uniforms and parameter uniforms from directives. Do **not** redeclare them in your shader code; duplicate declarations cause compilation errors."
|
|
1923
1955
|
},
|
|
1924
1956
|
{
|
|
1925
1957
|
"type": "live-example",
|
|
1926
1958
|
"title": "Pattern Selector",
|
|
1927
|
-
"sceneCode": "// @renderer shader\n// @viji-select:pattern label:\"Pattern\" options:[\"Stripes\",\"Dots\",\"Checker\"] default:0\n// @viji-color:color1 label:\"Color\" default:#ff4488\n\
|
|
1959
|
+
"sceneCode": "// @renderer shader\n// @viji-select:pattern label:\"Pattern\" options:[\"Stripes\",\"Dots\",\"Checker\"] default:0\n// @viji-color:color1 label:\"Color\" default:#ff4488\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n float v = 0.0;\n\n if (pattern == 0) {\n v = step(0.5, fract(uv.x * 10.0 + u_time));\n } else if (pattern == 1) {\n v = 1.0 - step(0.3, length(fract(uv * 5.0) - 0.5));\n } else {\n v = mod(floor(uv.x * 8.0) + floor(uv.y * 8.0), 2.0);\n }\n\n vec3 col = mix(vec3(0.06), color1, v);\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
1928
1960
|
"sceneFile": "select-shader.scene.glsl"
|
|
1929
1961
|
},
|
|
1930
1962
|
{
|
|
@@ -1940,17 +1972,17 @@ export const docsApi = {
|
|
|
1940
1972
|
"content": [
|
|
1941
1973
|
{
|
|
1942
1974
|
"type": "text",
|
|
1943
|
-
"markdown": "# @viji-number\n\n```glsl\n// @viji-number:density label:\"Density\" default:5 min:1 max:20 step:1\nuniform float density;\n```\n\nDeclares a numeric input parameter. The host renders it as a direct number input field. The value is injected as a `uniform float`.\n\n## Directive Syntax\n\n```\n// @viji-number:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial value |\n| `min` | No | `0` | Minimum allowed value |\n| `max` | No | `100` | Maximum allowed value |\n| `step` | No | `1` | Increment between values |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe number is always injected as a `float`, even when configured with integer steps:\n\n```glsl\n// @viji-number:count label:\"Count\" default:8 min:1 max:20 step:1\nuniform float count; // always float, use int(count) if needed\n```\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-number:rings label:\"Rings\" default:5 min:1 max:15 step:1\n// @viji-color:ringColor label:\"Color\" default:#44ddff\n\
|
|
1975
|
+
"markdown": "# @viji-number\n\n```glsl\n// @viji-number:density label:\"Density\" default:5 min:1 max:20 step:1\nuniform float density;\n```\n\nDeclares a numeric input parameter. The host renders it as a direct number input field. The value is injected as a `uniform float`.\n\n## Directive Syntax\n\n```\n// @viji-number:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `default` | Yes | — | Initial value |\n| `min` | No | `0` | Minimum allowed value |\n| `max` | No | `100` | Maximum allowed value |\n| `step` | No | `1` | Increment between values |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\n## Uniform Type\n\nThe number is always injected as a `float`, even when configured with integer steps:\n\n```glsl\n// @viji-number:count label:\"Count\" default:8 min:1 max:20 step:1\nuniform float count; // always float, use int(count) if needed\n```\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-number:rings label:\"Rings\" default:5 min:1 max:15 step:1\n// @viji-color:ringColor label:\"Color\" default:#44ddff\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec2 center = uv - 0.5;\n float aspect = u_resolution.x / u_resolution.y;\n center.x *= aspect;\n\n float d = length(center);\n float wave = sin(d * rings * 6.2832 - u_time * 3.0) * 0.5 + 0.5;\n vec3 col = ringColor * wave;\n\n gl_FragColor = vec4(col, 1.0);\n}\n```\n\n> [!WARNING]\n> The directive must use `//` comments only. Block comments (`/* */`) are not parsed.\n\n> [!NOTE]\n> Viji auto-injects all `uniform` declarations — both built-in uniforms and parameter uniforms from directives. Do **not** redeclare them in your shader code; duplicate declarations cause compilation errors."
|
|
1944
1976
|
},
|
|
1945
1977
|
{
|
|
1946
1978
|
"type": "live-example",
|
|
1947
1979
|
"title": "Ring Count",
|
|
1948
|
-
"sceneCode": "// @renderer shader\n// @viji-number:rings label:\"Rings\" default:5 min:1 max:15 step:1\n// @viji-number:speed label:\"Speed\" default:3 min:0 max:10 step:0.5\n// @viji-color:ringColor label:\"Color\" default:#44ddff\n\
|
|
1980
|
+
"sceneCode": "// @renderer shader\n// @viji-number:rings label:\"Rings\" default:5 min:1 max:15 step:1\n// @viji-number:speed label:\"Speed\" default:3 min:0 max:10 step:0.5\n// @viji-color:ringColor label:\"Color\" default:#44ddff\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec2 center = uv - 0.5;\n float aspect = u_resolution.x / u_resolution.y;\n center.x *= aspect;\n\n float d = length(center);\n float wave = sin(d * rings * 6.2832 - u_time * speed) * 0.5 + 0.5;\n vec3 col = ringColor * wave;\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
1949
1981
|
"sceneFile": "number-shader.scene.glsl"
|
|
1950
1982
|
},
|
|
1951
1983
|
{
|
|
1952
1984
|
"type": "text",
|
|
1953
|
-
"markdown": "## Number vs Slider in Shaders\n\nBoth `@viji-number` and `@viji-slider` produce a `uniform float` and accept the same config keys (`min`, `max`, `step`, `default`). The only difference is the host UI:\n\n| | @viji-slider | @viji-number |\n|--|--------------|--------------|\n| UI | Draggable track | Text input field |\n| Best for | Continuous ranges, visual tuning | Precise values, integer counts |\n\n## Smooth Animation with Accumulators\n\nWhen a number parameter controls animation speed, multiplying by `u_time` directly causes jumps when the value changes. The [`@viji-accumulator`](../accumulator/) integrates the rate over time for smooth transitions:\n\n```glsl\n// @viji-number:bpm label:\"BPM\" default:120 min:30 max:300 step:1\n// @viji-accumulator:beat rate:bpm\n
|
|
1985
|
+
"markdown": "## Number vs Slider in Shaders\n\nBoth `@viji-number` and `@viji-slider` produce a `uniform float` and accept the same config keys (`min`, `max`, `step`, `default`). The only difference is the host UI:\n\n| | @viji-slider | @viji-number |\n|--|--------------|--------------|\n| UI | Draggable track | Text input field |\n| Best for | Continuous ranges, visual tuning | Precise values, integer counts |\n\n## Smooth Animation with Accumulators\n\nWhen a number parameter controls animation speed, multiplying by `u_time` directly causes jumps when the value changes. The [`@viji-accumulator`](../accumulator/) integrates the rate over time for smooth transitions:\n\n```glsl\n// @viji-number:bpm label:\"BPM\" default:120 min:30 max:300 step:1\n// @viji-accumulator:beat rate:bpm\n// Generates: uniform float bpm; and uniform float beat;\n// beat grows by bpm × deltaTime each frame, no jumps\n```\n\nSee [Accumulator](../accumulator/) for full details and examples.\n\n## Rules\n\n- Numeric values have no quotes: `default:5`, `min:1`, `max:20`\n- String values use quotes: `label:\"Density\"`\n- The `label` and `default` keys are required\n\n## Related\n\n- [Shader Basics](/shader/basics) — shader file structure and directives\n- [Slider](/shader/parameters/slider) — numeric slider `uniform float`\n- [Toggle](../toggle/) — boolean `uniform bool`\n- [Select](../select/) — dropdown `uniform int`\n- [Accumulator](../accumulator/) — frame-persistent state driven by numeric values\n- [Native Number](/native/parameters/number) — equivalent for the Native renderer\n- [P5 Number](/p5/parameters/number) — equivalent for the P5 renderer"
|
|
1954
1986
|
}
|
|
1955
1987
|
]
|
|
1956
1988
|
},
|
|
@@ -1961,12 +1993,12 @@ export const docsApi = {
|
|
|
1961
1993
|
"content": [
|
|
1962
1994
|
{
|
|
1963
1995
|
"type": "text",
|
|
1964
|
-
"markdown": "# @viji-image\n\n```glsl\n// @viji-image:tex label:\"Texture\"\nuniform sampler2D tex;\n```\n\nDeclares an image upload parameter. The host renders a file picker or drag-and-drop area. The uploaded image is bound as a WebGL texture and accessed via a `uniform sampler2D`.\n\n## Directive Syntax\n\n```\n// @viji-image:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\nImage parameters do not have a `default` key — the default is always \"no image\" until the user uploads one.\n\n## Uniform Type\n\nThe image is injected as a `sampler2D`. When no image is uploaded, the texture contains a single transparent black pixel (`vec4(0.0)`).\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-image:tex label:\"Texture\"\n\
|
|
1996
|
+
"markdown": "# @viji-image\n\n```glsl\n// @viji-image:tex label:\"Texture\"\nuniform sampler2D tex;\n```\n\nDeclares an image upload parameter. The host renders a file picker or drag-and-drop area. The uploaded image is bound as a WebGL texture and accessed via a `uniform sampler2D`.\n\n## Directive Syntax\n\n```\n// @viji-image:uniformName key:value key:value ...\n```\n\n| Key | Required | Default | Description |\n|-----|----------|---------|-------------|\n| `label` | Yes | — | Display name in the parameter UI |\n| `description` | No | — | Tooltip text (use quotes) |\n| `group` | No | `general` | Group name (use quotes) |\n| `category` | No | `general` | Visibility category |\n\nImage parameters do not have a `default` key — the default is always \"no image\" until the user uploads one.\n\n## Uniform Type\n\nThe image is injected as a `sampler2D`. When no image is uploaded, the texture contains a single transparent black pixel (`vec4(0.0)`).\n\n## Usage\n\n```glsl\n// @renderer shader\n// @viji-image:tex label:\"Texture\"\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec4 img = texture2D(tex, uv);\n gl_FragColor = img;\n}\n```\n\n> [!WARNING]\n> The directive must use `//` comments only. Block comments (`/* */`) are not parsed.\n\n> [!NOTE]\n> Viji auto-injects all `uniform` declarations — both built-in uniforms and parameter uniforms from directives. Do **not** redeclare them in your shader code; duplicate declarations cause compilation errors."
|
|
1965
1997
|
},
|
|
1966
1998
|
{
|
|
1967
1999
|
"type": "live-example",
|
|
1968
2000
|
"title": "Image Texture",
|
|
1969
|
-
"sceneCode": "// @renderer shader\n// @viji-image:tex label:\"Texture\"\n// @viji-slider:distort label:\"Distortion\" default:0.02 min:0 max:0.1 step:0.005\n// @viji-color:tint label:\"Tint\" default:#ffffff\n\
|
|
2001
|
+
"sceneCode": "// @renderer shader\n// @viji-image:tex label:\"Texture\"\n// @viji-slider:distort label:\"Distortion\" default:0.02 min:0 max:0.1 step:0.005\n// @viji-color:tint label:\"Tint\" default:#ffffff\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec2 d = vec2(\n sin(uv.y * 10.0 + u_time * 2.0) * distort,\n cos(uv.x * 10.0 + u_time * 2.0) * distort\n );\n vec4 img = texture2D(tex, uv + d);\n\n float hasImage = step(0.001, img.a);\n vec3 fallback = vec3(uv, sin(u_time) * 0.5 + 0.5);\n vec3 col = mix(fallback, img.rgb * tint, hasImage);\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
1970
2002
|
"sceneFile": "image-shader.scene.glsl"
|
|
1971
2003
|
},
|
|
1972
2004
|
{
|
|
@@ -2061,7 +2093,11 @@ export const docsApi = {
|
|
|
2061
2093
|
"type": "live-example",
|
|
2062
2094
|
"title": "Shader Parameter Categories",
|
|
2063
2095
|
"sceneCode": "// @renderer shader\r\n// @viji-color:tint label:\"Color\" default:#4488ff category:general\r\n// @viji-slider:audioPulse label:\"Audio Pulse\" default:0.3 min:0.0 max:1.0 category:audio\r\n// @viji-slider:mouseSize label:\"Mouse Glow\" default:0.15 min:0.0 max:0.5 category:interaction\r\n// @viji-accumulator:phase rate:1.0\r\n\r\nvoid main() {\r\n vec2 uv = (2.0 * gl_FragCoord.xy - u_resolution) / u_resolution.y;\r\n float d = length(uv);\r\n\r\n float pulse = u_audioVolume * audioPulse;\r\n float wave = sin(d * 15.0 - phase * 3.0) * 0.5 + 0.5;\r\n vec3 col = tint * wave * (1.0 + pulse);\r\n\r\n vec2 mouseUV = (2.0 * u_mouse - u_resolution) / u_resolution.y;\r\n float mouseDist = length(uv - mouseUV);\r\n float glow = mouseSize / (mouseDist + 0.05);\r\n col += vec3(glow * 0.3);\r\n\r\n col *= smoothstep(1.5, 0.3, d);\r\n gl_FragColor = vec4(col, 1.0);\r\n}\r\n",
|
|
2064
|
-
"sceneFile": "categories-demo.scene.glsl"
|
|
2096
|
+
"sceneFile": "categories-demo.scene.glsl",
|
|
2097
|
+
"capabilities": {
|
|
2098
|
+
"audio": true,
|
|
2099
|
+
"interaction": true
|
|
2100
|
+
}
|
|
2065
2101
|
},
|
|
2066
2102
|
{
|
|
2067
2103
|
"type": "text",
|
|
@@ -2082,7 +2118,10 @@ export const docsApi = {
|
|
|
2082
2118
|
"type": "live-example",
|
|
2083
2119
|
"title": "Pointer — Click Flash & Glow",
|
|
2084
2120
|
"sceneCode": "// @renderer shader\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec2 pNorm = u_pointer / u_resolution;\n\n vec3 col = vec3(0.04, 0.04, 0.08);\n\n float dist = length(uv - pNorm);\n float glow = 0.015 / (dist + 0.01);\n vec3 glowColor = u_pointerDown\n ? vec3(0.3, 0.7, 1.0)\n : vec3(0.5, 0.5, 0.6);\n col += glow * glowColor;\n\n float flash = u_pointerWasPressed ? 1.0 : 0.0;\n col += vec3(0.2, 0.5, 0.8) * flash * smoothstep(0.3, 0.0, dist);\n\n float release = u_pointerWasReleased ? 1.0 : 0.0;\n float ring = smoothstep(0.002, 0.0, abs(dist - 0.15)) * release;\n col += vec3(0.8, 0.4, 0.2) * ring;\n\n if (!u_pointerInCanvas) {\n col *= 0.5;\n }\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
2085
|
-
"sceneFile": "pointer-shader-demo.scene.glsl"
|
|
2121
|
+
"sceneFile": "pointer-shader-demo.scene.glsl",
|
|
2122
|
+
"capabilities": {
|
|
2123
|
+
"interaction": true
|
|
2124
|
+
}
|
|
2086
2125
|
},
|
|
2087
2126
|
{
|
|
2088
2127
|
"type": "text",
|
|
@@ -2103,7 +2142,10 @@ export const docsApi = {
|
|
|
2103
2142
|
"type": "live-example",
|
|
2104
2143
|
"title": "Mouse — Glow & Button States",
|
|
2105
2144
|
"sceneCode": "// @renderer shader\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n vec2 mNorm = u_mouse / u_resolution;\n\n vec3 col = vec3(0.04, 0.04, 0.08);\n\n float dist = length(uv - mNorm);\n float glow = 0.012 / (dist + 0.01);\n\n vec3 btnColor = vec3(0.5);\n if (u_mouseLeft) btnColor = vec3(0.3, 0.8, 1.0);\n if (u_mouseRight) btnColor = vec3(1.0, 0.4, 0.3);\n if (u_mouseMiddle) btnColor = vec3(0.3, 1.0, 0.5);\n col += glow * btnColor;\n\n float flash = u_mouseWasPressed ? 1.0 : 0.0;\n col += vec3(0.3) * flash * smoothstep(0.2, 0.0, dist);\n\n float speed = length(u_mouseDelta) / max(u_resolution.x, 1.0);\n col += vec3(0.0, 0.2, 0.5) * speed * 5.0 * smoothstep(0.3, 0.0, dist);\n\n float wheelVis = clamp(u_mouseWheel * 0.001, -1.0, 1.0);\n col.r += abs(wheelVis) * 0.15 * smoothstep(0.5, 0.0, dist);\n\n if (!u_mouseInCanvas) {\n col *= 0.4;\n }\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
2106
|
-
"sceneFile": "mouse-shader-demo.scene.glsl"
|
|
2145
|
+
"sceneFile": "mouse-shader-demo.scene.glsl",
|
|
2146
|
+
"capabilities": {
|
|
2147
|
+
"interaction": true
|
|
2148
|
+
}
|
|
2107
2149
|
},
|
|
2108
2150
|
{
|
|
2109
2151
|
"type": "text",
|
|
@@ -2124,7 +2166,10 @@ export const docsApi = {
|
|
|
2124
2166
|
"type": "live-example",
|
|
2125
2167
|
"title": "Keyboard — WASD Movement",
|
|
2126
2168
|
"sceneCode": "// @renderer shader\n\n// Smooth WASD/arrow movement via accumulators\n// @viji-accumulator:moveRight rate:u_keyD\n// @viji-accumulator:moveLeft rate:u_keyA\n// @viji-accumulator:moveUp rate:u_keyW\n// @viji-accumulator:moveDown rate:u_keyS\n// @viji-accumulator:arRight rate:u_keyRight\n// @viji-accumulator:arLeft rate:u_keyLeft\n// @viji-accumulator:arUp rate:u_keyUp\n// @viji-accumulator:arDown rate:u_keyDown\n\nvoid main() {\n vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / min(u_resolution.x, u_resolution.y);\n\n float speed = u_keyShift ? 0.5 : 0.2;\n vec2 offset = vec2(\n (moveRight + arRight - moveLeft - arLeft) * speed,\n (moveUp + arUp - moveDown - arDown) * speed\n );\n\n vec2 p = uv - offset;\n\n float d = length(p) - 0.1;\n vec3 col = vec3(0.04, 0.04, 0.08);\n\n vec3 dotColor = u_keySpace ? vec3(1.0, 0.5, 0.2) : vec3(0.3, 0.7, 1.0);\n col += dotColor * smoothstep(0.01, 0.0, d);\n\n float glow = 0.005 / (abs(d) + 0.005);\n col += dotColor * glow * 0.3;\n\n float grid = step(0.98, fract(uv.x * 10.0)) + step(0.98, fract(uv.y * 10.0));\n col += grid * 0.03;\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
2127
|
-
"sceneFile": "keyboard-shader-demo.scene.glsl"
|
|
2169
|
+
"sceneFile": "keyboard-shader-demo.scene.glsl",
|
|
2170
|
+
"capabilities": {
|
|
2171
|
+
"interaction": true
|
|
2172
|
+
}
|
|
2128
2173
|
},
|
|
2129
2174
|
{
|
|
2130
2175
|
"type": "text",
|
|
@@ -2145,7 +2190,10 @@ export const docsApi = {
|
|
|
2145
2190
|
"type": "live-example",
|
|
2146
2191
|
"title": "Touch — Multi-Point Glow",
|
|
2147
2192
|
"sceneCode": "// @renderer shader\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy;\n\n vec3 col = vec3(0.04, 0.04, 0.08);\n\n vec3 colors[5];\n colors[0] = vec3(0.3, 0.7, 1.0);\n colors[1] = vec3(0.3, 1.0, 0.5);\n colors[2] = vec3(1.0, 0.5, 0.3);\n colors[3] = vec3(1.0, 0.8, 0.2);\n colors[4] = vec3(0.8, 0.3, 1.0);\n\n for (int i = 0; i < 5; i++) {\n if (i >= u_touchCount) break;\n vec2 tp;\n if (i == 0) tp = u_touch0;\n else if (i == 1) tp = u_touch1;\n else if (i == 2) tp = u_touch2;\n else if (i == 3) tp = u_touch3;\n else tp = u_touch4;\n\n float d = length(uv - tp);\n col += colors[i] * 20.0 / (d + 8.0);\n\n float ring = smoothstep(2.0, 0.0, abs(d - 50.0));\n col += colors[i] * ring * 0.5;\n }\n\n if (u_touchCount == 0) {\n vec2 center = u_resolution * 0.5;\n float pulse = 0.5 + 0.5 * sin(u_time * 2.0);\n float d = length(uv - center);\n col += vec3(0.2, 0.3, 0.5) * pulse * 10.0 / (d + 20.0);\n }\n\n if (u_touchCount >= 2) {\n float dist = length(u_touch0 - u_touch1);\n vec2 mid = (u_touch0 + u_touch1) * 0.5;\n float dMid = length(uv - mid);\n float ring = smoothstep(2.0, 0.0, abs(dMid - dist * 0.5));\n col += vec3(0.5, 0.5, 0.8) * ring * 0.4;\n }\n\n gl_FragColor = vec4(col, 1.0);\n}\n",
|
|
2148
|
-
"sceneFile": "touch-shader-demo.scene.glsl"
|
|
2193
|
+
"sceneFile": "touch-shader-demo.scene.glsl",
|
|
2194
|
+
"capabilities": {
|
|
2195
|
+
"interaction": true
|
|
2196
|
+
}
|
|
2149
2197
|
},
|
|
2150
2198
|
{
|
|
2151
2199
|
"type": "text",
|