mudra-skills 1.0.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 (149) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/CLAUDE.md +35 -0
  3. package/bin/install.js +25 -0
  4. package/package.json +28 -0
  5. package/skills/mudra-master/SKILL.md +202 -0
  6. package/skills/mudra-master/mudra-preview/SKILL.md +151 -0
  7. package/skills/mudra-master/mudra-preview/assets/ar-menu.html +382 -0
  8. package/skills/mudra-master/mudra-preview/assets/document-scroller.html +468 -0
  9. package/skills/mudra-master/mudra-preview/assets/drum-machine.html +788 -0
  10. package/skills/mudra-master/mudra-preview/assets/emg-visualizer.html +390 -0
  11. package/skills/mudra-master/mudra-preview/assets/generative-art.html +408 -0
  12. package/skills/mudra-master/mudra-preview/assets/gesture-assistant.html +342 -0
  13. package/skills/mudra-master/mudra-preview/assets/gesture-speech.html +444 -0
  14. package/skills/mudra-master/mudra-preview/assets/hands-free-desktop.html +376 -0
  15. package/skills/mudra-master/mudra-preview/assets/model-rotator.html +365 -0
  16. package/skills/mudra-master/mudra-preview/assets/mudra-duel.html +3200 -0
  17. package/skills/mudra-master/mudra-preview/assets/mudra-monitor.html +1451 -0
  18. package/skills/mudra-master/mudra-preview/assets/mudra-ultimate-template.html +891 -0
  19. package/skills/mudra-master/mudra-preview/assets/music-sequencer.html +455 -0
  20. package/skills/mudra-master/mudra-preview/assets/neural-pong.html +380 -0
  21. package/skills/mudra-master/mudra-preview/assets/neural-snake.html +364 -0
  22. package/skills/mudra-master/mudra-preview/assets/presentation-controller.html +345 -0
  23. package/skills/mudra-master/mudra-preview/assets/pressure-painter.html +285 -0
  24. package/skills/mudra-master/mudra-preview/assets/runner.html +1812 -0
  25. package/skills/mudra-master/mudra-preview/assets/smart-home.html +461 -0
  26. package/skills/mudra-master/mudra-preview/assets/space-invaders.html +1154 -0
  27. package/skills/mudra-master/mudra-preview/assets/waterful-ring-toss.html +1624 -0
  28. package/skills/mudra-master/mudra-preview/references/agent_protocol.json +646 -0
  29. package/skills/mudra-master/mudra-preview/references/promt.md +16829 -0
  30. package/skills/mudra-master/mudra-xr/SKILL.md +245 -0
  31. package/skills/mudra-master/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
  32. package/skills/mudra-master/mudra-xr/assets/demos/aisimulator.html +792 -0
  33. package/skills/mudra-master/mudra-xr/assets/demos/balloonpop.html +422 -0
  34. package/skills/mudra-master/mudra-xr/assets/demos/ballpit.html +284 -0
  35. package/skills/mudra-master/mudra-xr/assets/demos/drone.html +41 -0
  36. package/skills/mudra-master/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
  37. package/skills/mudra-master/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
  38. package/skills/mudra-master/mudra-xr/assets/demos/math3d.html +188 -0
  39. package/skills/mudra-master/mudra-xr/assets/demos/measure.html +247 -0
  40. package/skills/mudra-master/mudra-xr/assets/demos/occlusion.html +251 -0
  41. package/skills/mudra-master/mudra-xr/assets/demos/rain.html +498 -0
  42. package/skills/mudra-master/mudra-xr/assets/demos/screenwiper.html +432 -0
  43. package/skills/mudra-master/mudra-xr/assets/demos/splash.html +519 -0
  44. package/skills/mudra-master/mudra-xr/assets/demos/webcam_gestures.html +253 -0
  45. package/skills/mudra-master/mudra-xr/assets/demos/xremoji.html +833 -0
  46. package/skills/mudra-master/mudra-xr/assets/demos/xrpoet.html +151 -0
  47. package/skills/mudra-master/mudra-xr/assets/samples/depthmap.html +266 -0
  48. package/skills/mudra-master/mudra-xr/assets/samples/depthmesh.html +100 -0
  49. package/skills/mudra-master/mudra-xr/assets/samples/game_rps.html +1099 -0
  50. package/skills/mudra-master/mudra-xr/assets/samples/gestures_custom.html +467 -0
  51. package/skills/mudra-master/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
  52. package/skills/mudra-master/mudra-xr/assets/samples/lighting.html +163 -0
  53. package/skills/mudra-master/mudra-xr/assets/samples/mesh_detection.html +46 -0
  54. package/skills/mudra-master/mudra-xr/assets/samples/modelviewer.html +183 -0
  55. package/skills/mudra-master/mudra-xr/assets/samples/paint.html +133 -0
  56. package/skills/mudra-master/mudra-xr/assets/samples/planar-vst.html +45 -0
  57. package/skills/mudra-master/mudra-xr/assets/samples/reticle.html +95 -0
  58. package/skills/mudra-master/mudra-xr/assets/samples/skybox_agent.html +384 -0
  59. package/skills/mudra-master/mudra-xr/assets/samples/sound.html +390 -0
  60. package/skills/mudra-master/mudra-xr/assets/samples/ui.html +496 -0
  61. package/skills/mudra-master/mudra-xr/assets/samples/virtual-screens.html +808 -0
  62. package/skills/mudra-master/mudra-xr/assets/templates/0_basic.html +64 -0
  63. package/skills/mudra-master/mudra-xr/assets/templates/1_ui.html +107 -0
  64. package/skills/mudra-master/mudra-xr/assets/templates/2_hands.html +183 -0
  65. package/skills/mudra-master/mudra-xr/assets/templates/3_depth.html +89 -0
  66. package/skills/mudra-master/mudra-xr/assets/templates/4_stereo.html +84 -0
  67. package/skills/mudra-master/mudra-xr/assets/templates/5_camera.html +117 -0
  68. package/skills/mudra-master/mudra-xr/assets/templates/6_ai.html +154 -0
  69. package/skills/mudra-master/mudra-xr/assets/templates/7_ai_live.html +253 -0
  70. package/skills/mudra-master/mudra-xr/assets/templates/8_objects.html +95 -0
  71. package/skills/mudra-master/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
  72. package/skills/mudra-master/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
  73. package/skills/mudra-master/mudra-xr/assets/templates/meshes.html +47 -0
  74. package/skills/mudra-master/mudra-xr/assets/templates/planes.html +47 -0
  75. package/skills/mudra-master/mudra-xr/assets/templates/uikit.html +187 -0
  76. package/skills/mudra-master/mudra-xr/references/agent_protocol.json +878 -0
  77. package/skills/mudra-master/mudra-xr/references/promt.md +1516 -0
  78. package/skills/mudra-preview/SKILL.md +151 -0
  79. package/skills/mudra-preview/assets/ar-menu.html +382 -0
  80. package/skills/mudra-preview/assets/document-scroller.html +468 -0
  81. package/skills/mudra-preview/assets/drum-machine.html +788 -0
  82. package/skills/mudra-preview/assets/emg-visualizer.html +390 -0
  83. package/skills/mudra-preview/assets/generative-art.html +408 -0
  84. package/skills/mudra-preview/assets/gesture-assistant.html +342 -0
  85. package/skills/mudra-preview/assets/gesture-speech.html +444 -0
  86. package/skills/mudra-preview/assets/hands-free-desktop.html +376 -0
  87. package/skills/mudra-preview/assets/model-rotator.html +365 -0
  88. package/skills/mudra-preview/assets/mudra-duel.html +3200 -0
  89. package/skills/mudra-preview/assets/mudra-monitor.html +1451 -0
  90. package/skills/mudra-preview/assets/mudra-ultimate-template.html +891 -0
  91. package/skills/mudra-preview/assets/music-sequencer.html +455 -0
  92. package/skills/mudra-preview/assets/neural-pong.html +380 -0
  93. package/skills/mudra-preview/assets/neural-snake.html +364 -0
  94. package/skills/mudra-preview/assets/presentation-controller.html +345 -0
  95. package/skills/mudra-preview/assets/pressure-painter.html +285 -0
  96. package/skills/mudra-preview/assets/runner.html +1812 -0
  97. package/skills/mudra-preview/assets/smart-home.html +461 -0
  98. package/skills/mudra-preview/assets/space-invaders.html +1154 -0
  99. package/skills/mudra-preview/assets/waterful-ring-toss.html +1624 -0
  100. package/skills/mudra-preview/references/agent_protocol.json +646 -0
  101. package/skills/mudra-preview/references/promt.md +16829 -0
  102. package/skills/mudra-xr/SKILL.md +245 -0
  103. package/skills/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
  104. package/skills/mudra-xr/assets/demos/aisimulator.html +792 -0
  105. package/skills/mudra-xr/assets/demos/balloonpop.html +422 -0
  106. package/skills/mudra-xr/assets/demos/ballpit.html +284 -0
  107. package/skills/mudra-xr/assets/demos/drone.html +41 -0
  108. package/skills/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
  109. package/skills/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
  110. package/skills/mudra-xr/assets/demos/math3d.html +188 -0
  111. package/skills/mudra-xr/assets/demos/measure.html +247 -0
  112. package/skills/mudra-xr/assets/demos/occlusion.html +251 -0
  113. package/skills/mudra-xr/assets/demos/rain.html +498 -0
  114. package/skills/mudra-xr/assets/demos/screenwiper.html +432 -0
  115. package/skills/mudra-xr/assets/demos/splash.html +519 -0
  116. package/skills/mudra-xr/assets/demos/webcam_gestures.html +253 -0
  117. package/skills/mudra-xr/assets/demos/xremoji.html +833 -0
  118. package/skills/mudra-xr/assets/demos/xrpoet.html +151 -0
  119. package/skills/mudra-xr/assets/samples/depthmap.html +266 -0
  120. package/skills/mudra-xr/assets/samples/depthmesh.html +100 -0
  121. package/skills/mudra-xr/assets/samples/game_rps.html +1099 -0
  122. package/skills/mudra-xr/assets/samples/gestures_custom.html +467 -0
  123. package/skills/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
  124. package/skills/mudra-xr/assets/samples/lighting.html +163 -0
  125. package/skills/mudra-xr/assets/samples/mesh_detection.html +46 -0
  126. package/skills/mudra-xr/assets/samples/modelviewer.html +183 -0
  127. package/skills/mudra-xr/assets/samples/paint.html +133 -0
  128. package/skills/mudra-xr/assets/samples/planar-vst.html +45 -0
  129. package/skills/mudra-xr/assets/samples/reticle.html +95 -0
  130. package/skills/mudra-xr/assets/samples/skybox_agent.html +384 -0
  131. package/skills/mudra-xr/assets/samples/sound.html +390 -0
  132. package/skills/mudra-xr/assets/samples/ui.html +496 -0
  133. package/skills/mudra-xr/assets/samples/virtual-screens.html +808 -0
  134. package/skills/mudra-xr/assets/templates/0_basic.html +64 -0
  135. package/skills/mudra-xr/assets/templates/1_ui.html +107 -0
  136. package/skills/mudra-xr/assets/templates/2_hands.html +183 -0
  137. package/skills/mudra-xr/assets/templates/3_depth.html +89 -0
  138. package/skills/mudra-xr/assets/templates/4_stereo.html +84 -0
  139. package/skills/mudra-xr/assets/templates/5_camera.html +117 -0
  140. package/skills/mudra-xr/assets/templates/6_ai.html +154 -0
  141. package/skills/mudra-xr/assets/templates/7_ai_live.html +253 -0
  142. package/skills/mudra-xr/assets/templates/8_objects.html +95 -0
  143. package/skills/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
  144. package/skills/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
  145. package/skills/mudra-xr/assets/templates/meshes.html +47 -0
  146. package/skills/mudra-xr/assets/templates/planes.html +47 -0
  147. package/skills/mudra-xr/assets/templates/uikit.html +187 -0
  148. package/skills/mudra-xr/references/agent_protocol.json +878 -0
  149. package/skills/mudra-xr/references/promt.md +1516 -0
