summaryrefslogtreecommitdiff
path: root/public/js/components/voice-level.js
diff options
context:
space:
mode:
authorskylar <me@casper>2026-11-01 05:43:24 -0800
committerskylar <me@casper>2026-11-01 05:43:24 -0800
commite69ad01e700e80fa959dfee08cd556ca0d777ffd (patch)
tree645fc9db2142e87a5ef302733a77ba2fec848d88 /public/js/components/voice-level.js
Yesod: self-hosted WebXR rooms
Start a room in a world and hang out in VR from the headset's browser: A-Frame + Networked-A-Frame over EasyRTC, with a coturn relay that can hide everyone's IP address. Ships with the Campfire, Temple, Cabin, Balcony and Test room worlds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'public/js/components/voice-level.js')
-rw-r--r--public/js/components/voice-level.js39
1 files changed, 39 insertions, 0 deletions
diff --git a/public/js/components/voice-level.js b/public/js/components/voice-level.js
new file mode 100644
index 0000000..44e9ccc
--- /dev/null
+++ b/public/js/components/voice-level.js
@@ -0,0 +1,39 @@
+/* global AFRAME */
+// Measures how loud a remote person is talking (0..1) from their incoming voice
+// stream, for lip-sync and the speaking indicator.
+AFRAME.registerComponent('voice-level', {
+ init() {
+ this.level = 0;
+ this.analyser = null;
+ this.samples = null;
+
+ this.onSoundSource = (evt) => {
+ const source = evt.detail.soundSource;
+ if (this.analyser) this.analyser.disconnect();
+ this.analyser = source.context.createAnalyser();
+ this.analyser.fftSize = 512;
+ this.samples = new Uint8Array(this.analyser.fftSize);
+ source.connect(this.analyser);
+ };
+ this.el.addEventListener('sound-source-set', this.onSoundSource);
+ },
+
+ remove() {
+ this.el.removeEventListener('sound-source-set', this.onSoundSource);
+ if (this.analyser) this.analyser.disconnect();
+ },
+
+ tick() {
+ if (!this.analyser) return;
+ this.analyser.getByteTimeDomainData(this.samples);
+ let sum = 0;
+ for (let i = 0; i < this.samples.length; i++) {
+ const v = (this.samples[i] - 128) / 128;
+ sum += v * v;
+ }
+ const rms = Math.sqrt(sum / this.samples.length);
+ const target = Math.min(1, Math.max(0, (rms - 0.012) * 9));
+ // Open quickly, close a little slower, so mouths don't chatter.
+ this.level += (target - this.level) * (target > this.level ? 0.6 : 0.2);
+ },
+});