vizcore 0.1.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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +70 -117
  3. data/docs/.nojekyll +0 -0
  4. data/docs/assets/playground-worker.js +373 -0
  5. data/docs/assets/playground.css +440 -0
  6. data/docs/assets/playground.js +652 -0
  7. data/docs/assets/site.css +744 -0
  8. data/docs/assets/vizcore-demo.gif +0 -0
  9. data/docs/assets/vizcore-poster.png +0 -0
  10. data/docs/assets/vj-tunnel.js +159 -0
  11. data/docs/index.html +225 -0
  12. data/docs/playground.html +81 -0
  13. data/docs/shape_dsl.md +269 -0
  14. data/examples/README.md +59 -0
  15. data/examples/assets/README.md +19 -0
  16. data/examples/audio_inspector.rb +34 -0
  17. data/examples/club_intro_drop.rb +78 -0
  18. data/examples/kansai_rubykaigi_visual.rb +70 -0
  19. data/examples/live_coding_minimal.rb +22 -0
  20. data/examples/midi_controller_show.rb +78 -0
  21. data/examples/midi_scene_switch.rb +3 -1
  22. data/examples/parser_visualizer.rb +48 -0
  23. data/examples/readme_demo.rb +17 -0
  24. data/examples/rhythm_geometry.rb +34 -0
  25. data/examples/ruby_crystal_show.rb +35 -0
  26. data/examples/shader_playground.rb +18 -0
  27. data/examples/unyo_liquid.rb +59 -0
  28. data/examples/vj_ambient_chill_room.rb +124 -0
  29. data/examples/vj_dnb_jungle.rb +170 -0
  30. data/examples/vj_festival_mainstage.rb +245 -0
  31. data/examples/vj_festival_mainstage.yml +17 -0
  32. data/examples/vj_glitch_industrial.rb +164 -0
  33. data/examples/vj_hiphop_cipher.rb +167 -0
  34. data/examples/vj_jpop_idol_live.rb +210 -0
  35. data/examples/vj_synthwave_retro.rb +173 -0
  36. data/examples/vj_techno_warehouse.rb +195 -0
  37. data/frontend/index.html +494 -2
  38. data/frontend/src/audio-inspector.js +40 -0
  39. data/frontend/src/custom-shape-param-controls.js +106 -0
  40. data/frontend/src/live-controls.js +131 -0
  41. data/frontend/src/main.js +1060 -16
  42. data/frontend/src/mapping-target-selector.js +109 -0
  43. data/frontend/src/midi-learn.js +194 -0
  44. data/frontend/src/performance-monitor.js +183 -0
  45. data/frontend/src/plugin-runtime.js +130 -0
  46. data/frontend/src/projector-mode.js +56 -0
  47. data/frontend/src/renderer/engine.js +157 -3
  48. data/frontend/src/renderer/layer-manager.js +442 -30
  49. data/frontend/src/renderer/shader-manager.js +26 -0
  50. data/frontend/src/runtime-control-preset.js +11 -0
  51. data/frontend/src/shader-error-overlay.js +29 -0
  52. data/frontend/src/shader-param-controls.js +93 -0
  53. data/frontend/src/shaders/builtins.js +380 -2
  54. data/frontend/src/shaders/post-effects.js +52 -0
  55. data/frontend/src/shape-editor-controls.js +157 -0
  56. data/frontend/src/visual-regression.js +67 -0
  57. data/frontend/src/visual-settings-preset.js +103 -0
  58. data/frontend/src/visuals/geometry.js +666 -0
  59. data/frontend/src/visuals/image-renderer.js +291 -0
  60. data/frontend/src/visuals/particle-system.js +56 -10
  61. data/frontend/src/visuals/shape-renderer.js +475 -0
  62. data/frontend/src/visuals/spectrogram-renderer.js +226 -0
  63. data/frontend/src/visuals/svg-arc.js +104 -0
  64. data/frontend/src/visuals/text-renderer.js +112 -11
  65. data/frontend/src/websocket-client.js +12 -1
  66. data/lib/vizcore/analysis/adaptive_normalizer.rb +70 -0
  67. data/lib/vizcore/analysis/beat_detector.rb +4 -2
  68. data/lib/vizcore/analysis/bpm_estimator.rb +8 -0
  69. data/lib/vizcore/analysis/feature_recorder.rb +159 -0
  70. data/lib/vizcore/analysis/feature_replay.rb +84 -0
  71. data/lib/vizcore/analysis/pipeline.rb +235 -11
  72. data/lib/vizcore/analysis/tap_tempo.rb +74 -0
  73. data/lib/vizcore/analysis.rb +4 -0
  74. data/lib/vizcore/audio/dummy_sine_input.rb +1 -1
  75. data/lib/vizcore/audio/fixture_input.rb +65 -0
  76. data/lib/vizcore/audio/input_manager.rb +4 -2
  77. data/lib/vizcore/audio/mic_input.rb +24 -8
  78. data/lib/vizcore/audio/portaudio_ffi.rb +106 -1
  79. data/lib/vizcore/audio.rb +1 -0
  80. data/lib/vizcore/cli/doctor.rb +159 -0
  81. data/lib/vizcore/cli/dsl_reference.rb +99 -0
  82. data/lib/vizcore/cli/layer_docs.rb +46 -0
  83. data/lib/vizcore/cli/scene_diagnostics.rb +23 -0
  84. data/lib/vizcore/cli/scene_inspector.rb +136 -0
  85. data/lib/vizcore/cli/scene_validator.rb +337 -0
  86. data/lib/vizcore/cli/shader_template.rb +68 -0
  87. data/lib/vizcore/cli/shader_uniform_docs.rb +54 -0
  88. data/lib/vizcore/cli.rb +689 -18
  89. data/lib/vizcore/config.rb +103 -2
  90. data/lib/vizcore/control_preset.rb +68 -0
  91. data/lib/vizcore/dsl/engine.rb +277 -5
  92. data/lib/vizcore/dsl/layer_builder.rb +1280 -23
  93. data/lib/vizcore/dsl/layer_group_builder.rb +112 -0
  94. data/lib/vizcore/dsl/mapping_resolver.rb +290 -7
  95. data/lib/vizcore/dsl/mapping_transform_builder.rb +71 -0
  96. data/lib/vizcore/dsl/reaction_builder.rb +44 -0
  97. data/lib/vizcore/dsl/scene_builder.rb +61 -5
  98. data/lib/vizcore/dsl/shader_source_resolver.rb +67 -6
  99. data/lib/vizcore/dsl/style_builder.rb +68 -0
  100. data/lib/vizcore/dsl/timeline_builder.rb +138 -0
  101. data/lib/vizcore/dsl/transition_controller.rb +77 -0
  102. data/lib/vizcore/dsl.rb +5 -1
  103. data/lib/vizcore/layer_catalog.rb +275 -0
  104. data/lib/vizcore/project_manifest.rb +152 -0
  105. data/lib/vizcore/renderer/png_writer.rb +57 -0
  106. data/lib/vizcore/renderer/render_sequence.rb +153 -0
  107. data/lib/vizcore/renderer/scene_frame_source.rb +132 -0
  108. data/lib/vizcore/renderer/scene_serializer.rb +36 -3
  109. data/lib/vizcore/renderer/snapshot.rb +38 -0
  110. data/lib/vizcore/renderer/snapshot_renderer.rb +938 -0
  111. data/lib/vizcore/renderer.rb +5 -0
  112. data/lib/vizcore/server/frame_broadcaster.rb +143 -8
  113. data/lib/vizcore/server/gallery_app.rb +155 -0
  114. data/lib/vizcore/server/gallery_page.rb +100 -0
  115. data/lib/vizcore/server/gallery_runner.rb +48 -0
  116. data/lib/vizcore/server/rack_app.rb +203 -4
  117. data/lib/vizcore/server/runner.rb +391 -22
  118. data/lib/vizcore/server/scene_dependency_watcher.rb +79 -0
  119. data/lib/vizcore/server/websocket_handler.rb +60 -10
  120. data/lib/vizcore/server.rb +4 -0
  121. data/lib/vizcore/shape.rb +719 -0
  122. data/lib/vizcore/sync/osc_message.rb +103 -0
  123. data/lib/vizcore/sync/osc_receiver.rb +68 -0
  124. data/lib/vizcore/sync.rb +4 -0
  125. data/lib/vizcore/templates/midi_control_scene.rb +3 -1
  126. data/lib/vizcore/templates/plugin_layer.rb +20 -0
  127. data/lib/vizcore/templates/plugin_readme.md +23 -0
  128. data/lib/vizcore/templates/plugin_renderer.js +43 -0
  129. data/lib/vizcore/templates/plugin_scene.rb +14 -0
  130. data/lib/vizcore/templates/project_readme.md +7 -23
  131. data/lib/vizcore/templates/rubykaigi_scene.rb +30 -0
  132. data/lib/vizcore/version.rb +1 -1
  133. data/lib/vizcore.rb +28 -0
  134. data/scripts/browser_capture.mjs +75 -0
  135. data/sig/vizcore.rbs +461 -0
  136. metadata +94 -3
  137. data/docs/GETTING_STARTED.md +0 -105
