WASM Bindings
The dashboard module provides WebAssembly bindings for browser-based training dashboards.
Feature Flag
Enable WASM support in your Cargo.toml:
[dependencies]
entrenar = { version = "0.2", features = ["wasm"] }
IndexedDbStorage
Browser-compatible storage backend that implements ExperimentStorage:
#![allow(unused)] fn main() { pub struct IndexedDbStorage { // In-memory implementation mimicking IndexedDB behavior } impl ExperimentStorage for IndexedDbStorage { fn create_experiment(&mut self, name: &str, config: Option<Value>) -> Result<String>; fn create_run(&mut self, experiment_id: &str) -> Result<String>; fn start_run(&mut self, run_id: &str) -> Result<()>; fn complete_run(&mut self, run_id: &str, status: RunStatus) -> Result<()>; fn log_metric(&mut self, run_id: &str, key: &str, step: u64, value: f64) -> Result<()>; fn log_artifact(&mut self, run_id: &str, key: &str, data: &[u8]) -> Result<String>; fn get_metrics(&self, run_id: &str, key: &str) -> Result<Vec<MetricPoint>>; fn get_run_status(&self, run_id: &str) -> Result<RunStatus>; fn set_span_id(&mut self, run_id: &str, span_id: &str) -> Result<()>; fn get_span_id(&self, run_id: &str) -> Result<Option<String>>; } }
Additional Methods
#![allow(unused)] fn main() { impl IndexedDbStorage { /// List all experiments pub fn list_experiments(&self) -> Vec<String>; /// List all runs for an experiment pub fn list_runs(&self, experiment_id: &str) -> Vec<String>; /// List all metric keys for a run pub fn list_metric_keys(&self, run_id: &str) -> Vec<String>; } }
WasmRun
JavaScript-friendly training run wrapper with wasm_bindgen:
#![allow(unused)] fn main() { #[wasm_bindgen] pub struct WasmRun { run_id: String, experiment_id: String, storage: Arc<Mutex<IndexedDbStorage>>, step_counters: HashMap<String, u64>, finished: bool, } }
JavaScript API
// Create a new run
const run = await WasmRun.new('my-experiment');
// Log metrics (auto-incrementing step)
run.log_metric('loss', 0.5);
run.log_metric('loss', 0.4);
run.log_metric('accuracy', 0.85);
// Log at specific step
run.log_metric_at('learning_rate', 100, 0.001);
// Get all metrics as JSON
const metrics = JSON.parse(run.get_metrics_json());
// {
// "loss": [
// {"step": 0, "value": 0.5, "timestamp": "..."},
// {"step": 1, "value": 0.4, "timestamp": "..."}
// ],
// "accuracy": [
// {"step": 0, "value": 0.85, "timestamp": "..."}
// ]
// }
// Subscribe to metric updates
run.subscribe_metrics((key, value) => {
console.log(`${key}: ${value}`);
});
// Get run info
console.log(run.run_id()); // "run-0"
console.log(run.experiment_id()); // "exp-0"
console.log(run.current_step('loss')); // 2
console.log(run.is_finished()); // false
// Finish the run
run.finish(); // Success status
// or
run.fail(); // Failed status
Building for WASM
Prerequisites
# Install wasm-pack
cargo install wasm-pack
# Install wasm32 target
rustup target add wasm32-unknown-unknown
Build
# Build for bundler (webpack, etc.)
wasm-pack build --target bundler --features wasm
# Build for web (no bundler)
wasm-pack build --target web --features wasm
# Build for Node.js
wasm-pack build --target nodejs --features wasm
Output
pkg/
├── entrenar.js # JavaScript bindings
├── entrenar.d.ts # TypeScript definitions
├── entrenar_bg.wasm # WebAssembly module
├── entrenar_bg.wasm.d.ts # WASM TypeScript defs
└── package.json # npm package
Usage in Web Applications
With a Bundler (Webpack/Vite)
// Install: npm install ./pkg
import init, { WasmRun } from 'entrenar';
async function main() {
await init();
const run = WasmRun.new('training-session');
// Training loop
for (let epoch = 0; epoch < 100; epoch++) {
const loss = trainEpoch();
run.log_metric('loss', loss);
updateChart(JSON.parse(run.get_metrics_json()));
}
run.finish();
}
main();
Without a Bundler
<script type="module">
import init, { WasmRun } from './pkg/entrenar.js';
async function main() {
await init();
const run = WasmRun.new('browser-training');
run.log_metric('loss', 0.5);
document.getElementById('metrics').textContent =
run.get_metrics_json();
}
main();
</script>
React Example
import { useEffect, useState, useRef } from 'react';
import init, { WasmRun } from 'entrenar';
function TrainingDashboard() {
const [metrics, setMetrics] = useState({});
const runRef = useRef(null);
useEffect(() => {
async function setup() {
await init();
runRef.current = WasmRun.new('react-training');
}
setup();
return () => {
if (runRef.current && !runRef.current.is_finished()) {
runRef.current.finish();
}
};
}, []);
const logMetric = (key, value) => {
if (runRef.current) {
runRef.current.log_metric(key, value);
setMetrics(JSON.parse(runRef.current.get_metrics_json()));
}
};
return (
<div>
<button onClick={() => logMetric('loss', Math.random())}>
Log Random Loss
</button>
<pre>{JSON.stringify(metrics, null, 2)}</pre>
</div>
);
}
Error Handling
WASM methods return Result<T, JsValue> which throws on error:
try {
const run = WasmRun.new('my-experiment');
run.log_metric('loss', 0.5);
} catch (error) {
console.error('WASM error:', error);
}
Limitations
- No real IndexedDB - Current implementation uses in-memory storage
- Single-threaded - WebAssembly runs on the main thread
- No persistence - Data is lost on page refresh
- Subscribe placeholder -
subscribe_metricsis a placeholder API