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,17 @@
1
+ # Example manifest for the festival mainstage set.
2
+ #
3
+ # Start with:
4
+ # vizcore start examples/vj_festival_mainstage.yml
5
+ #
6
+ # A writable control preset can be attached when you want HUD reactivity and
7
+ # MIDI Learn bindings to round-trip with "Save Project":
8
+ #
9
+ # control_preset: assets/vj_festival_mainstage_controls.json
10
+ scene: vj_festival_mainstage.rb
11
+ audio_source: file
12
+ audio_file: assets/complex_demo_loop.wav
13
+
14
+ profiles:
15
+ mic:
16
+ audio_source: mic
17
+ audio_file:
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vj_glitch_industrial.rb
4
+ #
5
+ # Genre : Glitch / Industrial / Noise
6
+ # BPM : n/a
7
+ # Duration: scan, tear, white_out
8
+ # Audio : --audio-source mic
9
+ # --audio-source file --audio-file examples/assets/complex_demo_loop.wav
10
+ # Keys : 1 scan, 2 tear, X white_out, B blackout, F freeze
11
+ # MIDI : note 36..38 = scenes, note 41 = white_out, CC 1 = global intensity
12
+ Vizcore.define do
13
+ set :global_intensity, 0.84
14
+ audio_normalize mode: :adaptive, window: 2.0, target: 0.88, floor: 0.02
15
+
16
+ theme :factory_noise do
17
+ palette "#f8fafc", "#71717a", "#ef4444", "#111827"
18
+ background "#000000"
19
+ end
20
+
21
+ style :abrasive do
22
+ blend :difference
23
+ opacity 0.74
24
+ end
25
+
26
+ scene :scan do
27
+ use_theme :factory_noise
28
+
29
+ layer :scanner_waterfall do
30
+ type :spectrogram
31
+ scroll :vertical
32
+ bins 128
33
+ history 90
34
+ gain 0.9
35
+ use_style :abrasive
36
+ effect :glitch
37
+ effect_intensity 0.18
38
+ map amplitude, to: :opacity, gain: 0.9, range: 0.36..0.92, curve: :sqrt
39
+ map onset(:high), to: :effect_intensity, gain: 2.4, range: 0.12..0.94, attack: 1.0, release: 0.04
40
+ map fft_spectrum => :deform
41
+ end
42
+
43
+ layer :scanline_text do
44
+ type :text
45
+ content "SCAN"
46
+ font_size 76
47
+ color "#f8fafc"
48
+ blend :multiply
49
+ shadow color: "#ef4444", blur: 10
50
+ glow_strength 0.08
51
+ map high, to: :glow_strength, gain: 2.6, range: 0.06..0.7
52
+ map beat_pulse, to: :letter_spacing, gain: 18.0, min: 0.0, max: 22.0
53
+ map fft_spectrum => :deform
54
+ end
55
+ end
56
+
57
+ scene :tear do
58
+ use_theme :factory_noise
59
+
60
+ layer :feedback_tear do
61
+ shader :liquid_wobble
62
+ opacity 0.86
63
+ blend :difference
64
+ effect :feedback
65
+ effect_intensity 0.28
66
+ wobble 0.54
67
+ warp 1.1
68
+ map amplitude, to: :wobble, gain: 3.2, range: 0.28..1.8, curve: :sqrt
69
+ map onset(:high), to: :effect_intensity, gain: 2.2, range: 0.18..1.0, attack: 1.0, release: 0.05
70
+ map fft_spectrum => :deform
71
+ end
72
+
73
+ layer :tear_mesh do
74
+ type :mesh
75
+ geometry :octahedron
76
+ material :wireframe
77
+ scale 0.9
78
+ deform 0.36
79
+ blend :multiply
80
+ map low, to: :scale, gain: 0.8, range: 0.75..1.42
81
+ map beat_confidence, to: :deform, gain: 1.3, range: 0.22..1.0
82
+ map fft_spectrum => :color_shift
83
+ end
84
+ end
85
+
86
+ scene :white_out do
87
+ use_theme :factory_noise
88
+
89
+ layer :white_flash do
90
+ shader :glitch_flash
91
+ opacity 0.94
92
+ blend :add
93
+ effect :crt
94
+ effect_intensity 0.42
95
+ map amplitude, to: :opacity, gain: 1.2, range: 0.52..1.0, curve: :sqrt
96
+ map onset(:high), to: :effect_intensity, gain: 3.0, range: 0.22..1.0, attack: 1.0, release: 0.03
97
+ map fft_spectrum => :deform
98
+ end
99
+
100
+ layer :warning_text do
101
+ type :text
102
+ content "WHITE OUT"
103
+ font_size 96
104
+ color "#000000"
105
+ blend :difference
106
+ glow_strength 0.48
107
+ map mid, to: :glow_strength, gain: 2.5, range: 0.28..1.0
108
+ map beat_pulse, to: :font_size, gain: 26.0, min: 90.0, max: 132.0
109
+ map fft_spectrum => :deform
110
+ end
111
+ end
112
+
113
+ transition from: :scan, to: :tear do
114
+ trigger { seconds >= 36 || beat_count >= 32 }
115
+ effect :crossfade, duration: 0.35
116
+ end
117
+
118
+ transition from: :tear, to: :scan do
119
+ trigger { seconds >= 48 || beat_count >= 48 }
120
+ effect :flash, duration: 0.18
121
+ end
122
+
123
+ midi :controller, device: :default
124
+
125
+ midi_map note: 36 do
126
+ switch_scene :scan
127
+ end
128
+
129
+ midi_map note: 37 do
130
+ switch_scene :tear
131
+ end
132
+
133
+ midi_map note: 38 do
134
+ switch_scene :white_out
135
+ end
136
+
137
+ midi_map note: 41 do
138
+ switch_scene :white_out
139
+ end
140
+
141
+ midi_map cc: 1 do |value|
142
+ set :global_intensity, value / 127.0
143
+ end
144
+
145
+ key "1" do
146
+ switch_scene :scan
147
+ end
148
+
149
+ key "2" do
150
+ switch_scene :tear
151
+ end
152
+
153
+ key "x" do
154
+ switch_scene :white_out
155
+ end
156
+
157
+ key "b" do
158
+ blackout
159
+ end
160
+
161
+ key "f" do
162
+ freeze
163
+ end
164
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vj_hiphop_cipher.rb
4
+ #
5
+ # Genre : HipHop / Cipher
6
+ # BPM : 85-100
7
+ # Duration: performer-friendly intro, verse, hook
8
+ # Audio : --audio-source mic
9
+ # --audio-source file --audio-file examples/assets/complex_demo_loop.wav
10
+ # Keys : 1 intro, V verse, H hook, B blackout, F freeze, Space tap-tempo
11
+ # MIDI : note 36..38 = scenes, CC 1 = global intensity
12
+ Vizcore.define do
13
+ set :global_intensity, 0.78
14
+ audio_normalize mode: :adaptive, window: 3.5, target: 0.78, floor: 0.05
15
+ bpm 92
16
+ tap_tempo key: :space
17
+
18
+ theme :cipher_blacktop do
19
+ palette "#0f172a", "#f8fafc", "#facc15", "#ef4444"
20
+ background "#020617"
21
+ end
22
+
23
+ style :stage_text do
24
+ blend :screen
25
+ opacity 0.88
26
+ end
27
+
28
+ scene :intro do
29
+ use_theme :cipher_blacktop
30
+
31
+ layer :mic_check_title do
32
+ type :text
33
+ content "MIC CHECK"
34
+ font "Inter"
35
+ font_size 92
36
+ align :center
37
+ color "#f8fafc"
38
+ stroke width: 2, color: "#020617"
39
+ shadow color: "#facc15", blur: 18
40
+ glow_strength 0.18
41
+ use_style :stage_text
42
+ map amplitude, to: :glow_strength, gain: 1.3, range: 0.14..0.72, curve: :sqrt
43
+ map beat_pulse, to: :letter_spacing, gain: 10.0, min: 0.0, max: 14.0
44
+ map fft_spectrum => :deform
45
+ end
46
+
47
+ layer :intro_pulse do
48
+ type :radial_blob
49
+ opacity 0.34
50
+ blend :add
51
+ radius 0.34
52
+ wobble 0.12
53
+ map low, to: :radius, gain: 0.4, range: 0.3..0.68
54
+ map kick, to: :wobble, gain: 0.8, range: 0.08..0.52, attack: 1.0, release: 0.12
55
+ map fft_spectrum => :deform
56
+ end
57
+ end
58
+
59
+ scene :verse do
60
+ use_theme :cipher_blacktop
61
+
62
+ layer :quiet_backline do
63
+ type :waveform
64
+ source :audio
65
+ style :ribbon
66
+ height 0.12
67
+ opacity 0.36
68
+ blend :screen
69
+ map amplitude, to: :height, gain: 0.8, range: 0.08..0.28, curve: :sqrt, release: 0.34
70
+ map beat_pulse, to: :opacity, range: 0.28..0.52, attack: 1.0, release: 0.18
71
+ map fft_spectrum => :deform
72
+ end
73
+
74
+ layer :cipher_dots do
75
+ type :shape
76
+ opacity 0.42
77
+ blend :add
78
+ circle x: 240, y: 520, radius: 22, count: 3 do
79
+ stroke 2
80
+ map mid, to: :radius, gain: 36.0, min: 18.0, max: 56.0
81
+ map beat_pulse, to: :stroke, gain: 4.0, min: 1.0, max: 6.0
82
+ end
83
+ circle x: 1040, y: 180, radius: 18, count: 2 do
84
+ stroke 2
85
+ map fft_spectrum => :radius
86
+ end
87
+ end
88
+ end
89
+
90
+ scene :hook do
91
+ use_theme :cipher_blacktop
92
+
93
+ layer :hook_color do
94
+ shader :gradient_pulse
95
+ blend :screen
96
+ opacity 0.86
97
+ effect :bloom
98
+ effect_intensity 0.24
99
+ map amplitude, to: :effect_intensity, gain: 1.2, range: 0.18..0.72, curve: :sqrt
100
+ map beat_pulse, to: :pulse, range: 0.1..1.0, attack: 1.0, release: 0.14
101
+ map fft_spectrum => :deform
102
+ end
103
+
104
+ layer :hook_text do
105
+ type :text
106
+ content "HOOK"
107
+ font "Inter"
108
+ font_size 116
109
+ align :center
110
+ color "#facc15"
111
+ shadow color: "#ef4444", blur: 24
112
+ glow_strength 0.32
113
+ blend :add
114
+ map high, to: :glow_strength, gain: 2.0, range: 0.24..1.0
115
+ map beat_pulse, to: :font_size, gain: 32.0, min: 104.0, max: 140.0
116
+ map fft_spectrum => :deform
117
+ end
118
+ end
119
+
120
+ transition from: :intro, to: :verse do
121
+ trigger { beat_count >= 16 || frame_count >= 240 }
122
+ effect :crossfade, duration: 0.7
123
+ end
124
+
125
+ transition from: :verse, to: :hook do
126
+ trigger { beat_count >= 48 || frame_count >= 720 }
127
+ effect :flash, duration: 0.2
128
+ end
129
+
130
+ midi :controller, device: :default
131
+
132
+ midi_map note: 36 do
133
+ switch_scene :intro
134
+ end
135
+
136
+ midi_map note: 37 do
137
+ switch_scene :verse
138
+ end
139
+
140
+ midi_map note: 38 do
141
+ switch_scene :hook
142
+ end
143
+
144
+ midi_map cc: 1 do |value|
145
+ set :global_intensity, value / 127.0
146
+ end
147
+
148
+ key "1" do
149
+ switch_scene :intro
150
+ end
151
+
152
+ key "v" do
153
+ switch_scene :verse
154
+ end
155
+
156
+ key "h" do
157
+ switch_scene :hook
158
+ end
159
+
160
+ key "b" do
161
+ blackout
162
+ end
163
+
164
+ key "f" do
165
+ freeze
166
+ end
167
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vj_jpop_idol_live.rb
4
+ #
5
+ # Genre : J-POP / Idol live
6
+ # BPM : 130-180
7
+ # Duration: verse, pre-chorus, chorus, bridge
8
+ # Audio : --audio-source mic
9
+ # --audio-source file --audio-file examples/assets/complex_demo_loop.wav
10
+ # Keys : 1..4 scenes, T palette scene, B blackout, F freeze, Space tap-tempo
11
+ # MIDI : note 36..39 = scenes, CC 1 = global intensity
12
+ #
13
+ # Logo slot: place your own SVG at examples/assets/your_logo.svg and add an svg
14
+ # layer in :verse. No logo asset is bundled here.
15
+ Vizcore.define do
16
+ set :global_intensity, 0.9
17
+ audio_normalize mode: :adaptive, window: 3.0, target: 0.84, floor: 0.04
18
+ bpm 154
19
+ tap_tempo key: :space
20
+
21
+ theme :idol_pastel do
22
+ palette "#ff7eb6", "#7afcff", "#feff9c", "#fff4f4", "#bdb2ff", "#caffbf"
23
+ background "#090a1f"
24
+ end
25
+
26
+ theme :idol_vivid do
27
+ palette "#ff006e", "#3a86ff", "#ffbe0b", "#06d6a0", "#ffffff"
28
+ background "#030712"
29
+ end
30
+
31
+ style :penlight do
32
+ blend :add
33
+ opacity 0.76
34
+ end
35
+
36
+ scene :verse do
37
+ use_theme :idol_pastel
38
+
39
+ layer :color_base do
40
+ shader :gradient_pulse
41
+ blend :screen
42
+ opacity 0.48
43
+ map amplitude, to: :effect_intensity, gain: 0.7, range: 0.08..0.32
44
+ map beat_pulse, to: :pulse, range: 0.08..0.36, attack: 1.0, release: 0.16
45
+ map fft_spectrum => :deform
46
+ end
47
+
48
+ layer :logo_placeholder do
49
+ type :text
50
+ content "YOUR LOGO"
51
+ font "Inter"
52
+ font_size 46
53
+ align :center
54
+ color "#fff4f4"
55
+ glow_strength 0.14
56
+ blend :screen
57
+ # Replace this text with:
58
+ # type :svg
59
+ # file "assets/your_logo.svg"
60
+ map low, to: :glow_strength, gain: 1.4, range: 0.12..0.56
61
+ map beat_pulse, to: :font_size, gain: 10.0, min: 42.0, max: 56.0
62
+ map fft_spectrum => :deform
63
+ end
64
+ end
65
+
66
+ scene :pre_chorus do
67
+ use_theme :idol_pastel
68
+
69
+ layer :spark_grid do
70
+ shader :neon_grid
71
+ use_style :penlight
72
+ effect :bloom
73
+ effect_intensity 0.18
74
+ map high, to: :effect_intensity, gain: 1.6, range: 0.14..0.62
75
+ map beat_pulse, to: :pulse, range: 0.15..0.8, attack: 1.0, release: 0.12
76
+ map fft_spectrum => :deform
77
+ end
78
+
79
+ layer :pastel_particles do
80
+ type :particle_field
81
+ count 2400
82
+ force_field :fountain
83
+ size 2.6
84
+ use_style :penlight
85
+ map amplitude, to: :speed, gain: 2.6, range: 0.4..4.8, curve: :sqrt
86
+ map hihat, to: :sparkle, gain: 2.2, range: 0.2..1.0, attack: 1.0, release: 0.06
87
+ map fft_spectrum => :deform
88
+ end
89
+ end
90
+
91
+ scene :chorus do
92
+ use_theme :idol_vivid
93
+
94
+ layer :chorus_burst do
95
+ shader :kaleidoscope
96
+ blend :screen
97
+ opacity 0.92
98
+ effect :bloom
99
+ effect_intensity 0.3
100
+ vj_effect :mirror
101
+ map amplitude, to: :effect_intensity, gain: 1.6, range: 0.22..0.95, curve: :sqrt
102
+ map beat_pulse, to: :zoom, range: 0.12..1.0, attack: 1.0, release: 0.12
103
+ map fft_spectrum => :deform
104
+ end
105
+
106
+ layer :chorus_confetti do
107
+ type :particle_field
108
+ count 5200 # tune: lower to 3000 when FPS drops
109
+ force_field :burst
110
+ size 3.0
111
+ speed 2.4
112
+ blend :add
113
+ map high, to: :sparkle, gain: 2.8, range: 0.2..1.0
114
+ map kick, to: :bass_explosion, gain: 1.6, range: 0.4..2.0, attack: 1.0, release: 0.08
115
+ map fft_spectrum => :deform
116
+ end
117
+ end
118
+
119
+ scene :bridge do
120
+ use_theme :idol_pastel
121
+
122
+ layer :bridge_ribbon do
123
+ shader :waveform_ribbon
124
+ blend :screen
125
+ opacity 0.6
126
+ effect :motion_blur
127
+ effect_intensity 0.12
128
+ map amplitude, to: :opacity, gain: 0.8, range: 0.34..0.76, curve: :sqrt
129
+ map beat_pulse, to: :effect_intensity, range: 0.08..0.28
130
+ map fft_spectrum => :deform
131
+ end
132
+
133
+ layer :bridge_title do
134
+ type :text
135
+ content "BRIDGE"
136
+ font_size 64
137
+ color "#ffffff"
138
+ glow_strength 0.18
139
+ blend :screen
140
+ map mid, to: :glow_strength, gain: 1.8, range: 0.14..0.72
141
+ map beat_pulse, to: :letter_spacing, gain: 12.0, min: 0.0, max: 18.0
142
+ map fft_spectrum => :deform
143
+ end
144
+ end
145
+
146
+ transition from: :verse, to: :pre_chorus do
147
+ on_bar 8
148
+ effect :crossfade, duration: 0.65
149
+ end
150
+
151
+ transition from: :pre_chorus, to: :chorus do
152
+ on_bar 8
153
+ effect :flash, duration: 0.28
154
+ end
155
+
156
+ transition from: :chorus, to: :bridge do
157
+ on_bar 16
158
+ effect :crossfade, duration: 0.8
159
+ end
160
+
161
+ midi :controller, device: :default
162
+
163
+ midi_map note: 36 do
164
+ switch_scene :verse
165
+ end
166
+
167
+ midi_map note: 37 do
168
+ switch_scene :pre_chorus
169
+ end
170
+
171
+ midi_map note: 38 do
172
+ switch_scene :chorus
173
+ end
174
+
175
+ midi_map note: 39 do
176
+ switch_scene :bridge
177
+ end
178
+
179
+ midi_map cc: 1 do |value|
180
+ set :global_intensity, value / 127.0
181
+ end
182
+
183
+ key "1" do
184
+ switch_scene :verse
185
+ end
186
+
187
+ key "2" do
188
+ switch_scene :pre_chorus
189
+ end
190
+
191
+ key "3" do
192
+ switch_scene :chorus
193
+ end
194
+
195
+ key "4" do
196
+ switch_scene :bridge
197
+ end
198
+
199
+ key "t" do
200
+ switch_scene :chorus
201
+ end
202
+
203
+ key "b" do
204
+ blackout
205
+ end
206
+
207
+ key "f" do
208
+ freeze
209
+ end
210
+ end