@@ -0,0 +1,440 @@
1
+ @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap");
2
+
3
+ :root {
4
+ color-scheme: dark;
5
+ --bg: #06110f;
6
+ --bg-strong: #030807;
7
+ --surface: #0f172a;
8
+ --surface-soft: #111c28;
9
+ --panel: #132033;
10
+ --line: rgba(148, 163, 184, 0.28);
11
+ --line-strong: rgba(148, 163, 184, 0.42);
12
+ --text: #f8fafc;
13
+ --muted: #aab6c7;
14
+ --green: #22c55e;
15
+ --cyan: #38bdf8;
16
+ --rose: #fb7185;
17
+ --yellow: #facc15;
18
+ --shadow: 0 24px 90px rgba(0, 0, 0, 0.38);
19
+ font-family: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
20
+ letter-spacing: 0;
21
+ }
22
+
23
+ * {
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ html,
28
+ body {
29
+ min-height: 100%;
30
+ }
31
+
32
+ body {
33
+ margin: 0;
34
+ min-width: 320px;
35
+ background:
36
+ linear-gradient(125deg, rgba(34, 197, 94, 0.14), transparent 36%),
37
+ linear-gradient(235deg, rgba(251, 113, 133, 0.13), transparent 42%),
38
+ linear-gradient(180deg, var(--bg) 0%, #08111c 56%, var(--bg-strong) 100%);
39
+ color: var(--text);
40
+ }
41
+
42
+ button,
43
+ select,
44
+ textarea {
45
+ font: inherit;
46
+ }
47
+
48
+ button,
49
+ select {
50
+ cursor: pointer;
51
+ }
52
+
53
+ .skip-link {
54
+ position: fixed;
55
+ left: 1rem;
56
+ top: 1rem;
57
+ z-index: 20;
58
+ min-height: 44px;
59
+ padding: 0.75rem 1rem;
60
+ border-radius: 8px;
61
+ background: var(--text);
62
+ color: #03100c;
63
+ transform: translateY(-150%);
64
+ }
65
+
66
+ .skip-link:focus {
67
+ transform: translateY(0);
68
+ }
69
+
70
+ .topbar {
71
+ position: sticky;
72
+ top: 0;
73
+ z-index: 10;
74
+ display: flex;
75
+ min-height: 64px;
76
+ align-items: center;
77
+ justify-content: space-between;
78
+ gap: 1rem;
79
+ padding: 0.75rem 1rem;
80
+ border-bottom: 1px solid var(--line);
81
+ background: rgba(3, 8, 7, 0.86);
82
+ backdrop-filter: blur(18px);
83
+ }
84
+
85
+ .brand,
86
+ .topbar-nav a {
87
+ display: inline-flex;
88
+ min-height: 44px;
89
+ align-items: center;
90
+ justify-content: center;
91
+ border-radius: 8px;
92
+ color: var(--text);
93
+ text-decoration: none;
94
+ }
95
+
96
+ .brand {
97
+ gap: 0.55rem;
98
+ font-weight: 800;
99
+ }
100
+
101
+ .brand::before {
102
+ width: 0.72rem;
103
+ height: 0.72rem;
104
+ border-radius: 3px;
105
+ background: linear-gradient(135deg, var(--green), var(--cyan));
106
+ box-shadow: 0 0 24px rgba(34, 197, 94, 0.45);
107
+ content: "";
108
+ }
109
+
110
+ .topbar-nav {
111
+ display: flex;
112
+ gap: 0.35rem;
113
+ }
114
+
115
+ .topbar-nav a {
116
+ padding: 0 0.8rem;
117
+ color: #dbeafe;
118
+ }
119
+
120
+ .topbar-nav a:hover,
121
+ .topbar-nav a:focus-visible {
122
+ background: rgba(255, 255, 255, 0.08);
123
+ }
124
+
125
+ .playground-shell {
126
+ display: grid;
127
+ min-height: calc(100svh - 64px);
128
+ grid-template-columns: minmax(360px, 0.92fr) minmax(420px, 1.08fr);
129
+ gap: 1rem;
130
+ padding: 1rem;
131
+ }
132
+
133
+ .editor-panel,
134
+ .preview-panel {
135
+ min-width: 0;
136
+ border: 1px solid var(--line);
137
+ border-radius: 8px;
138
+ background: rgba(15, 23, 42, 0.88);
139
+ box-shadow: var(--shadow);
140
+ }
141
+
142
+ .editor-panel {
143
+ display: grid;
144
+ min-height: 720px;
145
+ grid-template-rows: auto minmax(420px, 1fr) auto;
146
+ }
147
+
148
+ .preview-panel {
149
+ display: grid;
150
+ min-height: 720px;
151
+ grid-template-rows: auto minmax(420px, 1fr) auto;
152
+ overflow: hidden;
153
+ }
154
+
155
+ .panel-header,
156
+ .preview-header {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ gap: 1rem;
161
+ padding: 1rem;
162
+ border-bottom: 1px solid var(--line);
163
+ }
164
+
165
+ .eyebrow {
166
+ margin: 0 0 0.15rem;
167
+ color: var(--green);
168
+ font-family: "JetBrains Mono", monospace;
169
+ font-size: 0.78rem;
170
+ font-weight: 700;
171
+ text-transform: uppercase;
172
+ }
173
+
174
+ h1,
175
+ h2 {
176
+ margin: 0;
177
+ font-size: 1.35rem;
178
+ line-height: 1.15;
179
+ }
180
+
181
+ .toolbar {
182
+ display: flex;
183
+ min-width: 0;
184
+ align-items: center;
185
+ justify-content: flex-end;
186
+ gap: 0.5rem;
187
+ }
188
+
189
+ .preset-label {
190
+ color: var(--muted);
191
+ font-size: 0.9rem;
192
+ }
193
+
194
+ .preset-select,
195
+ .button {
196
+ min-height: 44px;
197
+ border-radius: 8px;
198
+ }
199
+
200
+ .preset-select {
201
+ max-width: 11rem;
202
+ border: 1px solid var(--line);
203
+ background: #08111c;
204
+ color: var(--text);
205
+ padding: 0 0.75rem;
206
+ }
207
+
208
+ .button {
209
+ border: 1px solid var(--line);
210
+ padding: 0 0.95rem;
211
+ color: var(--text);
212
+ font-weight: 700;
213
+ transition:
214
+ background-color 180ms ease,
215
+ border-color 180ms ease,
216
+ transform 180ms ease;
217
+ }
218
+
219
+ .button:hover,
220
+ .button:focus-visible {
221
+ transform: translateY(-1px);
222
+ }
223
+
224
+ .button:disabled {
225
+ cursor: wait;
226
+ opacity: 0.62;
227
+ transform: none;
228
+ }
229
+
230
+ .button.primary {
231
+ border-color: rgba(34, 197, 94, 0.62);
232
+ background: var(--green);
233
+ color: #03100c;
234
+ }
235
+
236
+ .button.secondary {
237
+ background: rgba(255, 255, 255, 0.05);
238
+ }
239
+
240
+ .code-editor {
241
+ width: 100%;
242
+ min-width: 0;
243
+ height: 100%;
244
+ min-height: 420px;
245
+ resize: none;
246
+ border: 0;
247
+ border-bottom: 1px solid var(--line);
248
+ outline: none;
249
+ background: #07101a;
250
+ color: #e2e8f0;
251
+ padding: 1rem;
252
+ font-family: "JetBrains Mono", monospace;
253
+ font-size: 0.94rem;
254
+ line-height: 1.6;
255
+ tab-size: 2;
256
+ }
257
+
258
+ .code-editor:focus {
259
+ box-shadow: inset 0 0 0 2px rgba(56, 189, 248, 0.45);
260
+ }
261
+
262
+ .output-row {
263
+ min-height: 150px;
264
+ background: rgba(3, 8, 7, 0.32);
265
+ }
266
+
267
+ .status-line {
268
+ display: flex;
269
+ flex-wrap: wrap;
270
+ gap: 0.5rem;
271
+ padding: 0.75rem 1rem;
272
+ border-bottom: 1px solid var(--line);
273
+ color: var(--muted);
274
+ font-family: "JetBrains Mono", monospace;
275
+ font-size: 0.82rem;
276
+ }
277
+
278
+ .status-line span {
279
+ display: inline-flex;
280
+ min-height: 30px;
281
+ align-items: center;
282
+ border: 1px solid rgba(148, 163, 184, 0.2);
283
+ border-radius: 8px;
284
+ padding: 0 0.65rem;
285
+ background: rgba(255, 255, 255, 0.04);
286
+ }
287
+
288
+ .json-output {
289
+ padding: 0.75rem 1rem 1rem;
290
+ }
291
+
292
+ .json-output summary {
293
+ min-height: 44px;
294
+ color: #dbeafe;
295
+ cursor: pointer;
296
+ font-weight: 700;
297
+ }
298
+
299
+ .json-output pre {
300
+ max-height: 220px;
301
+ overflow: auto;
302
+ margin: 0;
303
+ color: #d1fae5;
304
+ font-family: "JetBrains Mono", monospace;
305
+ font-size: 0.78rem;
306
+ line-height: 1.5;
307
+ white-space: pre-wrap;
308
+ }
309
+
310
+ .scene-tabs {
311
+ display: flex;
312
+ flex-wrap: wrap;
313
+ justify-content: flex-end;
314
+ gap: 0.5rem;
315
+ }
316
+
317
+ .scene-tab {
318
+ min-height: 40px;
319
+ border: 1px solid var(--line);
320
+ border-radius: 8px;
321
+ background: rgba(255, 255, 255, 0.05);
322
+ color: #dbeafe;
323
+ padding: 0 0.75rem;
324
+ }
325
+
326
+ .scene-tab.active,
327
+ .scene-tab:hover,
328
+ .scene-tab:focus-visible {
329
+ border-color: rgba(56, 189, 248, 0.65);
330
+ background: rgba(56, 189, 248, 0.16);
331
+ color: var(--text);
332
+ }
333
+
334
+ .canvas-wrap {
335
+ position: relative;
336
+ min-height: 420px;
337
+ background: #030807;
338
+ }
339
+
340
+ #preview-canvas {
341
+ display: block;
342
+ width: 100%;
343
+ height: 100%;
344
+ min-height: 420px;
345
+ }
346
+
347
+ .preview-meter {
348
+ position: absolute;
349
+ left: 1rem;
350
+ right: 1rem;
351
+ bottom: 1rem;
352
+ display: flex;
353
+ flex-wrap: wrap;
354
+ gap: 0.5rem;
355
+ pointer-events: none;
356
+ }
357
+
358
+ .preview-meter span {
359
+ min-height: 34px;
360
+ border: 1px solid rgba(148, 163, 184, 0.24);
361
+ border-radius: 8px;
362
+ background: rgba(3, 8, 7, 0.72);
363
+ color: #dbeafe;
364
+ padding: 0.45rem 0.65rem;
365
+ font-family: "JetBrains Mono", monospace;
366
+ font-size: 0.8rem;
367
+ }
368
+
369
+ #beat-stat.active {
370
+ border-color: rgba(34, 197, 94, 0.68);
371
+ color: #dcfce7;
372
+ }
373
+
374
+ .error-output {
375
+ min-height: 96px;
376
+ max-height: 180px;
377
+ overflow: auto;
378
+ border-top: 1px solid rgba(251, 113, 133, 0.38);
379
+ background: rgba(127, 29, 29, 0.28);
380
+ color: #fecdd3;
381
+ padding: 1rem;
382
+ font-family: "JetBrains Mono", monospace;
383
+ font-size: 0.82rem;
384
+ line-height: 1.5;
385
+ white-space: pre-wrap;
386
+ }
387
+
388
+ :focus-visible {
389
+ outline: 2px solid var(--cyan);
390
+ outline-offset: 2px;
391
+ }
392
+
393
+ @media (max-width: 980px) {
394
+ .playground-shell {
395
+ grid-template-columns: 1fr;
396
+ }
397
+
398
+ .editor-panel,
399
+ .preview-panel {
400
+ min-height: 620px;
401
+ }
402
+ }
403
+
404
+ @media (max-width: 700px) {
405
+ .topbar,
406
+ .panel-header,
407
+ .preview-header,
408
+ .toolbar {
409
+ align-items: stretch;
410
+ flex-direction: column;
411
+ }
412
+
413
+ .topbar-nav {
414
+ width: 100%;
415
+ }
416
+
417
+ .topbar-nav a,
418
+ .button,
419
+ .preset-select {
420
+ width: 100%;
421
+ }
422
+
423
+ .playground-shell {
424
+ padding: 0.75rem;
425
+ }
426
+
427
+ .editor-panel,
428
+ .preview-panel {
429
+ min-height: 560px;
430
+ }
431
+ }
432
+
433
+ @media (prefers-reduced-motion: reduce) {
434
+ *,
435
+ *::before,
436
+ *::after {
437
+ scroll-behavior: auto !important;
438
+ transition: none !important;
439
+ }
440
+ }