vizcore 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +66 -648
- data/docs/assets/playground-worker.js +373 -0
- data/docs/assets/playground.css +440 -0
- data/docs/assets/playground.js +652 -0
- data/docs/index.html +2 -1
- data/docs/playground.html +81 -0
- data/docs/shape_dsl.md +269 -0
- data/frontend/index.html +26 -0
- data/frontend/src/custom-shape-param-controls.js +106 -0
- data/frontend/src/main.js +268 -0
- data/frontend/src/mapping-target-selector.js +109 -0
- data/frontend/src/renderer/engine.js +10 -1
- data/frontend/src/renderer/layer-manager.js +18 -4
- data/frontend/src/shape-editor-controls.js +157 -0
- data/frontend/src/visuals/geometry.js +425 -27
- data/frontend/src/visuals/shape-renderer.js +475 -0
- data/frontend/src/visuals/svg-arc.js +104 -0
- data/lib/vizcore/cli/dsl_reference.rb +1 -1
- data/lib/vizcore/cli/scene_validator.rb +92 -0
- data/lib/vizcore/dsl/layer_builder.rb +795 -7
- data/lib/vizcore/dsl/mapping_resolver.rb +158 -4
- data/lib/vizcore/layer_catalog.rb +4 -2
- data/lib/vizcore/renderer/scene_frame_source.rb +14 -1
- data/lib/vizcore/renderer/snapshot_renderer.rb +507 -15
- data/lib/vizcore/server/frame_broadcaster.rb +53 -4
- data/lib/vizcore/server/runner.rb +21 -0
- data/lib/vizcore/shape.rb +719 -0
- data/lib/vizcore/version.rb +1 -1
- data/lib/vizcore.rb +1 -0
- data/sig/vizcore.rbs +100 -1
- metadata +12 -1
data/README.md
CHANGED
|
@@ -1,31 +1,34 @@
|
|
|
1
1
|
# Vizcore [](https://badge.fury.io/rb/vizcore) [](https://github.com/ydah/vizcore/actions/workflows/main.yml)
|
|
2
2
|
|
|
3
|
-
Vizcore is a Ruby gem for
|
|
3
|
+
Vizcore is a Ruby gem for audio-reactive VJ visuals. Define scenes in Ruby, stream them to a browser renderer, and map audio analysis, beats, MIDI, OSC, and live controls to visual parameters.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="docs/assets/vizcore-demo.gif" width="640" alt="Animated Vizcore demo where detected beats expand concentric rings" />
|
|
7
|
+
</p>
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
gem install vizcore
|
|
9
|
-
```
|
|
9
|
+
## Install
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Requirements:
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
**System dependencies:**
|
|
13
|
+
- Ruby `>= 3.2`
|
|
14
|
+
- PortAudio for microphone input
|
|
15
|
+
- ffmpeg for MP3/FLAC input and MP4 output
|
|
16
|
+
- fftw3 optional for faster FFT analysis
|
|
18
17
|
|
|
19
|
-
macOS:
|
|
20
18
|
```bash
|
|
21
|
-
|
|
22
|
-
brew install
|
|
19
|
+
# macOS
|
|
20
|
+
brew install portaudio ffmpeg
|
|
21
|
+
brew install fftw # optional
|
|
22
|
+
|
|
23
|
+
# Ubuntu / Debian
|
|
24
|
+
sudo apt install -y libportaudio2 libportaudio-dev ffmpeg
|
|
25
|
+
sudo apt install -y libfftw3-dev # optional
|
|
23
26
|
```
|
|
24
27
|
|
|
25
|
-
Ubuntu/Debian:
|
|
26
28
|
```bash
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
gem install vizcore
|
|
30
|
+
# or
|
|
31
|
+
bundle add vizcore
|
|
29
32
|
```
|
|
30
33
|
|
|
31
34
|
## Quick Start
|
|
@@ -33,673 +36,88 @@ sudo apt install -y libfftw3-dev # optional: faster FFT
|
|
|
33
36
|
```bash
|
|
34
37
|
vizcore doctor
|
|
35
38
|
vizcore demo
|
|
36
|
-
vizcore start examples/basic.rb
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Then open `http://127.0.0.1:4567`.
|
|
40
|
-
|
|
41
|
-
<p align="center">
|
|
42
|
-
<img src="docs/assets/vizcore-demo.gif" width="640" alt="Animated Vizcore demo where detected beats expand concentric rings" />
|
|
43
|
-
</p>
|
|
44
|
-
|
|
45
|
-
This short preview is generated from `examples/readme_demo.rb` with the bundled
|
|
46
|
-
demo audio: `beat_pulse -> ring radius`.
|
|
47
|
-
|
|
48
|
-
## Scene DSL
|
|
49
|
-
|
|
50
|
-
Scenes are written in plain Ruby. Layers map audio analysis values to visual parameters:
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
Vizcore.define do
|
|
54
|
-
scene :intro do
|
|
55
|
-
layer :wireframe do
|
|
56
|
-
type :wireframe_cube
|
|
57
|
-
map amplitude => :rotation_speed
|
|
58
|
-
map fft_spectrum => :deform
|
|
59
|
-
map frequency_band(:high) => :color_shift
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
scene :drop do
|
|
64
|
-
layer :particles do
|
|
65
|
-
type :particle_field
|
|
66
|
-
count 3600
|
|
67
|
-
map amplitude => :speed
|
|
68
|
-
map frequency_band(:low) => :size
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
layer :waveform do
|
|
72
|
-
type :waveform
|
|
73
|
-
source :audio
|
|
74
|
-
style :ribbon
|
|
75
|
-
map amplitude, to: :height, range: 0.2..0.7
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
layer :spectrogram do
|
|
79
|
-
type :spectrogram
|
|
80
|
-
scroll :vertical
|
|
81
|
-
map amplitude, to: :gain, range: 0.8..3.0
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
layer :mesh do
|
|
85
|
-
type :mesh
|
|
86
|
-
geometry :icosahedron
|
|
87
|
-
material :wireframe
|
|
88
|
-
map bass, to: :scale, range: 0.8..1.4
|
|
89
|
-
map high, to: :deform
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
layer :rings do
|
|
93
|
-
circle count: 8 do
|
|
94
|
-
radius 100
|
|
95
|
-
stroke 2
|
|
96
|
-
map bass, to: :radius, range: 40..180
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
layer :title do
|
|
101
|
-
type :text
|
|
102
|
-
content "DROP\nNOW"
|
|
103
|
-
font "Inter Black"
|
|
104
|
-
font_size 96
|
|
105
|
-
letter_spacing 4
|
|
106
|
-
align :center
|
|
107
|
-
fill "#ffffff"
|
|
108
|
-
stroke width: 2, color: "#111111"
|
|
109
|
-
shadow color: "rgba(0, 0, 0, 0.45)", blur: 18
|
|
110
|
-
map beat? => :flash
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
layer :logo do
|
|
114
|
-
type :svg
|
|
115
|
-
file "assets/logo.svg"
|
|
116
|
-
scale 0.9
|
|
117
|
-
map bass, to: :scale, range: 0.8..1.15
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
layer :photo do
|
|
121
|
-
type :image
|
|
122
|
-
file "assets/noise.png"
|
|
123
|
-
fit :cover
|
|
124
|
-
blend :screen
|
|
125
|
-
map amplitude, to: :opacity, range: 0.25..0.85
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
layer :footage do
|
|
129
|
-
type :video
|
|
130
|
-
file "assets/loop.mp4"
|
|
131
|
-
fit :cover
|
|
132
|
-
map beat?, to: :invert
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
transition from: :intro, to: :drop do
|
|
137
|
-
on_bar 16
|
|
138
|
-
effect :crossfade, duration: 1.4
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Mapping options can exaggerate small analysis values, clamp them into useful visual ranges, and smooth changes over time:
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
layer :liquid do
|
|
147
|
-
shader :liquid_wobble
|
|
148
|
-
wobble 0.25
|
|
149
|
-
warp 0.45
|
|
150
|
-
|
|
151
|
-
map amplitude, to: :wobble, gain: 3.5, range: 0.12..1.4, curve: :sqrt
|
|
152
|
-
map frequency_band(:low), to: :warp, gain: 2.2, range: 0.25..2.4
|
|
153
|
-
map onset(:high), to: :spark, range: 0.0..1.0
|
|
154
|
-
map kick, to: :pulse, range: 0.0..1.0
|
|
155
|
-
map beat_pulse, to: :effect_intensity, range: 0.08..0.35
|
|
156
|
-
end
|
|
157
39
|
```
|
|
158
40
|
|
|
159
|
-
|
|
160
|
-
Use block syntax when shaping a mapping reads better:
|
|
41
|
+
Open `http://127.0.0.1:4567`.
|
|
161
42
|
|
|
162
|
-
|
|
163
|
-
map amplitude, to: :scale do
|
|
164
|
-
gain 2.0
|
|
165
|
-
range 0.8..1.6
|
|
166
|
-
curve :ease_out
|
|
167
|
-
smooth attack: 0.02, release: 0.18
|
|
168
|
-
deadzone 0.05
|
|
169
|
-
end
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Block syntax is additive and writes the same transform metadata as keyword
|
|
173
|
-
syntax. `deadzone` suppresses tiny values before gain and curve are applied;
|
|
174
|
-
`curve` also supports `:ease_out`.
|
|
175
|
-
|
|
176
|
-
For tracks with very different levels, opt in to adaptive feature
|
|
177
|
-
normalization at the top of the scene file:
|
|
178
|
-
|
|
179
|
-
```ruby
|
|
180
|
-
audio_normalize mode: :adaptive, window: 3.0, target: 0.85, floor: 0.05
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
This keeps `amplitude` and FFT-driven mappings in a repeatable range without
|
|
184
|
-
changing the default analysis behavior.
|
|
185
|
-
|
|
186
|
-
When the detected tempo should not drift during a prepared file or live set,
|
|
187
|
-
lock BPM at the top of the scene file:
|
|
188
|
-
|
|
189
|
-
```ruby
|
|
190
|
-
bpm 128
|
|
191
|
-
bpm_lock true
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
When you want to set tempo by ear from the browser during a live set, opt in to
|
|
195
|
-
tap tempo and choose the keyboard key:
|
|
196
|
-
|
|
197
|
-
```ruby
|
|
198
|
-
tap_tempo key: :space
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
After two valid taps, Vizcore estimates BPM from recent intervals and applies it
|
|
202
|
-
as a locked BPM so beat-driven visuals stop drifting.
|
|
203
|
-
|
|
204
|
-
Browser keyboard shortcuts can also be declared in the scene file for operators
|
|
205
|
-
who do not have a MIDI controller:
|
|
206
|
-
|
|
207
|
-
```ruby
|
|
208
|
-
key "d" do
|
|
209
|
-
switch_scene :drop
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
key "x" do
|
|
213
|
-
blackout
|
|
214
|
-
end
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
For a more music-oriented style, `react_to` groups the same mappings by source:
|
|
218
|
-
|
|
219
|
-
```ruby
|
|
220
|
-
layer :particles do
|
|
221
|
-
type :particle_field
|
|
222
|
-
|
|
223
|
-
react_to bass do
|
|
224
|
-
change :size, gain: 4.0, range: 2.0..8.0, curve: :sqrt
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
react_to beat do
|
|
228
|
-
trigger :burst
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
`react_to` is additive syntax; it serializes to the same mapping model as `map`.
|
|
234
|
-
|
|
235
|
-
Transitions can use explicit trigger blocks, or beat/bar helpers when that reads
|
|
236
|
-
closer to the structure of a track:
|
|
237
|
-
|
|
238
|
-
```ruby
|
|
239
|
-
transition from: :build, to: :drop do
|
|
240
|
-
on_bar 8
|
|
241
|
-
effect :flash, duration: 0.35
|
|
242
|
-
end
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
For simple song structure, `section` defines scenes in order and creates
|
|
246
|
-
beat-counted transitions between adjacent sections:
|
|
247
|
-
|
|
248
|
-
```ruby
|
|
249
|
-
section :intro, bars: 8 do
|
|
250
|
-
layer :pulse do
|
|
251
|
-
map amplitude => :scale
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
section :drop, bars: 16 do
|
|
256
|
-
layer :sparks do
|
|
257
|
-
map beat? => :burst
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
For file-backed shows or rehearsed sets, `timeline` can mark existing scenes at
|
|
263
|
-
ordered beat or second positions and generate the transitions:
|
|
264
|
-
|
|
265
|
-
```ruby
|
|
266
|
-
timeline do
|
|
267
|
-
at beats(0), scene: :intro
|
|
268
|
-
at bars(8), scene: :build
|
|
269
|
-
at bars(16), scene: :drop
|
|
270
|
-
end
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Layers can choose their compositing mode with `blend`. Supported modes are `:alpha` / `:normal`, `:add`, `:multiply`, `:screen`, and `:difference`:
|
|
274
|
-
|
|
275
|
-
```ruby
|
|
276
|
-
layer :sparks do
|
|
277
|
-
type :particle_field
|
|
278
|
-
blend :screen
|
|
279
|
-
map treble, to: :sparkle
|
|
280
|
-
end
|
|
281
|
-
```
|
|
43
|
+
To run a scene file directly:
|
|
282
44
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
```ruby
|
|
288
|
-
layer :scope do
|
|
289
|
-
type :waveform
|
|
290
|
-
source :audio
|
|
291
|
-
style :ribbon
|
|
292
|
-
map amplitude, to: :height, range: 0.2..0.7
|
|
293
|
-
end
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
Video layers embed MP4/WebM/OGV assets as muted looping textures. They share
|
|
297
|
-
`fit`, `scale`, `rotation`, `blend`, and post-effect params with image layers:
|
|
298
|
-
|
|
299
|
-
```ruby
|
|
300
|
-
layer :footage do
|
|
301
|
-
type :video
|
|
302
|
-
file "assets/loop.mp4"
|
|
303
|
-
fit :cover
|
|
304
|
-
map beat?, to: :invert
|
|
305
|
-
end
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
Spectrogram layers render a short scrolling FFT heatmap. Use `scroll :vertical`
|
|
309
|
-
for a waterfall view or `scroll :horizontal` when time should move left to right:
|
|
310
|
-
|
|
311
|
-
```ruby
|
|
312
|
-
layer :waterfall do
|
|
313
|
-
type :spectrogram
|
|
314
|
-
scroll :vertical
|
|
315
|
-
bins 96
|
|
316
|
-
history 128
|
|
317
|
-
map amplitude, to: :gain, range: 0.8..3.0
|
|
318
|
-
end
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
Mesh layers render preset 3D wireframes without writing GLSL. Available
|
|
322
|
-
geometry presets are `:cube`, `:tetrahedron`, `:octahedron`, and
|
|
323
|
-
`:icosahedron`; use `material :wireframe`:
|
|
324
|
-
|
|
325
|
-
```ruby
|
|
326
|
-
layer :mesh do
|
|
327
|
-
type :mesh
|
|
328
|
-
geometry :icosahedron
|
|
329
|
-
material :wireframe
|
|
330
|
-
map bass, to: :scale, range: 0.8..1.4
|
|
331
|
-
map high, to: :deform
|
|
332
|
-
end
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
Shape layers provide declarative circles and lines for scenes that do not need
|
|
336
|
-
custom GLSL. Shape-local mappings target the primitive inside the block:
|
|
337
|
-
|
|
338
|
-
```ruby
|
|
339
|
-
layer :rings do
|
|
340
|
-
circle count: 8 do
|
|
341
|
-
radius 100
|
|
342
|
-
stroke 2
|
|
343
|
-
map bass, to: :radius, range: 40..180
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
draw do
|
|
347
|
-
line x1: 0, y1: 360, x2: 1280, y2: 360
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
Layers can also apply browser-side post effects. Supported effects are
|
|
353
|
-
`:bloom`, `:glitch`, `:chromatic`, `:feedback`, `:motion_blur`, and `:crt`:
|
|
354
|
-
|
|
355
|
-
```ruby
|
|
356
|
-
layer :tunnel do
|
|
357
|
-
shader :bass_tunnel
|
|
358
|
-
effect :motion_blur
|
|
359
|
-
map bass, to: :effect_intensity, range: 0.1..0.7
|
|
360
|
-
end
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
Reusable layer styles keep repeated visual params in one place:
|
|
364
|
-
|
|
365
|
-
```ruby
|
|
366
|
-
style :neon do
|
|
367
|
-
palette "#00ffff", "#ff00aa", "#facc15"
|
|
368
|
-
glow_strength 0.45
|
|
369
|
-
blend :add
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
scene :drop do
|
|
373
|
-
layer :title do
|
|
374
|
-
type :text
|
|
375
|
-
use_style :neon
|
|
376
|
-
content "DROP"
|
|
377
|
-
end
|
|
378
|
-
end
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
Themes provide scene-wide layer defaults:
|
|
382
|
-
|
|
383
|
-
```ruby
|
|
384
|
-
theme :ruby_night do
|
|
385
|
-
palette "#e11d48", "#f59e0b", "#38bdf8"
|
|
386
|
-
glow_strength 0.5
|
|
387
|
-
blend :screen
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
scene :drop do
|
|
391
|
-
use_theme :ruby_night
|
|
392
|
-
|
|
393
|
-
layer :title do
|
|
394
|
-
type :text
|
|
395
|
-
content "DROP"
|
|
396
|
-
end
|
|
397
|
-
end
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
Layer groups apply shared params to a subset of adjacent layers while keeping the
|
|
401
|
-
runtime payload as normal ordered layers:
|
|
402
|
-
|
|
403
|
-
```ruby
|
|
404
|
-
scene :drop do
|
|
405
|
-
group :foreground do
|
|
406
|
-
use_style :neon
|
|
407
|
-
blend :add
|
|
408
|
-
opacity 0.9
|
|
409
|
-
|
|
410
|
-
layer(:particles) { type :particle_field }
|
|
411
|
-
layer(:title) { type :text }
|
|
412
|
-
end
|
|
413
|
-
end
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
Scenes can inherit shared layers from an earlier scene:
|
|
417
|
-
|
|
418
|
-
```ruby
|
|
419
|
-
scene :base do
|
|
420
|
-
layer(:background) { shader :neon_grid }
|
|
421
|
-
end
|
|
422
|
-
|
|
423
|
-
scene :drop, extends: :base do
|
|
424
|
-
layer(:particles) { type :particle_field }
|
|
425
|
-
end
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
Frequency bands can be written with musical aliases when that reads better in a scene:
|
|
429
|
-
|
|
430
|
-
```ruby
|
|
431
|
-
map bass, to: :size # same as frequency_band(:low)
|
|
432
|
-
map mid, to: :twist
|
|
433
|
-
map treble, to: :sparkle # same as frequency_band(:high)
|
|
434
|
-
map beat_confidence, to: :sync_strength
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Custom GLSL Shaders
|
|
438
|
-
|
|
439
|
-
Built-in shader presets include `:gradient_pulse`, `:bass_tunnel`,
|
|
440
|
-
`:neon_grid`, `:kaleidoscope`, `:spectrum_rings`, `:liquid_wobble`,
|
|
441
|
-
`:audio_bars`, `:ruby_crystal`, `:starfield`, `:waveform_ribbon`,
|
|
442
|
-
`:unyo_geometry`, and `:glitch_flash`.
|
|
443
|
-
|
|
444
|
-
```ruby
|
|
445
|
-
layer :wave_shader do
|
|
446
|
-
type :shader
|
|
447
|
-
glsl "shaders/custom_wave.frag"
|
|
448
|
-
param :intensity, default: 0.6, range: 0.0..2.0, step: 0.05
|
|
449
|
-
map amplitude => :param_intensity
|
|
450
|
-
map frequency_band(:low) => :param_bass
|
|
451
|
-
map beat? => :param_flash
|
|
452
|
-
end
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
Path-style shader declarations are also accepted, so `shader "shaders/liquid.frag", reload: true` is equivalent to `glsl "shaders/liquid.frag"` for custom fragment shaders. When hot reload is enabled, Vizcore watches referenced GLSL files and pushes updated shader source to connected browsers.
|
|
456
|
-
|
|
457
|
-
Use `vizcore shader new liquid` to create `shaders/liquid.frag` with a GLSL ES
|
|
458
|
-
starter template.
|
|
459
|
-
|
|
460
|
-
Custom fragment shaders must be GLSL ES 3.00. Run `vizcore shader-docs`
|
|
461
|
-
to print the generated uniform reference. Common uniforms include:
|
|
462
|
-
|
|
463
|
-
- `u_amplitude`
|
|
464
|
-
- `u_bass` / `u_mid` / `u_high`
|
|
465
|
-
- `u_beat`
|
|
466
|
-
- `u_beat_pulse`
|
|
467
|
-
- `u_onset` / `u_low_onset` / `u_mid_onset` / `u_high_onset`
|
|
468
|
-
- `u_kick` / `u_snare` / `u_hihat`
|
|
469
|
-
- `u_bpm`
|
|
470
|
-
- `u_fft[32]`
|
|
471
|
-
- `u_fft_size`
|
|
472
|
-
- `u_param_<name>`
|
|
473
|
-
- `u_global_<name>`
|
|
474
|
-
|
|
475
|
-
For backward compatibility, a DSL target like `:param_intensity` is also exposed as `u_param_intensity`.
|
|
476
|
-
Runtime globals set with `set :global_intensity, 0.75` are exposed as `u_global_intensity`.
|
|
477
|
-
Use `param :name, default:, range:, step:` to attach numeric metadata for shader
|
|
478
|
-
params that can be surfaced by tooling.
|
|
479
|
-
The browser HUD turns this metadata into per-layer shader parameter sliders, so
|
|
480
|
-
declared params can be adjusted during a live run without editing the scene file.
|
|
481
|
-
|
|
482
|
-
### Plugin Layer Capabilities
|
|
483
|
-
|
|
484
|
-
Plugins can register layer capability metadata for validation, docs, and HUD
|
|
485
|
-
tooling without patching Vizcore itself:
|
|
486
|
-
|
|
487
|
-
```ruby
|
|
488
|
-
# in a plugin file loaded by Ruby
|
|
489
|
-
Vizcore.register_layer_capability(
|
|
490
|
-
type: :laser_grid,
|
|
491
|
-
aliases: %i[laser_layer],
|
|
492
|
-
params: { beam_count: "Integer", intensity: "Float" },
|
|
493
|
-
mappable_params: %i[intensity opacity],
|
|
494
|
-
description: "Plugin laser renderer."
|
|
495
|
-
)
|
|
45
|
+
```bash
|
|
46
|
+
vizcore start examples/basic.rb
|
|
47
|
+
vizcore start examples/vj_techno_warehouse.rb --audio-source file --audio-file examples/assets/complex_demo_loop.wav
|
|
496
48
|
```
|
|
497
49
|
|
|
498
|
-
|
|
499
|
-
scenes. A plugin-provided browser renderer or shader still needs to handle the
|
|
500
|
-
custom layer type at runtime; the capability API keeps Ruby validation and docs
|
|
501
|
-
aware of the extension.
|
|
50
|
+
## Minimal Scene
|
|
502
51
|
|
|
503
|
-
|
|
504
|
-
capability file, a browser renderer that registers with
|
|
505
|
-
`globalThis.VizcorePlugins`, and an example scene. The browser plugin API exposes
|
|
506
|
-
`apiVersion` and version 1 supports `registerLayerRenderer(type, renderer)` for
|
|
507
|
-
line renderers and `registerShaderRenderer(type, renderer)` for shader
|
|
508
|
-
renderers. Browser plugin renderers can return
|
|
509
|
-
`{ kind: "lines", points: [...], color: [r, g, b] }`; shader renderers can return
|
|
510
|
-
a GLSL fragment shader string or `{ kind: "shader", fragmentShader }`. Vizcore
|
|
511
|
-
composites the result using the layer's normal `opacity`, `blend`, and palette
|
|
512
|
-
behavior.
|
|
513
|
-
|
|
514
|
-
### MIDI Scene Switching
|
|
52
|
+
Scenes are plain Ruby files:
|
|
515
53
|
|
|
516
54
|
```ruby
|
|
517
55
|
Vizcore.define do
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
56
|
+
scene :readme_demo do
|
|
57
|
+
layer :beat_rings do
|
|
58
|
+
palette "#24f6ff", "#ff2bbd", "#caff2e"
|
|
59
|
+
|
|
60
|
+
circle count: 4 do
|
|
61
|
+
radius 92
|
|
62
|
+
stroke 3
|
|
63
|
+
map beat_pulse,
|
|
64
|
+
to: :radius,
|
|
65
|
+
gain: 160.0,
|
|
66
|
+
min: 56,
|
|
67
|
+
max: 164,
|
|
68
|
+
attack: 1.0,
|
|
69
|
+
release: 0.2
|
|
70
|
+
end
|
|
526
71
|
end
|
|
527
72
|
end
|
|
528
|
-
|
|
529
|
-
midi_map note: 36 do
|
|
530
|
-
switch_scene :impact
|
|
531
|
-
end
|
|
532
|
-
|
|
533
|
-
midi_map cc: 1 do |value|
|
|
534
|
-
set :global_intensity, value / 127.0
|
|
535
|
-
end
|
|
536
73
|
end
|
|
537
74
|
```
|
|
538
75
|
|
|
539
|
-
|
|
76
|
+
Run it with:
|
|
540
77
|
|
|
541
78
|
```bash
|
|
542
|
-
vizcore start
|
|
543
|
-
vizcore demo [--host 127.0.0.1] [--port 4567] [--control-preset controls.json] [--osc-port 9000] [--projector]
|
|
544
|
-
vizcore doctor
|
|
545
|
-
vizcore validate SCENE_FILE
|
|
546
|
-
vizcore inspect SCENE_FILE
|
|
547
|
-
vizcore snapshot SCENE_FILE [--audio-source dummy|file|mic] [--audio-file PATH] [--out screenshot.png]
|
|
548
|
-
vizcore render SCENE_FILE [--audio-source dummy|file|mic] [--audio-file PATH] [--out frames|movie.mp4] [--frames 60] [--fps 30]
|
|
549
|
-
vizcore capture SCENE_FILE [--audio-source dummy|file|mic] [--out browser-capture.png]
|
|
550
|
-
vizcore browser-capture http://127.0.0.1:4567/projector [--out browser-capture.png]
|
|
551
|
-
vizcore record-features AUDIO_FILE [--out features.json] [--frames 300] [--fps 30]
|
|
552
|
-
vizcore gallery [--host 127.0.0.1] [--port 4568]
|
|
553
|
-
vizcore layers
|
|
554
|
-
vizcore dsl-docs
|
|
555
|
-
vizcore shader new NAME [--out shaders/name.frag]
|
|
556
|
-
vizcore shader-docs
|
|
557
|
-
vizcore plugin new NAME [--out plugins/name]
|
|
558
|
-
vizcore new PROJECT_NAME [--template standard|minimal|shader|midi|live-set|rubykaigi]
|
|
559
|
-
vizcore devices [audio|midi]
|
|
79
|
+
vizcore start scene.rb
|
|
560
80
|
```
|
|
561
81
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
| Source | Description |
|
|
565
|
-
|--------|-------------|
|
|
566
|
-
| `mic` | Live microphone input (default) |
|
|
567
|
-
| `file` | File playback — `.wav` directly, `.mp3`/`.flac` via `ffmpeg` |
|
|
568
|
-
| `dummy` | Silent source for layout testing |
|
|
82
|
+
## Useful Commands
|
|
569
83
|
|
|
570
84
|
```bash
|
|
571
|
-
|
|
572
|
-
vizcore start
|
|
573
|
-
|
|
574
|
-
|
|
85
|
+
vizcore start SCENE_FILE
|
|
86
|
+
vizcore start --manifest vizcore.yml
|
|
87
|
+
vizcore gallery
|
|
88
|
+
vizcore validate SCENE_FILE
|
|
575
89
|
vizcore devices audio
|
|
576
|
-
vizcore
|
|
577
|
-
|
|
578
|
-
# Lower this when the selected input is too quiet to move the visual
|
|
579
|
-
vizcore start examples/audio_inspector.rb --audio-source mic --audio-device 5 --noise-gate 0.001
|
|
580
|
-
|
|
581
|
-
# WAV file
|
|
582
|
-
vizcore start scene.rb --audio-source file --audio-file track.wav
|
|
583
|
-
|
|
584
|
-
# MP3/FLAC (requires ffmpeg)
|
|
585
|
-
vizcore start scene.rb --audio-source file --audio-file set.mp3
|
|
586
|
-
|
|
587
|
-
# Recorded analysis features
|
|
588
|
-
vizcore record-features track.wav --out features.json --frames 300 --fps 30
|
|
589
|
-
vizcore start scene.rb --feature-file features.json
|
|
90
|
+
vizcore devices midi
|
|
91
|
+
vizcore snapshot SCENE_FILE --out screenshot.png
|
|
590
92
|
```
|
|
591
93
|
|
|
592
|
-
|
|
94
|
+
Use `vizcore help` for the full CLI.
|
|
593
95
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
Project manifests keep show startup repeatable:
|
|
597
|
-
|
|
598
|
-
```yaml
|
|
599
|
-
# vizcore.yml
|
|
600
|
-
scene: scenes/show.rb
|
|
601
|
-
audio:
|
|
602
|
-
source: file
|
|
603
|
-
file: audio/set.wav
|
|
604
|
-
control_preset: controls/live.json
|
|
605
|
-
sync:
|
|
606
|
-
osc:
|
|
607
|
-
port: 9000
|
|
608
|
-
plugins:
|
|
609
|
-
- require: vizcore-laser-grid
|
|
610
|
-
frontend: frontend/laser-grid-renderer.js
|
|
611
|
-
plugin_assets:
|
|
612
|
-
- frontend/local-overlay.js
|
|
613
|
-
profiles:
|
|
614
|
-
rehearsal:
|
|
615
|
-
audio:
|
|
616
|
-
source: dummy
|
|
617
|
-
control_preset: controls/rehearsal.json
|
|
618
|
-
```
|
|
96
|
+
Browser routes:
|
|
619
97
|
|
|
620
|
-
|
|
621
|
-
`
|
|
622
|
-
|
|
623
|
-
RackApp and loaded before `/src/main.js`. A control preset JSON can include
|
|
624
|
-
`visual_settings` and `midi_learn_bindings`; Vizcore sends it through `/runtime`
|
|
625
|
-
and the browser applies it to HUD reactivity and MIDI Learn state.
|
|
626
|
-
Set `--osc-port` or `sync.osc.port` to receive OSC controls:
|
|
627
|
-
`/vizcore/scene` with a string scene name, `/vizcore/tap`,
|
|
628
|
-
`/vizcore/bpm`, `/vizcore/bpm_unlock`, `/vizcore/global/<name>`,
|
|
629
|
-
`/vizcore/live/blackout`, `/vizcore/live/freeze`, and
|
|
630
|
-
`/vizcore/transport/play` or `/vizcore/transport/stop` for file transport.
|
|
98
|
+
- `/` visual output with operator controls
|
|
99
|
+
- `/projector` clean projection output
|
|
100
|
+
- `/control` separate operator panel
|
|
631
101
|
|
|
632
|
-
|
|
102
|
+
## Documentation
|
|
633
103
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
Use `vizcore browser-capture http://127.0.0.1:4567/projector --out browser.png`
|
|
641
|
-
when you need a PNG from an already-running browser/WebGL renderer. Use
|
|
642
|
-
`vizcore capture scene.rb --out browser.png` to start a temporary projector
|
|
643
|
-
server, capture it, and stop the server automatically. Both commands require
|
|
644
|
-
Playwright in the local Node environment.
|
|
645
|
-
|
|
646
|
-
Use `vizcore record-features track.wav --out features.json --frames 300 --fps 30` to capture the same audio analysis values as JSON for debugging mappings or comparing tracks. Replay the file with `vizcore start scene.rb --feature-file features.json` when you want deterministic visual behavior without live audio input.
|
|
647
|
-
|
|
648
|
-
## Requirements
|
|
649
|
-
|
|
650
|
-
- Ruby `>= 3.2`
|
|
651
|
-
- `portaudio` for microphone input
|
|
652
|
-
- `ffmpeg` on `PATH` when using `.mp3` / `.flac` file input or `.mp4` render output
|
|
653
|
-
- `fftw3` (optional) — Vizcore falls back to pure-Ruby FFT automatically when unavailable
|
|
654
|
-
|
|
655
|
-
## Examples
|
|
656
|
-
|
|
657
|
-
Run `vizcore gallery` to open a browser gallery of bundled examples with scene counts, layer counts, audio-source hints, and launch commands.
|
|
658
|
-
|
|
659
|
-
| File | Description |
|
|
660
|
-
|------|-------------|
|
|
661
|
-
| `examples/basic.rb` | Single wireframe cube layer |
|
|
662
|
-
| `examples/intro_drop.rb` | Beat-triggered scene transition |
|
|
663
|
-
| `examples/file_audio_demo.rb` | File audio source walkthrough |
|
|
664
|
-
| `examples/complex_audio_showcase.rb` | Dense multi-layer showcase |
|
|
665
|
-
| `examples/rhythm_geometry.rb` | Single large morphing geometric pattern scene with drum-reactive motion |
|
|
666
|
-
| `examples/ruby_crystal_show.rb` | Ruby-themed crystal, particles, and title visual |
|
|
667
|
-
| `examples/parser_visualizer.rb` | Parser-themed token, AST, and reduce visual sketch |
|
|
668
|
-
| `examples/live_coding_minimal.rb` | Tiny scene for live-coding demos |
|
|
669
|
-
| `examples/club_intro_drop.rb` | Intro, build, and drop flow for rhythmic file input |
|
|
670
|
-
| `examples/shader_playground.rb` | Focused shader scene with declared params |
|
|
671
|
-
| `examples/audio_inspector.rb` | Audio bars and blob for analysis visualization |
|
|
672
|
-
| `examples/readme_demo.rb` | Minimal beat pulse to ring radius demo |
|
|
673
|
-
| `examples/midi_scene_switch.rb` | MIDI-driven scene switching |
|
|
674
|
-
| `examples/midi_controller_show.rb` | MIDI pads for scenes and knobs for global shader intensity/color |
|
|
675
|
-
| `examples/kansai_rubykaigi_visual.rb` | Event showcase with ruby crystal, water ripple, and Kyoto-inspired pattern |
|
|
676
|
-
| `examples/custom_shader.rb` | Custom GLSL shader with audio mapping |
|
|
677
|
-
| `examples/unyo_liquid.rb` | Organic liquid wobble scene with FFT blob and particles |
|
|
678
|
-
|
|
679
|
-
### VJ Set Examples
|
|
680
|
-
|
|
681
|
-
Ready-to-run scenes grouped by genre / mood. Each accepts live mic input or any
|
|
682
|
-
audio file, including the bundled `examples/assets/complex_demo_loop.wav`.
|
|
683
|
-
|
|
684
|
-
| File | Genre / Mood | BPM | Scenes | Notes |
|
|
685
|
-
|------|--------------|-----|--------|-------|
|
|
686
|
-
| `examples/vj_techno_warehouse.rb` | Techno / Warehouse | 128-140 | 4 | wireframe + particles |
|
|
687
|
-
| `examples/vj_dnb_jungle.rb` | Drum & Bass / Jungle | 170-180 | 3 | kick/snare/hihat split |
|
|
688
|
-
| `examples/vj_ambient_chill_room.rb` | Ambient / Chill | 60-90 | 2 | beatless, drone-friendly |
|
|
689
|
-
| `examples/vj_hiphop_cipher.rb` | HipHop / Cipher | 85-100 | 3 | text-forward |
|
|
690
|
-
| `examples/vj_jpop_idol_live.rb` | J-POP / Idol | 130-180 | 4 | color fields + tap tempo |
|
|
691
|
-
| `examples/vj_synthwave_retro.rb` | Synthwave / Retro | 100-120 | 3 | circle and line primitives |
|
|
692
|
-
| `examples/vj_glitch_industrial.rb` | Glitch / Industrial | n/a | 3 | feedback + difference blend |
|
|
693
|
-
| `examples/vj_festival_mainstage.rb` | EDM / Mainstage | 124-132 | 5 | uses `extends:` and `group` |
|
|
104
|
+
- Project site: <https://ydah.github.io/vizcore/>
|
|
105
|
+
- Examples: [examples/README.md](examples/README.md)
|
|
106
|
+
- Changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
107
|
+
- Runtime layer reference: `vizcore layers`
|
|
108
|
+
- Ruby DSL reference: `vizcore dsl-docs`
|
|
109
|
+
- Shader uniform reference: `vizcore shader-docs`
|
|
694
110
|
|
|
695
111
|
## Development
|
|
696
112
|
|
|
697
113
|
```bash
|
|
114
|
+
bundle install
|
|
115
|
+
npm install --prefix frontend
|
|
698
116
|
bundle exec rspec
|
|
699
117
|
npm --prefix frontend test
|
|
118
|
+
bundle exec rake release:verify
|
|
700
119
|
```
|
|
701
120
|
|
|
702
|
-
|
|
703
121
|
## License
|
|
704
122
|
|
|
705
123
|
MIT
|