diff --git a/ui/pose-fusion.html b/ui/pose-fusion.html
new file mode 100644
index 00000000..2b023c6f
--- /dev/null
+++ b/ui/pose-fusion.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
DUAL FUSION
+
+
+
Enable your webcam for live video pose estimation.
+ Or switch to CSI Only mode for WiFi-based sensing.
+
+
+
+
+
+
+
+
+
+
◆ Fusion Confidence
+
+
+ Cross-modal: 0.000
+
+
+
+
+
+
◆ CSI Amplitude Heatmap
+
+
+
+
+
+
+
+
◆ Embedding Space (2D Projection)
+
+
+
+
+
+
+
+
+
+
+
◆ Controls
+
+
+
+
+
+
+
+ 0.30
+
+
+
+
◆ Live CSI Source
+
+
+
+
+
+
+
+
+
+
+
+
+ WiFi-DensePose · Dual-Modal Pose Estimation ·
+ Architecture: MobileNet-V3 × 2 → Attention Fusion → 17-Keypoint COCO
+
+
+
+
+
+
+
+
+
diff --git a/ui/pose-fusion/build.sh b/ui/pose-fusion/build.sh
new file mode 100644
index 00000000..4d76eba2
--- /dev/null
+++ b/ui/pose-fusion/build.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Build WASM packages for the dual-modal pose estimation demo.
+# Requires: wasm-pack (cargo install wasm-pack)
+#
+# Usage: ./build.sh
+#
+# Output: pkg/ruvector_cnn_wasm/ — WASM CNN embedder for browser
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+VENDOR_DIR="$SCRIPT_DIR/../../vendor/ruvector"
+OUT_DIR="$SCRIPT_DIR/pkg/ruvector_cnn_wasm"
+
+echo "Building ruvector-cnn-wasm..."
+wasm-pack build "$VENDOR_DIR/crates/ruvector-cnn-wasm" \
+ --target web \
+ --out-dir "$OUT_DIR" \
+ --no-typescript
+
+# Remove .gitignore so we can commit the build output for GitHub Pages
+rm -f "$OUT_DIR/.gitignore"
+
+echo ""
+echo "Build complete!"
+echo " WASM: $(du -sh "$OUT_DIR/ruvector_cnn_wasm_bg.wasm" | cut -f1)"
+echo " JS: $(du -sh "$OUT_DIR/ruvector_cnn_wasm.js" | cut -f1)"
+echo ""
+echo "Serve the demo: cd $SCRIPT_DIR/.. && python3 -m http.server 8080"
+echo "Open: http://localhost:8080/pose-fusion.html"
diff --git a/ui/pose-fusion/js/main.js b/ui/pose-fusion/js/main.js
index 29f283f4..db045922 100644
--- a/ui/pose-fusion/js/main.js
+++ b/ui/pose-fusion/js/main.js
@@ -116,6 +116,18 @@ function init() {
visualCnn.tryLoadWasm(wasmBase);
csiCnn.tryLoadWasm(wasmBase);
+ // Auto-connect to local sensing server WebSocket if available
+ const defaultWsUrl = 'ws://localhost:8765/ws/sensing';
+ if (wsUrlInput) wsUrlInput.value = defaultWsUrl;
+ csiSimulator.connectLive(defaultWsUrl).then(ok => {
+ if (ok && connectWsBtn) {
+ connectWsBtn.textContent = '✓ Live ESP32';
+ connectWsBtn.classList.add('active');
+ statusLabel.textContent = 'LIVE CSI';
+ statusDot.classList.remove('offline');
+ }
+ });
+
// Auto-start camera for video/dual modes
updateModeUI();
startTime = performance.now() / 1000;
diff --git a/ui/pose-fusion/pkg/ruvector_cnn_wasm/package.json b/ui/pose-fusion/pkg/ruvector_cnn_wasm/package.json
new file mode 100644
index 00000000..f1e17faf
--- /dev/null
+++ b/ui/pose-fusion/pkg/ruvector_cnn_wasm/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "ruvector-cnn-wasm",
+ "type": "module",
+ "description": "WASM bindings for ruvector-cnn - CNN feature extraction for image embeddings",
+ "version": "0.1.0",
+ "license": "MIT OR Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ruvnet/ruvector"
+ },
+ "files": [
+ "ruvector_cnn_wasm_bg.wasm",
+ "ruvector_cnn_wasm.js"
+ ],
+ "main": "ruvector_cnn_wasm.js",
+ "sideEffects": [
+ "./snippets/*"
+ ],
+ "keywords": [
+ "cnn",
+ "embeddings",
+ "wasm",
+ "simd",
+ "machine-learning"
+ ]
+}
\ No newline at end of file
diff --git a/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm.js b/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm.js
new file mode 100644
index 00000000..f899cf7b
--- /dev/null
+++ b/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm.js
@@ -0,0 +1,802 @@
+/**
+ * Configuration for CNN embedder
+ */
+export class EmbedderConfig {
+ __destroy_into_raw() {
+ const ptr = this.__wbg_ptr;
+ this.__wbg_ptr = 0;
+ EmbedderConfigFinalization.unregister(this);
+ return ptr;
+ }
+ free() {
+ const ptr = this.__destroy_into_raw();
+ wasm.__wbg_embedderconfig_free(ptr, 0);
+ }
+ constructor() {
+ const ret = wasm.embedderconfig_new();
+ this.__wbg_ptr = ret >>> 0;
+ EmbedderConfigFinalization.register(this, this.__wbg_ptr, this);
+ return this;
+ }
+ /**
+ * Output embedding dimension
+ * @returns {number}
+ */
+ get embedding_dim() {
+ const ret = wasm.__wbg_get_embedderconfig_embedding_dim(this.__wbg_ptr);
+ return ret >>> 0;
+ }
+ /**
+ * Input image size (square)
+ * @returns {number}
+ */
+ get input_size() {
+ const ret = wasm.__wbg_get_embedderconfig_input_size(this.__wbg_ptr);
+ return ret >>> 0;
+ }
+ /**
+ * Whether to L2 normalize embeddings
+ * @returns {boolean}
+ */
+ get normalize() {
+ const ret = wasm.__wbg_get_embedderconfig_normalize(this.__wbg_ptr);
+ return ret !== 0;
+ }
+ /**
+ * Output embedding dimension
+ * @param {number} arg0
+ */
+ set embedding_dim(arg0) {
+ wasm.__wbg_set_embedderconfig_embedding_dim(this.__wbg_ptr, arg0);
+ }
+ /**
+ * Input image size (square)
+ * @param {number} arg0
+ */
+ set input_size(arg0) {
+ wasm.__wbg_set_embedderconfig_input_size(this.__wbg_ptr, arg0);
+ }
+ /**
+ * Whether to L2 normalize embeddings
+ * @param {boolean} arg0
+ */
+ set normalize(arg0) {
+ wasm.__wbg_set_embedderconfig_normalize(this.__wbg_ptr, arg0);
+ }
+}
+if (Symbol.dispose) EmbedderConfig.prototype[Symbol.dispose] = EmbedderConfig.prototype.free;
+
+/**
+ * Layer operations for building custom networks
+ */
+export class LayerOps {
+ __destroy_into_raw() {
+ const ptr = this.__wbg_ptr;
+ this.__wbg_ptr = 0;
+ LayerOpsFinalization.unregister(this);
+ return ptr;
+ }
+ free() {
+ const ptr = this.__destroy_into_raw();
+ wasm.__wbg_layerops_free(ptr, 0);
+ }
+ /**
+ * Apply batch normalization (returns new array)
+ * @param {Float32Array} input
+ * @param {Float32Array} gamma
+ * @param {Float32Array} beta
+ * @param {Float32Array} mean
+ * @param {Float32Array} _var
+ * @param {number} epsilon
+ * @returns {Float32Array}
+ */
+ static batch_norm(input, gamma, beta, mean, _var, epsilon) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passArrayF32ToWasm0(gamma, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passArrayF32ToWasm0(beta, wasm.__wbindgen_export2);
+ const len2 = WASM_VECTOR_LEN;
+ const ptr3 = passArrayF32ToWasm0(mean, wasm.__wbindgen_export2);
+ const len3 = WASM_VECTOR_LEN;
+ const ptr4 = passArrayF32ToWasm0(_var, wasm.__wbindgen_export2);
+ const len4 = WASM_VECTOR_LEN;
+ wasm.layerops_batch_norm(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, epsilon);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var v6 = getArrayF32FromWasm0(r0, r1).slice();
+ wasm.__wbindgen_export(r0, r1 * 4, 4);
+ return v6;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * Apply global average pooling
+ * Returns one value per channel
+ * @param {Float32Array} input
+ * @param {number} height
+ * @param {number} width
+ * @param {number} channels
+ * @returns {Float32Array}
+ */
+ static global_avg_pool(input, height, width, channels) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.layerops_global_avg_pool(retptr, ptr0, len0, height, width, channels);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var v2 = getArrayF32FromWasm0(r0, r1).slice();
+ wasm.__wbindgen_export(r0, r1 * 4, 4);
+ return v2;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+}
+if (Symbol.dispose) LayerOps.prototype[Symbol.dispose] = LayerOps.prototype.free;
+
+/**
+ * SIMD-optimized operations
+ */
+export class SimdOps {
+ __destroy_into_raw() {
+ const ptr = this.__wbg_ptr;
+ this.__wbg_ptr = 0;
+ SimdOpsFinalization.unregister(this);
+ return ptr;
+ }
+ free() {
+ const ptr = this.__destroy_into_raw();
+ wasm.__wbg_simdops_free(ptr, 0);
+ }
+ /**
+ * Dot product of two vectors
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @returns {number}
+ */
+ static dot_product(a, b) {
+ const ptr0 = passArrayF32ToWasm0(a, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passArrayF32ToWasm0(b, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ const ret = wasm.simdops_dot_product(ptr0, len0, ptr1, len1);
+ return ret;
+ }
+ /**
+ * L2 normalize a vector (returns new array)
+ * @param {Float32Array} data
+ * @returns {Float32Array}
+ */
+ static l2_normalize(data) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(data, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.simdops_l2_normalize(retptr, ptr0, len0);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var v2 = getArrayF32FromWasm0(r0, r1).slice();
+ wasm.__wbindgen_export(r0, r1 * 4, 4);
+ return v2;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * ReLU activation (returns new array)
+ * @param {Float32Array} data
+ * @returns {Float32Array}
+ */
+ static relu(data) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(data, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.simdops_relu(retptr, ptr0, len0);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var v2 = getArrayF32FromWasm0(r0, r1).slice();
+ wasm.__wbindgen_export(r0, r1 * 4, 4);
+ return v2;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * ReLU6 activation (returns new array)
+ * @param {Float32Array} data
+ * @returns {Float32Array}
+ */
+ static relu6(data) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(data, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.simdops_relu6(retptr, ptr0, len0);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var v2 = getArrayF32FromWasm0(r0, r1).slice();
+ wasm.__wbindgen_export(r0, r1 * 4, 4);
+ return v2;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+}
+if (Symbol.dispose) SimdOps.prototype[Symbol.dispose] = SimdOps.prototype.free;
+
+/**
+ * WASM CNN Embedder for image feature extraction
+ */
+export class WasmCnnEmbedder {
+ __destroy_into_raw() {
+ const ptr = this.__wbg_ptr;
+ this.__wbg_ptr = 0;
+ WasmCnnEmbedderFinalization.unregister(this);
+ return ptr;
+ }
+ free() {
+ const ptr = this.__destroy_into_raw();
+ wasm.__wbg_wasmcnnembedder_free(ptr, 0);
+ }
+ /**
+ * Compute cosine similarity between two embeddings
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @returns {number}
+ */
+ cosine_similarity(a, b) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(a, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passArrayF32ToWasm0(b, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.wasmcnnembedder_cosine_similarity(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1);
+ var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ if (r2) {
+ throw takeObject(r1);
+ }
+ return r0;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * Get the embedding dimension
+ * @returns {number}
+ */
+ get embedding_dim() {
+ const ret = wasm.wasmcnnembedder_embedding_dim(this.__wbg_ptr);
+ return ret >>> 0;
+ }
+ /**
+ * Extract embedding from image data (RGB format, row-major)
+ * @param {Uint8Array} image_data
+ * @param {number} width
+ * @param {number} height
+ * @returns {Float32Array}
+ */
+ extract(image_data, width, height) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArray8ToWasm0(image_data, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.wasmcnnembedder_extract(retptr, this.__wbg_ptr, ptr0, len0, width, height);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
+ if (r3) {
+ throw takeObject(r2);
+ }
+ var v2 = getArrayF32FromWasm0(r0, r1).slice();
+ wasm.__wbindgen_export(r0, r1 * 4, 4);
+ return v2;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * Create a new CNN embedder
+ * @param {EmbedderConfig | null} [config]
+ */
+ constructor(config) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ let ptr0 = 0;
+ if (!isLikeNone(config)) {
+ _assertClass(config, EmbedderConfig);
+ ptr0 = config.__destroy_into_raw();
+ }
+ wasm.wasmcnnembedder_new(retptr, ptr0);
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ if (r2) {
+ throw takeObject(r1);
+ }
+ this.__wbg_ptr = r0 >>> 0;
+ WasmCnnEmbedderFinalization.register(this, this.__wbg_ptr, this);
+ return this;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+}
+if (Symbol.dispose) WasmCnnEmbedder.prototype[Symbol.dispose] = WasmCnnEmbedder.prototype.free;
+
+/**
+ * InfoNCE loss for contrastive learning (SimCLR style)
+ */
+export class WasmInfoNCELoss {
+ __destroy_into_raw() {
+ const ptr = this.__wbg_ptr;
+ this.__wbg_ptr = 0;
+ WasmInfoNCELossFinalization.unregister(this);
+ return ptr;
+ }
+ free() {
+ const ptr = this.__destroy_into_raw();
+ wasm.__wbg_wasminfonceloss_free(ptr, 0);
+ }
+ /**
+ * Compute loss for a batch of embedding pairs
+ * embeddings: [2N, D] flattened where (i, i+N) are positive pairs
+ * @param {Float32Array} embeddings
+ * @param {number} batch_size
+ * @param {number} dim
+ * @returns {number}
+ */
+ forward(embeddings, batch_size, dim) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(embeddings, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.wasminfonceloss_forward(retptr, this.__wbg_ptr, ptr0, len0, batch_size, dim);
+ var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ if (r2) {
+ throw takeObject(r1);
+ }
+ return r0;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * Create new InfoNCE loss with temperature parameter
+ * @param {number} temperature
+ */
+ constructor(temperature) {
+ const ret = wasm.wasminfonceloss_new(temperature);
+ this.__wbg_ptr = ret >>> 0;
+ WasmInfoNCELossFinalization.register(this, this.__wbg_ptr, this);
+ return this;
+ }
+ /**
+ * Get the temperature parameter
+ * @returns {number}
+ */
+ get temperature() {
+ const ret = wasm.wasminfonceloss_temperature(this.__wbg_ptr);
+ return ret;
+ }
+}
+if (Symbol.dispose) WasmInfoNCELoss.prototype[Symbol.dispose] = WasmInfoNCELoss.prototype.free;
+
+/**
+ * Triplet loss for metric learning
+ */
+export class WasmTripletLoss {
+ __destroy_into_raw() {
+ const ptr = this.__wbg_ptr;
+ this.__wbg_ptr = 0;
+ WasmTripletLossFinalization.unregister(this);
+ return ptr;
+ }
+ free() {
+ const ptr = this.__destroy_into_raw();
+ wasm.__wbg_wasmtripletloss_free(ptr, 0);
+ }
+ /**
+ * Compute loss for a batch of triplets
+ * @param {Float32Array} anchors
+ * @param {Float32Array} positives
+ * @param {Float32Array} negatives
+ * @param {number} dim
+ * @returns {number}
+ */
+ forward(anchors, positives, negatives, dim) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(anchors, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passArrayF32ToWasm0(positives, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passArrayF32ToWasm0(negatives, wasm.__wbindgen_export2);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.wasmtripletloss_forward(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, dim);
+ var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ if (r2) {
+ throw takeObject(r1);
+ }
+ return r0;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * Compute loss for a single triplet
+ * @param {Float32Array} anchor
+ * @param {Float32Array} positive
+ * @param {Float32Array} negative
+ * @returns {number}
+ */
+ forward_single(anchor, positive, negative) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArrayF32ToWasm0(anchor, wasm.__wbindgen_export2);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passArrayF32ToWasm0(positive, wasm.__wbindgen_export2);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passArrayF32ToWasm0(negative, wasm.__wbindgen_export2);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.wasmtripletloss_forward_single(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getDataViewMemory0().getFloat32(retptr + 4 * 0, true);
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
+ if (r2) {
+ throw takeObject(r1);
+ }
+ return r0;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+ }
+ /**
+ * Get the margin parameter
+ * @returns {number}
+ */
+ get margin() {
+ const ret = wasm.wasmtripletloss_margin(this.__wbg_ptr);
+ return ret;
+ }
+ /**
+ * Create new triplet loss with margin
+ * @param {number} margin
+ */
+ constructor(margin) {
+ const ret = wasm.wasmtripletloss_new(margin);
+ this.__wbg_ptr = ret >>> 0;
+ WasmTripletLossFinalization.register(this, this.__wbg_ptr, this);
+ return this;
+ }
+}
+if (Symbol.dispose) WasmTripletLoss.prototype[Symbol.dispose] = WasmTripletLoss.prototype.free;
+
+/**
+ * Initialize panic hook for better error messages
+ */
+export function init() {
+ wasm.init();
+}
+
+function __wbg_get_imports() {
+ const import0 = {
+ __proto__: null,
+ __wbg___wbindgen_throw_39bc967c0e5a9b58: function(arg0, arg1) {
+ throw new Error(getStringFromWasm0(arg0, arg1));
+ },
+ __wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) {
+ let deferred0_0;
+ let deferred0_1;
+ try {
+ deferred0_0 = arg0;
+ deferred0_1 = arg1;
+ console.error(getStringFromWasm0(arg0, arg1));
+ } finally {
+ wasm.__wbindgen_export(deferred0_0, deferred0_1, 1);
+ }
+ },
+ __wbg_new_227d7c05414eb861: function() {
+ const ret = new Error();
+ return addHeapObject(ret);
+ },
+ __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
+ const ret = getObject(arg1).stack;
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export2, wasm.__wbindgen_export3);
+ const len1 = WASM_VECTOR_LEN;
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
+ },
+ __wbindgen_cast_0000000000000001: function(arg0, arg1) {
+ // Cast intrinsic for `Ref(String) -> Externref`.
+ const ret = getStringFromWasm0(arg0, arg1);
+ return addHeapObject(ret);
+ },
+ __wbindgen_object_drop_ref: function(arg0) {
+ takeObject(arg0);
+ },
+ };
+ return {
+ __proto__: null,
+ "./ruvector_cnn_wasm_bg.js": import0,
+ };
+}
+
+const EmbedderConfigFinalization = (typeof FinalizationRegistry === 'undefined')
+ ? { register: () => {}, unregister: () => {} }
+ : new FinalizationRegistry(ptr => wasm.__wbg_embedderconfig_free(ptr >>> 0, 1));
+const LayerOpsFinalization = (typeof FinalizationRegistry === 'undefined')
+ ? { register: () => {}, unregister: () => {} }
+ : new FinalizationRegistry(ptr => wasm.__wbg_layerops_free(ptr >>> 0, 1));
+const SimdOpsFinalization = (typeof FinalizationRegistry === 'undefined')
+ ? { register: () => {}, unregister: () => {} }
+ : new FinalizationRegistry(ptr => wasm.__wbg_simdops_free(ptr >>> 0, 1));
+const WasmCnnEmbedderFinalization = (typeof FinalizationRegistry === 'undefined')
+ ? { register: () => {}, unregister: () => {} }
+ : new FinalizationRegistry(ptr => wasm.__wbg_wasmcnnembedder_free(ptr >>> 0, 1));
+const WasmInfoNCELossFinalization = (typeof FinalizationRegistry === 'undefined')
+ ? { register: () => {}, unregister: () => {} }
+ : new FinalizationRegistry(ptr => wasm.__wbg_wasminfonceloss_free(ptr >>> 0, 1));
+const WasmTripletLossFinalization = (typeof FinalizationRegistry === 'undefined')
+ ? { register: () => {}, unregister: () => {} }
+ : new FinalizationRegistry(ptr => wasm.__wbg_wasmtripletloss_free(ptr >>> 0, 1));
+
+function addHeapObject(obj) {
+ if (heap_next === heap.length) heap.push(heap.length + 1);
+ const idx = heap_next;
+ heap_next = heap[idx];
+
+ heap[idx] = obj;
+ return idx;
+}
+
+function _assertClass(instance, klass) {
+ if (!(instance instanceof klass)) {
+ throw new Error(`expected instance of ${klass.name}`);
+ }
+}
+
+function dropObject(idx) {
+ if (idx < 1028) return;
+ heap[idx] = heap_next;
+ heap_next = idx;
+}
+
+function getArrayF32FromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
+}
+
+let cachedDataViewMemory0 = null;
+function getDataViewMemory0() {
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
+ }
+ return cachedDataViewMemory0;
+}
+
+let cachedFloat32ArrayMemory0 = null;
+function getFloat32ArrayMemory0() {
+ if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {
+ cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);
+ }
+ return cachedFloat32ArrayMemory0;
+}
+
+function getStringFromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return decodeText(ptr, len);
+}
+
+let cachedUint8ArrayMemory0 = null;
+function getUint8ArrayMemory0() {
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
+ }
+ return cachedUint8ArrayMemory0;
+}
+
+function getObject(idx) { return heap[idx]; }
+
+let heap = new Array(1024).fill(undefined);
+heap.push(undefined, null, true, false);
+
+let heap_next = heap.length;
+
+function isLikeNone(x) {
+ return x === undefined || x === null;
+}
+
+function passArray8ToWasm0(arg, malloc) {
+ const ptr = malloc(arg.length * 1, 1) >>> 0;
+ getUint8ArrayMemory0().set(arg, ptr / 1);
+ WASM_VECTOR_LEN = arg.length;
+ return ptr;
+}
+
+function passArrayF32ToWasm0(arg, malloc) {
+ const ptr = malloc(arg.length * 4, 4) >>> 0;
+ getFloat32ArrayMemory0().set(arg, ptr / 4);
+ WASM_VECTOR_LEN = arg.length;
+ return ptr;
+}
+
+function passStringToWasm0(arg, malloc, realloc) {
+ if (realloc === undefined) {
+ const buf = cachedTextEncoder.encode(arg);
+ const ptr = malloc(buf.length, 1) >>> 0;
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
+ WASM_VECTOR_LEN = buf.length;
+ return ptr;
+ }
+
+ let len = arg.length;
+ let ptr = malloc(len, 1) >>> 0;
+
+ const mem = getUint8ArrayMemory0();
+
+ let offset = 0;
+
+ for (; offset < len; offset++) {
+ const code = arg.charCodeAt(offset);
+ if (code > 0x7F) break;
+ mem[ptr + offset] = code;
+ }
+ if (offset !== len) {
+ if (offset !== 0) {
+ arg = arg.slice(offset);
+ }
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
+ const ret = cachedTextEncoder.encodeInto(arg, view);
+
+ offset += ret.written;
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
+ }
+
+ WASM_VECTOR_LEN = offset;
+ return ptr;
+}
+
+function takeObject(idx) {
+ const ret = getObject(idx);
+ dropObject(idx);
+ return ret;
+}
+
+let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
+cachedTextDecoder.decode();
+const MAX_SAFARI_DECODE_BYTES = 2146435072;
+let numBytesDecoded = 0;
+function decodeText(ptr, len) {
+ numBytesDecoded += len;
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
+ cachedTextDecoder.decode();
+ numBytesDecoded = len;
+ }
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
+}
+
+const cachedTextEncoder = new TextEncoder();
+
+if (!('encodeInto' in cachedTextEncoder)) {
+ cachedTextEncoder.encodeInto = function (arg, view) {
+ const buf = cachedTextEncoder.encode(arg);
+ view.set(buf);
+ return {
+ read: arg.length,
+ written: buf.length
+ };
+ };
+}
+
+let WASM_VECTOR_LEN = 0;
+
+let wasmModule, wasm;
+function __wbg_finalize_init(instance, module) {
+ wasm = instance.exports;
+ wasmModule = module;
+ cachedDataViewMemory0 = null;
+ cachedFloat32ArrayMemory0 = null;
+ cachedUint8ArrayMemory0 = null;
+ wasm.__wbindgen_start();
+ return wasm;
+}
+
+async function __wbg_load(module, imports) {
+ if (typeof Response === 'function' && module instanceof Response) {
+ if (typeof WebAssembly.instantiateStreaming === 'function') {
+ try {
+ return await WebAssembly.instantiateStreaming(module, imports);
+ } catch (e) {
+ const validResponse = module.ok && expectedResponseType(module.type);
+
+ if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
+ console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
+
+ } else { throw e; }
+ }
+ }
+
+ const bytes = await module.arrayBuffer();
+ return await WebAssembly.instantiate(bytes, imports);
+ } else {
+ const instance = await WebAssembly.instantiate(module, imports);
+
+ if (instance instanceof WebAssembly.Instance) {
+ return { instance, module };
+ } else {
+ return instance;
+ }
+ }
+
+ function expectedResponseType(type) {
+ switch (type) {
+ case 'basic': case 'cors': case 'default': return true;
+ }
+ return false;
+ }
+}
+
+function initSync(module) {
+ if (wasm !== undefined) return wasm;
+
+
+ if (module !== undefined) {
+ if (Object.getPrototypeOf(module) === Object.prototype) {
+ ({module} = module)
+ } else {
+ console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
+ }
+ }
+
+ const imports = __wbg_get_imports();
+ if (!(module instanceof WebAssembly.Module)) {
+ module = new WebAssembly.Module(module);
+ }
+ const instance = new WebAssembly.Instance(module, imports);
+ return __wbg_finalize_init(instance, module);
+}
+
+async function __wbg_init(module_or_path) {
+ if (wasm !== undefined) return wasm;
+
+
+ if (module_or_path !== undefined) {
+ if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
+ ({module_or_path} = module_or_path)
+ } else {
+ console.warn('using deprecated parameters for the initialization function; pass a single object instead')
+ }
+ }
+
+ if (module_or_path === undefined) {
+ module_or_path = new URL('ruvector_cnn_wasm_bg.wasm', import.meta.url);
+ }
+ const imports = __wbg_get_imports();
+
+ if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
+ module_or_path = fetch(module_or_path);
+ }
+
+ const { instance, module } = await __wbg_load(await module_or_path, imports);
+
+ return __wbg_finalize_init(instance, module);
+}
+
+export { initSync, __wbg_init as default };
diff --git a/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm_bg.wasm b/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm_bg.wasm
new file mode 100644
index 00000000..a1a54ee2
Binary files /dev/null and b/ui/pose-fusion/pkg/ruvector_cnn_wasm/ruvector_cnn_wasm_bg.wasm differ