@@ -0,0 +1,390 @@
1
+ <!doctype html>
2
+ <!-- reference: samples/sound -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Sound Sample | XR Blocks</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+
12
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
13
+ <script type="importmap">
14
+ {
15
+ "imports": {
16
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
17
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
18
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
19
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
20
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
21
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
22
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
23
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js"
24
+ }
25
+ }
26
+ </script>
27
+ </head>
28
+
29
+ <body>
30
+ <script type="module">
31
+ /* eslint-env browser */
32
+ import * as THREE from 'three';
33
+ import * as xb from 'xrblocks';
34
+
35
+ class SoundDemoScript extends xb.Script {
36
+ constructor() {
37
+ super();
38
+ this.soundBalls = [];
39
+ this.mainPanel = null;
40
+ this.recordedAudioBuffer = null;
41
+ this.isRecording = false;
42
+ this.recordBtn = null;
43
+ this.statusText = null;
44
+ this.volumeText = null;
45
+ this.recordingStartTime = 0;
46
+ this.currentVolume = 1.0;
47
+ this.ballJumpPhase = 0;
48
+ }
49
+
50
+ init() {
51
+ this.createSoundBalls();
52
+ this.createDemoUI();
53
+
54
+ const light = new THREE.DirectionalLight(0xffffff, 1.5);
55
+ light.position.set(0, 3, 0);
56
+ this.add(light);
57
+
58
+ const ambientLight = new THREE.AmbientLight(0x404040, 1.0);
59
+ this.add(ambientLight);
60
+ }
61
+
62
+ createSoundBalls() {
63
+ const ballPositions = [
64
+ {x: -1.0, y: xb.user.height * 0.5, z: -1.5, color: 0xff6b6b},
65
+ {x: 0.0, y: xb.user.height * 0.5, z: -1.5, color: 0x4ecdc4},
66
+ {x: 1.0, y: xb.user.height * 0.5, z: -1.5, color: 0xffe66d},
67
+ ];
68
+
69
+ ballPositions.forEach((pos, index) => {
70
+ const geometry = new THREE.SphereGeometry(0.1, 32, 32);
71
+ const material = new THREE.MeshStandardMaterial({
72
+ color: pos.color,
73
+ metalness: 0.3,
74
+ roughness: 0.4,
75
+ });
76
+ const ball = new THREE.Mesh(geometry, material);
77
+ ball.position.set(pos.x, pos.y, pos.z);
78
+ ball.userData.soundIndex = index;
79
+ ball.name = `SoundBall${index}`;
80
+ this.add(ball);
81
+ this.soundBalls.push(ball);
82
+ });
83
+ }
84
+
85
+ createDemoUI() {
86
+ this.mainPanel = new xb.SpatialPanel({
87
+ backgroundColor: '#1a1a1aF0',
88
+ useDefaultPosition: false,
89
+ showEdge: true,
90
+ width: 1.0,
91
+ height: 0.8,
92
+ });
93
+ this.mainPanel.isRoot = true;
94
+ this.mainPanel.position.set(
95
+ 0,
96
+ xb.user.height + 0.2,
97
+ -xb.user.panelDistance
98
+ );
99
+ this.add(this.mainPanel);
100
+
101
+ const mainGrid = this.mainPanel.addGrid();
102
+
103
+ const titleRow = mainGrid.addRow({weight: 0.18});
104
+ titleRow.addText({
105
+ text: 'Sound Recorder',
106
+ fontSize: 0.08,
107
+ fontColor: '#4ecdc4',
108
+ });
109
+
110
+ const statusRow = mainGrid.addRow({weight: 0.15});
111
+ this.statusText = statusRow.addText({
112
+ text: 'Click mic to record',
113
+ fontSize: 0.05,
114
+ fontColor: '#ffe66d',
115
+ });
116
+
117
+ mainGrid.addRow({weight: 0.1});
118
+
119
+ const controlRow = mainGrid.addRow({weight: 0.35});
120
+
121
+ {
122
+ const recordCol = controlRow.addCol({weight: 0.4});
123
+ this.recordBtn = recordCol.addIconButton({
124
+ text: 'mic',
125
+ fontSize: 0.5,
126
+ });
127
+
128
+ this.recordBtn.onTriggered = () => {
129
+ this.toggleRecording();
130
+ };
131
+ }
132
+
133
+ {
134
+ const volDownCol = controlRow.addCol({weight: 0.2});
135
+ const volDownBtn = volDownCol.addIconButton({
136
+ text: 'remove',
137
+ fontSize: 0.5,
138
+ });
139
+
140
+ volDownBtn.onTriggered = () => {
141
+ this.adjustVolume(-0.1);
142
+ };
143
+ }
144
+
145
+ {
146
+ const volDisplayCol = controlRow.addCol({weight: 0.2});
147
+ this.volumeText = volDisplayCol.addText({
148
+ text: '100%',
149
+ fontSize: 0.5,
150
+ fontColor: '#4ecdc4',
151
+ });
152
+ }
153
+
154
+ {
155
+ const volUpCol = controlRow.addCol({weight: 0.2});
156
+ const volUpBtn = volUpCol.addIconButton({
157
+ text: 'add',
158
+ fontSize: 0.5,
159
+ });
160
+
161
+ volUpBtn.onTriggered = () => {
162
+ this.adjustVolume(0.1);
163
+ };
164
+ }
165
+
166
+ const bottomRow = mainGrid.addRow({weight: 0.2});
167
+ bottomRow.addText({
168
+ text: 'Click jumping balls to play',
169
+ fontSize: 0.045,
170
+ fontColor: '#888888',
171
+ });
172
+
173
+ if (this.mainPanel) {
174
+ this.mainPanel.updateLayouts();
175
+ }
176
+ }
177
+
178
+ async toggleRecording() {
179
+ if (this.isRecording) {
180
+ this.isRecording = false;
181
+ this.updateStatus('Stopping recording...');
182
+ await new Promise((resolve) => setTimeout(resolve, 300));
183
+ this.recordedAudioBuffer = xb.core.sound.stopRecording();
184
+
185
+ if (this.recordedAudioBuffer && this.recordedAudioBuffer.byteLength > 0) {
186
+ const duration = (
187
+ (Date.now() - this.recordingStartTime) /
188
+ 1000
189
+ ).toFixed(1);
190
+ this.updateStatus(`Recorded ${duration}s - Click balls to play`);
191
+ } else {
192
+ this.recordedAudioBuffer = null;
193
+ this.updateStatus('Recording failed - no data captured');
194
+ }
195
+
196
+ this.recordBtn.text = 'mic';
197
+ } else {
198
+ // Start recording using SDK
199
+ try {
200
+ await xb.core.sound.startRecording();
201
+
202
+ this.isRecording = true;
203
+ this.recordingStartTime = Date.now();
204
+ this.updateStatus('Recording... Click mic again to stop');
205
+ this.recordBtn.text = 'mic_off';
206
+ } catch (error) {
207
+ this.updateStatus('Recording failed - ' + error);
208
+ this.isRecording = false;
209
+ }
210
+ }
211
+ }
212
+
213
+ async playRecording() {
214
+ if (!this.recordedAudioBuffer) {
215
+ this.updateStatus('No recording - click mic first!');
216
+ return;
217
+ }
218
+
219
+ try {
220
+ this.updateStatus('Playing recording...');
221
+
222
+ const sampleRate = xb.core.sound.getRecordingSampleRate();
223
+ await xb.core.sound.playRecordedAudio(
224
+ this.recordedAudioBuffer,
225
+ sampleRate
226
+ );
227
+
228
+ setTimeout(() => {
229
+ if (!this.isRecording) {
230
+ this.updateStatus('Click mic to record');
231
+ }
232
+ }, 2000);
233
+ } catch (error) {
234
+ this.updateStatus('Playback failed: ' + error);
235
+ }
236
+ }
237
+
238
+ adjustVolume(delta) {
239
+ this.currentVolume = Math.max(0, Math.min(1, this.currentVolume + delta));
240
+ const volumePercent = Math.round(this.currentVolume * 100);
241
+
242
+ xb.core.sound.setMasterVolume(this.currentVolume);
243
+
244
+ if (this.volumeText) {
245
+ this.volumeText.text = `${volumePercent}%`;
246
+ }
247
+
248
+ this.updateStatus(`Volume: ${volumePercent}%`);
249
+ }
250
+
251
+ updateStatus(message) {
252
+ if (this.statusText) {
253
+ this.statusText.text = message;
254
+ }
255
+ }
256
+
257
+ onSelectStart(event) {
258
+ const controller = event.target;
259
+
260
+ this.soundBalls.forEach((ball) => {
261
+ const intersection = xb.core.user.select(ball, controller);
262
+ if (intersection) {
263
+ if (this.recordedAudioBuffer) {
264
+ this.playRecordingFromBall(ball);
265
+ this.updateStatus(
266
+ `Playing from ball ${ball.userData.soundIndex + 1}`
267
+ );
268
+ } else {
269
+ this.updateStatus('Record something first!');
270
+ }
271
+
272
+ this.pulseBall(ball);
273
+ }
274
+ });
275
+ }
276
+
277
+ async playRecordingFromBall(ball) {
278
+ if (!this.recordedAudioBuffer) return;
279
+
280
+ try {
281
+ const sampleRate = xb.core.sound.getRecordingSampleRate();
282
+ const audioListener = xb.core.sound.getAudioListener();
283
+
284
+ const audioContext = new AudioContext({sampleRate: sampleRate});
285
+
286
+ const int16Data = new Int16Array(this.recordedAudioBuffer);
287
+ const audioBuffer = audioContext.createBuffer(
288
+ 1,
289
+ int16Data.length,
290
+ sampleRate
291
+ );
292
+ const channelData = audioBuffer.getChannelData(0);
293
+ for (let i = 0; i < int16Data.length; i++) {
294
+ channelData[i] = int16Data[i] / 32768.0;
295
+ }
296
+
297
+ const positionalAudio = new THREE.PositionalAudio(audioListener);
298
+ positionalAudio.setBuffer(audioBuffer);
299
+ positionalAudio.setRefDistance(0.5);
300
+ positionalAudio.setRolloffFactor(2.0);
301
+ positionalAudio.setVolume(this.currentVolume);
302
+
303
+ ball.add(positionalAudio);
304
+ positionalAudio.play();
305
+
306
+ positionalAudio.onEnded = () => {
307
+ ball.remove(positionalAudio);
308
+ audioContext.close();
309
+ };
310
+ } catch (error) {
311
+ this.updateStatus('Play recording from ball failed: ' + error);
312
+ }
313
+ }
314
+
315
+ pulseBall(ball) {
316
+ const originalScale = ball.scale.clone();
317
+ const targetScale = originalScale.clone().multiplyScalar(1.3);
318
+ const startTime = Date.now();
319
+ const duration = 200;
320
+
321
+ const animate = () => {
322
+ const elapsed = Date.now() - startTime;
323
+ const progress = Math.min(elapsed / duration, 1);
324
+
325
+ if (progress < 0.5) {
326
+ const t = progress * 2;
327
+ ball.scale.lerpVectors(originalScale, targetScale, t);
328
+ } else {
329
+ const t = (progress - 0.5) * 2;
330
+ ball.scale.lerpVectors(targetScale, originalScale, t);
331
+ }
332
+
333
+ if (progress < 1) {
334
+ requestAnimationFrame(animate);
335
+ } else {
336
+ ball.scale.copy(originalScale);
337
+ }
338
+ };
339
+
340
+ animate();
341
+ }
342
+
343
+ update() {
344
+ this.soundBalls.forEach((ball, index) => {
345
+ ball.rotation.y += 0.01 * (index + 1);
346
+
347
+ if (this.recordedAudioBuffer) {
348
+ this.ballJumpPhase += 0.01;
349
+ const jumpHeight = 0.08;
350
+ const baseHeight = xb.user.height * 0.5;
351
+ const jumpOffset =
352
+ Math.abs(Math.sin(this.ballJumpPhase + (index * Math.PI) / 3)) *
353
+ jumpHeight;
354
+ ball.position.y = baseHeight + jumpOffset;
355
+
356
+ const targetScale = 1.1;
357
+ ball.scale.lerp(
358
+ new THREE.Vector3(targetScale, targetScale, targetScale),
359
+ 0.1
360
+ );
361
+ } else {
362
+ const baseHeight = xb.user.height * 0.5;
363
+ ball.position.y = baseHeight;
364
+ ball.scale.lerp(new THREE.Vector3(1, 1, 1), 0.1);
365
+ }
366
+ });
367
+ }
368
+
369
+ destroy() {
370
+ if (this.isRecording) {
371
+ xb.core.sound.disableAudio();
372
+ }
373
+ super.destroy();
374
+ }
375
+ }
376
+
377
+ document.addEventListener('DOMContentLoaded', function () {
378
+ const options = new xb.Options();
379
+ options.simulator.instructions.enabled = false;
380
+ options.reticles.enabled = true;
381
+ options.controllers.visualizeRays = true;
382
+ options.setAppTitle('XR Sound');
383
+
384
+ xb.add(new SoundDemoScript());
385
+ xb.init(options);
386
+ });
387
+
388
+ </script>
389
+ </body>
390
+ </html>