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,284 @@
1
+ <!doctype html>
2
+ <!-- reference: demos/ballpit -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Ballpit</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
12
+ <script>
13
+ window.litDisableBundleWarning = true;
14
+ </script>
15
+ <script type="importmap">
16
+ {
17
+ "imports": {
18
+ "@dimforge/rapier3d-simd-compat": "https://cdn.jsdelivr.net/npm/@dimforge/rapier3d-simd-compat@0.19.2/rapier.mjs",
19
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
20
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
21
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
22
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
23
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
24
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
25
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
26
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
27
+ "lit/": "https://esm.run/lit@3/",
28
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
29
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
30
+ }
31
+ }
32
+ </script>
33
+ </head>
34
+
35
+ <body>
36
+ <div
37
+ class="background-image"
38
+ style="background-image: url('textures/background.webp')"
39
+ ></div>
40
+ <script type="module">
41
+ import 'xrblocks/addons/simulator/SimulatorAddons.js';
42
+
43
+ import RAPIER from '@dimforge/rapier3d-simd-compat';
44
+ import * as xb from 'xrblocks';
45
+ import * as THREE from 'three';
46
+ import {palette} from 'xrblocks/addons/utils/Palette.js';
47
+
48
+ // --- BallShooter inlined ---
49
+ class BallShooter extends xb.Script {
50
+ constructor({
51
+ numBalls = 100,
52
+ radius = 0.08,
53
+ palette: pal = null,
54
+ liveDuration = 3000,
55
+ deflateDuration = 200,
56
+ distanceThreshold = 0.25,
57
+ distanceFadeout = 0.25,
58
+ }) {
59
+ super();
60
+ this.liveDuration = liveDuration;
61
+ this.deflateDuration = deflateDuration;
62
+ this.distanceThreshold = distanceThreshold;
63
+ this.distanceFadeout = distanceFadeout;
64
+ const geometry = new THREE.IcosahedronGeometry(radius, 3);
65
+ this.spheres = [];
66
+ for (let i = 0; i < numBalls; ++i) {
67
+ const material = new THREE.MeshLambertMaterial({transparent: true});
68
+ const sphere = new THREE.Mesh(geometry, material);
69
+ sphere.castShadow = true;
70
+ sphere.receiveShadow = true;
71
+ this.spheres.push(sphere);
72
+ }
73
+ for (let i = 0; i < this.spheres.length; i++) {
74
+ this.spheres[i].position.set(Math.random() * 2 - 2, Math.random() * 2, Math.random() * 2 - 2);
75
+ if (pal != null) this.spheres[i].material.color.copy(pal.getRandomLiteGColor());
76
+ }
77
+ const now = performance.now();
78
+ this.spawnTimes = [];
79
+ for (let i = 0; i < numBalls; ++i) this.spawnTimes[i] = now;
80
+ this.nextBall = 0;
81
+ this.rigidBodies = [];
82
+ this.colliders = [];
83
+ this.colliderHandleToIndex = new Map();
84
+ this.viewSpacePosition = new THREE.Vector3();
85
+ this.clipSpacePosition = new THREE.Vector3();
86
+ this.projectedPosition = new THREE.Vector3();
87
+ this.clipFromWorld = new THREE.Matrix4();
88
+ }
89
+
90
+ initPhysics(physics) {
91
+ this.setupPhysics({
92
+ RAPIER: physics.RAPIER,
93
+ blendedWorld: physics.blendedWorld,
94
+ colliderActiveEvents: physics.RAPIER.ActiveEvents.CONTACT_FORCE_EVENTS,
95
+ });
96
+ }
97
+
98
+ setupPhysics({RAPIER, blendedWorld, colliderActiveEvents = 0, continuousCollisionDetection = false}) {
99
+ this.RAPIER = RAPIER;
100
+ this.blendedWorld = blendedWorld;
101
+ this.colliderActiveEvents = colliderActiveEvents;
102
+ this.continuousCollisionDetection = continuousCollisionDetection;
103
+ }
104
+
105
+ spawnBallAt(position, velocity = new THREE.Vector3(), now = performance.now()) {
106
+ const ball = this.spheres[this.nextBall];
107
+ ball.position.copy(position);
108
+ ball.scale.setScalar(1.0);
109
+ ball.opacity = 1.0;
110
+ this._createRigidBody(this.nextBall, position, velocity, ball.geometry.parameters.radius);
111
+ this.spawnTimes[this.nextBall] = now;
112
+ this.nextBall = (this.nextBall + 1) % this.spheres.length;
113
+ this.add(ball);
114
+ }
115
+
116
+ _createRigidBody(index, position, velocity, radius) {
117
+ if (this.rigidBodies[index] != null) this.blendedWorld.removeRigidBody(this.rigidBodies[index]);
118
+ const desc = this.RAPIER.RigidBodyDesc.dynamic().setTranslation(position.x, position.y, position.z).setLinvel(velocity.x, velocity.y, velocity.z).setCcdEnabled(this.continuousCollisionDetection);
119
+ const body = this.blendedWorld.createRigidBody(desc);
120
+ const shape = this.RAPIER.ColliderDesc.ball(radius).setActiveEvents(this.colliderActiveEvents);
121
+ const collider = this.blendedWorld.createCollider(shape, body);
122
+ this.colliderHandleToIndex.set(collider.handle, index);
123
+ this.rigidBodies[index] = body;
124
+ this.colliders[index] = collider;
125
+ }
126
+
127
+ physicsStep(now = performance.now()) {
128
+ for (let i = 0; i < this.spheres.length; i++) {
129
+ const sphere = this.spheres[i];
130
+ const body = this.rigidBodies[i];
131
+ let spawnTime = this.spawnTimes[i];
132
+ if (this.isBallActive(i) && body != null) {
133
+ let ballVisibility = 1.0;
134
+ const position = sphere.position.copy(body.translation());
135
+ const viewMatrix = xb.depth?.enabled ? xb.depth.depthViewMatrices[0] : xb.core.camera.matrixWorldInverse;
136
+ const projectionMatrix = xb.depth?.enabled ? xb.depth.depthProjectionMatrices[0] : xb.core.camera.projectionMatrix;
137
+ const viewSpacePosition = this.viewSpacePosition.copy(position).applyMatrix4(viewMatrix);
138
+ const clipSpacePosition = this.clipSpacePosition.copy(viewSpacePosition).applyMatrix4(projectionMatrix);
139
+ const ballIsInView = -1.0 <= clipSpacePosition.x && clipSpacePosition.x <= 1.0 && -1.0 <= clipSpacePosition.y && clipSpacePosition.y <= 1.0;
140
+ if (ballIsInView && xb.depth.enabled) {
141
+ const projectedPosition = xb.depth.getProjectedDepthViewPositionFromWorldPosition(position, this.projectedPosition);
142
+ const distanceBehindDepth = Math.max(projectedPosition.z - viewSpacePosition.z, 0.0);
143
+ if (distanceBehindDepth > this.distanceThreshold) {
144
+ const deflateAmount = Math.max((distanceBehindDepth - this.distanceThreshold) / this.distanceFadeout, 1.0);
145
+ spawnTime = Math.min(spawnTime, now - this.liveDuration - this.deflateDuration * deflateAmount);
146
+ }
147
+ }
148
+ if (now - spawnTime > this.liveDuration) {
149
+ const timeSinceDeflateStarted = now - spawnTime - this.liveDuration;
150
+ const deflateAmount = Math.min(1, timeSinceDeflateStarted / this.deflateDuration);
151
+ ballVisibility = 1.0 - deflateAmount;
152
+ }
153
+ sphere.material.opacity = ballVisibility;
154
+ if (ballVisibility < 0.001) { this.removeBall(i); }
155
+ else { sphere.position.copy(body.translation()); sphere.quaternion.copy(body.rotation()); }
156
+ }
157
+ }
158
+ }
159
+
160
+ getIndexForColliderHandle(handle) { return this.colliderHandleToIndex.get(handle); }
161
+
162
+ removeBall(index) {
163
+ const ball = this.spheres[index];
164
+ ball.material.opacity = 0.0;
165
+ ball.scale.setScalar(0);
166
+ const body = this.rigidBodies[index];
167
+ if (body != null) { this.blendedWorld.removeRigidBody(body); this.rigidBodies[index] = null; this.colliders[index] = null; }
168
+ this.remove(ball);
169
+ }
170
+
171
+ isBallActive(index) { return this.spheres[index].parent == this; }
172
+ }
173
+
174
+ // --- BallPit inlined ---
175
+ const kTimeLiveMs = xb.getUrlParamInt('timeLiveMs', 3000);
176
+ const kDefalteMs = xb.getUrlParamInt('defalteMs', 200);
177
+ const kLightX = xb.getUrlParamFloat('lightX', 0);
178
+ const kLightY = xb.getUrlParamFloat('lightY', 500);
179
+ const kLightZ = xb.getUrlParamFloat('lightZ', -10);
180
+ const kRadius = xb.getUrlParamFloat('radius', 0.08);
181
+ const kBallsPerSecond = xb.getUrlParamFloat('ballsPerSecond', 30);
182
+ const kVelocityScale = xb.getUrlParamInt('velocityScale', 1.0);
183
+ const kNumSpheres = 100;
184
+
185
+ class BallPit extends xb.Script {
186
+ constructor() {
187
+ super();
188
+ this.ballShooter = new BallShooter({numBalls: kNumSpheres, radius: kRadius, palette: palette, liveDuration: kTimeLiveMs, deflateDuration: kDefalteMs});
189
+ this.add(this.ballShooter);
190
+ this.addLights();
191
+ this.lastBallCreatedTimeForController = new Map();
192
+ this.pointer = new THREE.Vector2();
193
+ this.velocity = new THREE.Vector3();
194
+ }
195
+
196
+ init() { xb.add(this); }
197
+
198
+ update() {
199
+ super.update();
200
+ for (const controller of xb.core.input.controllers) this.controllerUpdate(controller);
201
+ }
202
+
203
+ addLights() {
204
+ this.add(new THREE.HemisphereLight(0xbbbbbb, 0x888888, 3));
205
+ const light = new THREE.DirectionalLight(0xffffff, 2);
206
+ light.position.set(kLightX, kLightY, kLightZ);
207
+ light.castShadow = true;
208
+ light.shadow.mapSize.width = 2048;
209
+ light.shadow.mapSize.height = 2048;
210
+ this.add(light);
211
+ }
212
+
213
+ updatePointerPosition(event) {
214
+ this.pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
215
+ this.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
216
+ this.pointer.x = 1 + 2 * this.pointer.x;
217
+ }
218
+
219
+ onPointerDown(event) {
220
+ this.updatePointerPosition(event);
221
+ const cameras = xb.core.renderer.xr.getCamera().cameras;
222
+ if (cameras.length == 0) return;
223
+ const camera = cameras[0];
224
+ const position = new THREE.Vector3(0.0, 0.0, -0.2).applyQuaternion(camera.quaternion).add(camera.position);
225
+ const matrix = new THREE.Matrix4();
226
+ matrix.setPosition(position.x, position.y, position.z);
227
+ const vector = new THREE.Vector4(this.pointer.x, this.pointer.y, 1.0, 1);
228
+ const inverseProjectionMatrix = camera.projectionMatrix.clone().invert();
229
+ vector.applyMatrix4(inverseProjectionMatrix);
230
+ vector.multiplyScalar(1 / vector.w);
231
+ this.velocity.copy(vector);
232
+ this.velocity.normalize().multiplyScalar(4.0);
233
+ this.velocity.applyQuaternion(camera.quaternion);
234
+ this.ballShooter.spawnBallAt(position, this.velocity);
235
+ }
236
+
237
+ controllerUpdate(controller) {
238
+ const now = performance.now();
239
+ if (!this.lastBallCreatedTimeForController.has(controller)) this.lastBallCreatedTimeForController.set(controller, -99);
240
+ if (controller.userData.selected && now - this.lastBallCreatedTimeForController.get(controller) >= 1000 / kBallsPerSecond) {
241
+ const newPosition = new THREE.Vector3(0.0, 0.0, -0.08).applyQuaternion(controller.quaternion).add(controller.position);
242
+ this.velocity.set(0, 0, -5.0 * kVelocityScale);
243
+ this.velocity.applyQuaternion(controller.quaternion);
244
+ this.ballShooter.spawnBallAt(newPosition, this.velocity);
245
+ this.lastBallCreatedTimeForController.set(controller, now);
246
+ }
247
+ }
248
+ }
249
+
250
+ const depthMeshColliderUpdateFps = xb.getUrlParamFloat('depthMeshColliderUpdateFps', 5);
251
+ const useSceneMesh = xb.getUrlParamBool('scenemesh', false);
252
+ const sceneMeshDebug = xb.getUrlParamBool('scenemeshdebug', false);
253
+
254
+ const options = new xb.Options();
255
+ options.simulator.instructions.enabled = false;
256
+ if (useSceneMesh) {
257
+ options.world.enableMeshDetection();
258
+ options.world.meshes.showDebugVisualizations = sceneMeshDebug;
259
+ } else {
260
+ options.depth = new xb.DepthOptions(xb.xrDepthMeshPhysicsOptions);
261
+ options.depth.depthMesh.colliderUpdateFps = depthMeshColliderUpdateFps;
262
+ options.depth.matchDepthView = false;
263
+ }
264
+ options.reticles.enabled = false;
265
+ options.controllers.performRaycastOnUpdate = false;
266
+ options.xrButton = {
267
+ ...options.xrButton,
268
+ startText: '<i id="xrlogo"></i> LET THE FUN BEGIN',
269
+ endText: '<i id="xrlogo"></i> MISSION COMPLETE',
270
+ };
271
+ options.physics.RAPIER = RAPIER;
272
+
273
+ async function start() {
274
+ xb.add(new BallPit());
275
+ await xb.init(options);
276
+ }
277
+
278
+ document.addEventListener('DOMContentLoaded', function () {
279
+ start();
280
+ });
281
+
282
+ </script>
283
+ </body>
284
+ </html>
@@ -0,0 +1,41 @@
1
+ <!doctype html>
2
+ <!-- reference: demos/drone -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Drone</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
12
+ <script>
13
+ window.litDisableBundleWarning = true;
14
+ </script>
15
+ <script type="importmap">
16
+ {
17
+ "imports": {
18
+ "@dimforge/rapier3d-simd-compat": "https://cdn.jsdelivr.net/npm/@dimforge/rapier3d-simd-compat@0.19.2/rapier.mjs",
19
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
20
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
21
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
22
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
23
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
24
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
25
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
26
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
27
+ "lit/": "https://esm.run/lit@3/",
28
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
29
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
30
+ }
31
+ }
32
+ </script>
33
+ </head>
34
+
35
+ <body>
36
+ <!-- NOTE: The original drone demo references build/main.js (a compiled build artifact
37
+ not included in xrpromt.md). No companion JS source was provided for this demo.
38
+ This file preserves the original HTML shell as-is from the reference corpus. -->
39
+ <script type="module" src="build/main.js"></script>
40
+ </body>
41
+ </html>
@@ -0,0 +1,338 @@
1
+ <!doctype html>
2
+ <!-- reference: demos/gemini-icebreakers -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Gemini Icebreakers</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
12
+ <script>
13
+ window.litDisableBundleWarning = true;
14
+ </script>
15
+ <script type="importmap">
16
+ {
17
+ "imports": {
18
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
19
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
20
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
21
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
22
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
23
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
24
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
25
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
26
+ "lit/": "https://esm.run/lit@3/",
27
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
28
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/",
29
+ "@google/genai": "https://cdn.jsdelivr.net/npm/@google/genai/+esm"
30
+ }
31
+ }
32
+ </script>
33
+ </head>
34
+
35
+ <body>
36
+ <div
37
+ id="background-image"
38
+ class="background-image"
39
+ style="background-image: url('textures/background.webp')"
40
+ ></div>
41
+ <script type="module">
42
+ import 'xrblocks/addons/simulator/SimulatorAddons.js';
43
+
44
+ import * as THREE from 'three';
45
+ import * as xb from 'xrblocks';
46
+
47
+ // --- EarthAnimation inlined ---
48
+ class EarthAnimation {
49
+ model = null;
50
+ speed = 0.2;
51
+
52
+ setModel(model) { this.model = model; }
53
+
54
+ update(deltaTime) {
55
+ const gltfScene = this?.model?.gltf?.scene;
56
+ if (gltfScene) gltfScene.rotation.y += this.speed * deltaTime;
57
+ }
58
+ }
59
+
60
+ // --- TranscriptionManager inlined ---
61
+ class TranscriptionManager {
62
+ constructor(responseDisplay) {
63
+ this.responseDisplay = responseDisplay;
64
+ this.currentInputText = '';
65
+ this.currentOutputText = '';
66
+ this.conversationHistory = [];
67
+ }
68
+
69
+ handleInputTranscription(text) {
70
+ if (!text) return;
71
+ this.currentInputText += text;
72
+ this.updateLiveDisplay();
73
+ }
74
+
75
+ handleOutputTranscription(text) {
76
+ if (!text) return;
77
+ this.currentOutputText += text;
78
+ this.updateLiveDisplay();
79
+ }
80
+
81
+ finalizeTurn() {
82
+ if (this.currentInputText.trim()) this.conversationHistory.push({speaker: 'You', text: this.currentInputText.trim()});
83
+ if (this.currentOutputText.trim()) this.conversationHistory.push({speaker: 'AI', text: this.currentOutputText.trim()});
84
+ this.currentInputText = '';
85
+ this.currentOutputText = '';
86
+ this.updateFinalDisplay();
87
+ }
88
+
89
+ updateLiveDisplay() {
90
+ let displayText = '';
91
+ for (const entry of this.conversationHistory.slice(-2)) displayText += `${entry.speaker}: ${entry.text}\n\n`;
92
+ if (this.currentInputText.trim()) displayText += `You: ${this.currentInputText}`;
93
+ if (this.currentOutputText.trim()) { if (this.currentInputText.trim()) displayText += '\n\n'; displayText += `AI: ${this.currentOutputText}`; }
94
+ this.responseDisplay?.setText(displayText);
95
+ }
96
+
97
+ updateFinalDisplay() {
98
+ let displayText = '';
99
+ for (const entry of this.conversationHistory) displayText += `${entry.speaker}: ${entry.text}\n\n`;
100
+ this.responseDisplay?.setText(displayText);
101
+ }
102
+
103
+ clear() { this.currentInputText = ''; this.currentOutputText = ''; this.conversationHistory = []; }
104
+ addText(text) { this.responseDisplay?.addText(text + '\n\n'); }
105
+ setText(text) { this.responseDisplay?.setText(text); }
106
+ }
107
+
108
+ // --- GeminiIcebreakers inlined ---
109
+ const ASSETS_BASE_URL = 'https://cdn.jsdelivr.net/gh/xrblocks/assets@main/';
110
+ const PROPRIETARY_ASSETS_BASE_URL = 'https://cdn.jsdelivr.net/gh/xrblocks/proprietary-assets@main/';
111
+
112
+ const DATA = [
113
+ {
114
+ model: {scale: {x: 4.0, y: 4.0, z: 4.0}, path: PROPRIETARY_ASSETS_BASE_URL + 'monalisa/', model: 'mona_lisa_picture_frame_compressed.glb', verticallyAlignObject: false},
115
+ prompt: '"What is she smiling about?"',
116
+ },
117
+ {
118
+ model: {scale: {x: 0.03, y: 0.03, z: 0.03}, rotation: {x: 80, y: 0, z: 0}, position: {x: 0, y: -0.2, z: -3.0}, path: PROPRIETARY_ASSETS_BASE_URL + 'chess/', model: 'chess_compressed.glb', verticallyAlignObject: false},
119
+ prompt: '"What\'s a good strategy for this game?"',
120
+ },
121
+ {
122
+ model: {scale: {x: 0.9, y: 0.9, z: 0.9}, rotation: {x: 75, y: 0, z: 0}, position: {x: 0, y: 0.0, z: 0}, path: PROPRIETARY_ASSETS_BASE_URL + 'vegetable_on_board/', model: 'vegetable_on_board_compressed.glb', verticallyAlignObject: false},
123
+ prompt: '"What is the most unexpected dish you could make with these ingredients?"',
124
+ },
125
+ {
126
+ model: {path: ASSETS_BASE_URL + 'models/', model: 'Parasaurolophus.glb', scale: {x: 0.3, y: 0.3, z: 0.3}, position: {x: 0, y: -0.6, z: 0}, verticallyAlignObject: false, horizontallyAlignObject: false},
127
+ prompt: '"If this dinosaur could talk, what would it say?"',
128
+ },
129
+ {
130
+ model: {path: PROPRIETARY_ASSETS_BASE_URL + 'earth/', model: 'Earth_1_12756.glb', scale: {x: 0.001, y: 0.001, z: 0.001}, position: {x: 0, y: 0, z: 0}, verticallyAlignObject: false},
131
+ modelAnimation: new EarthAnimation(),
132
+ prompt: '"How big would I need to be to hold this in my hands?"',
133
+ },
134
+ ];
135
+
136
+ class GeminiIcebreakers extends xb.Script {
137
+ constructor() {
138
+ super();
139
+ this.data = DATA;
140
+ this.journeyId = 0;
141
+ this.models = [];
142
+ this.isAIRunning = false;
143
+ this.screenshotInterval = null;
144
+ this.time = 0;
145
+ this.micButtonInitialY = null;
146
+ this.transcriptionManager = null;
147
+
148
+ const panel = new xb.SpatialPanel({backgroundColor: '#00000000', useDefaultPosition: false, showEdge: false});
149
+ this.add(panel);
150
+
151
+ this.descriptionPagerState = new xb.PagerState({pages: DATA.length});
152
+ const grid = panel.addGrid();
153
+
154
+ const imageRow = grid.addRow({weight: 0.5});
155
+ imageRow.addCol({weight: 0.1});
156
+ this.imagePager = new xb.HorizontalPager({state: this.descriptionPagerState});
157
+ imageRow.addCol({weight: 0.8}).add(this.imagePager);
158
+ imageRow.addCol({weight: 0.1});
159
+ for (let i = 0; i < DATA.length; i++) {
160
+ if (DATA[i].src) this.imagePager.children[i].addImage({src: DATA[i].src});
161
+ else this.imagePager.children[i].add(new xb.View());
162
+ }
163
+
164
+ grid.addRow({weight: 0.15});
165
+ const controlRow = grid.addRow({weight: 0.35});
166
+ const ctrlPanel = controlRow.addPanel({backgroundColor: '#000000D9'});
167
+ const ctrlGrid = ctrlPanel.addGrid();
168
+ {
169
+ const leftColumn = ctrlGrid.addCol({weight: 0.1});
170
+ this.backButton = leftColumn.addIconButton({text: 'arrow_back', fontSize: 0.5, paddingX: 0.2});
171
+
172
+ const midColumn = ctrlGrid.addCol({weight: 0.8});
173
+ const descRow = midColumn.addRow({weight: 0.8});
174
+ this.descRow = descRow;
175
+
176
+ this.add(this.descriptionPagerState);
177
+ this.descriptionPager = new xb.HorizontalPager({state: this.descriptionPagerState, enableRaycastOnChildren: false});
178
+ descRow.add(this.descriptionPager);
179
+ this.transcriptView = new xb.ScrollingTroikaTextView({text: '', fontSize: 0.05, textAlign: 'left'});
180
+ for (let i = 0; i < DATA.length; i++) {
181
+ this.descriptionPager.children[i].add(new xb.TextView({text: this.data[i].prompt, fontColor: '#ffffff', imageOverlay: 'images/gradient.png', imageOffsetX: 0.2}));
182
+ }
183
+
184
+ const botRow = midColumn.addRow({weight: 0.1});
185
+ botRow.add(new xb.PageIndicator({pagerState: this.descriptionPager.state, fontColor: '#FFFFFF'}));
186
+
187
+ const rightColumn = ctrlGrid.addCol({weight: 0.1});
188
+ this.forwardButton = rightColumn.addIconButton({text: 'arrow_forward', fontSize: 0.5, paddingX: -0.2});
189
+
190
+ this.micButton = ctrlGrid.addCol({weight: 0.1}).addIconButton({text: 'mic', fontSize: 0.8, paddingX: -2, paddingY: -1, fontColor: '#fdfdfdff'});
191
+ this.micButton.onTriggered = () => { this.toggleGeminiLive(); };
192
+ }
193
+
194
+ const orbiter = ctrlGrid.addOrbiter();
195
+ orbiter.addExitButton();
196
+ panel.updateLayouts();
197
+ this.panel = panel;
198
+
199
+ this.backButton.onTriggered = (id) => { this.loadPrevious(); };
200
+ this.forwardButton.onTriggered = (id) => { this.loadNext(); };
201
+ }
202
+
203
+ init() {
204
+ this.loadModels();
205
+ xb.core.renderer.localClippingEnabled = true;
206
+ this.add(new THREE.HemisphereLight(0x888877, 0x777788, 3));
207
+ const light = new THREE.DirectionalLight(0xffffff, 5.0);
208
+ light.position.set(-0.5, 4, 1.0);
209
+ this.add(light);
210
+ this.panel.position.set(0, 1.2, -1.0);
211
+ if (!xb.core.ai || !xb.core.ai.options.gemini.apiKey) this.micButton.visible = false;
212
+ }
213
+
214
+ reload() {
215
+ const roundedCurrentPage = Math.round(this.descriptionPagerState.currentPage);
216
+ if (roundedCurrentPage != this.journeyId) this.descriptionPagerState.currentPage = this.journeyId;
217
+ }
218
+
219
+ loadPrevious() { this.journeyId = (this.journeyId - 1 + this.data.length) % this.data.length; this.reload(); }
220
+ loadNext() { this.journeyId = (this.journeyId + 1 + this.data.length) % this.data.length; this.reload(); }
221
+
222
+ update() {
223
+ const deltaTime = xb.getDeltaTime();
224
+ this.time += deltaTime;
225
+ if (this.micButtonInitialY === null && this.micButton.visible) this.micButtonInitialY = this.micButton.position.y;
226
+ const roundedCurrentPage = Math.round(this.descriptionPagerState.currentPage);
227
+ if (this.journeyId != roundedCurrentPage) { this.journeyId = roundedCurrentPage; this.reload(); }
228
+ for (const model of this.data) model.modelAnimation?.update(deltaTime);
229
+ if (this.micButtonInitialY !== null && this.micButton.visible && this.isAIRunning) {
230
+ const jumpHeight = 0.05, jumpSpeed = 4;
231
+ this.micButton.position.y = this.micButtonInitialY + Math.abs(Math.sin(this.time * jumpSpeed)) * jumpHeight;
232
+ } else if (this.micButtonInitialY !== null) {
233
+ this.micButton.position.y = this.micButtonInitialY;
234
+ }
235
+ }
236
+
237
+ loadModels() {
238
+ for (let i = 0; i < this.data.length; i++) {
239
+ if (this.data[i].model) {
240
+ const data = this.data[i];
241
+ const model = new xb.ModelViewer({});
242
+ model.loadGLTFModel({
243
+ data: this.data[i].model,
244
+ setupPlatform: false,
245
+ setupRaycastCylinder: false,
246
+ setupRaycastBox: true,
247
+ renderer: xb.core.renderer,
248
+ onSceneLoaded: () => {
249
+ this.reload();
250
+ this.imagePager.children[i].children[0].add(model);
251
+ data.modelAnimation?.setModel(model);
252
+ },
253
+ });
254
+ this.models[i] = model;
255
+ }
256
+ }
257
+ }
258
+
259
+ async toggleGeminiLive() { return this.isAIRunning ? this.stopGeminiLive() : this.startGeminiLive(); }
260
+
261
+ async startGeminiLive() {
262
+ if (this.isAIRunning) return;
263
+ try {
264
+ this.descriptionPager.visible = false;
265
+ this.descRow.add(this.transcriptView);
266
+ this.transcriptView.visible = true;
267
+ this.transcriptionManager = new TranscriptionManager(this.transcriptView);
268
+ await xb.core.sound.enableAudio();
269
+ await this.startLiveAI();
270
+ this.startScreenshotCapture();
271
+ this.isAIRunning = true;
272
+ } catch (error) { console.error('Failed to start AI session:', error); this.cleanup(); this.isAIRunning = false; }
273
+ }
274
+
275
+ async stopGeminiLive() {
276
+ if (this.transcriptionManager) this.transcriptionManager.clear();
277
+ if (!this.isAIRunning) return;
278
+ this.descriptionPager.state.currentPage = this.journeyId;
279
+ this.descriptionPager.visible = true;
280
+ this.transcriptView.visible = false;
281
+ await xb.core.ai?.stopLiveSession?.();
282
+ this.cleanup();
283
+ if (this.screenshotInterval) { clearInterval(this.screenshotInterval); this.screenshotInterval = null; }
284
+ }
285
+
286
+ async startLiveAI() {
287
+ return new Promise((resolve, reject) => {
288
+ xb.core.ai.setLiveCallbacks({
289
+ onopen: resolve,
290
+ onmessage: (message) => this.handleAIMessage(message),
291
+ onerror: reject,
292
+ onclose: (closeEvent) => { this.cleanup(); this.isAIRunning = false; },
293
+ });
294
+ xb.core.ai.startLiveSession().catch(reject);
295
+ });
296
+ }
297
+
298
+ handleAIMessage(message) {
299
+ message.data && xb.core.sound.playAIAudio(message.data);
300
+ const content = message.serverContent;
301
+ if (content) {
302
+ content.inputTranscription?.text && this.transcriptionManager.handleInputTranscription(content.inputTranscription.text);
303
+ content.outputTranscription?.text && this.transcriptionManager.handleOutputTranscription(content.outputTranscription.text);
304
+ content.turnComplete && this.transcriptionManager.finalizeTurn();
305
+ }
306
+ }
307
+
308
+ cleanup() { this.isAIRunning = false; }
309
+
310
+ startScreenshotCapture() {
311
+ this.screenshotInterval = setInterval(async () => {
312
+ const base64Image = await xb.core.screenshotSynthesizer.getScreenshot();
313
+ if (base64Image) {
314
+ const base64Data = base64Image.startsWith('data:') ? base64Image.split(',')[1] : base64Image;
315
+ try { xb.core.ai?.sendRealtimeInput?.({video: {data: base64Data, mimeType: 'image/png'}}); }
316
+ catch (error) { console.warn(error); this.stopGeminiLive(); }
317
+ }
318
+ }, 1000);
319
+ }
320
+ }
321
+
322
+ const options = new xb.Options({antialias: true, reticles: {enabled: true}, visualizeRays: true});
323
+ options.simulator.instructions.enabled = false;
324
+ options.enableAI();
325
+ options.enableCamera();
326
+
327
+ async function start() {
328
+ xb.add(new GeminiIcebreakers());
329
+ await xb.init(options);
330
+ }
331
+
332
+ document.addEventListener('DOMContentLoaded', function () {
333
+ start();
334
+ });
335
+
336
+ </script>
337
+ </body>
338
+ </html>