Examples
JavaScript
Node and browser examples for ingest and subscribe.
Node: ingest synthetic PPG
Install:
npm install ws// ingest-synth.mjs
import WebSocket from "ws";
const KEY = process.env.RAEH_CLIENT_KEY;
const DM_ID = process.env.RAEH_DEVICE_MODEL_ID;
const url = new URL("wss://api.raeh.io/stream/ingest");
url.searchParams.set("api_key", KEY);
url.searchParams.set("device_id", `demo-${Math.random().toString(36).slice(2, 10)}`);
url.searchParams.set("device_model_id", DM_ID);
const ws = new WebSocket(url.toString());
function encodeFrame(slotId, t0Ms, samples) {
const buf = new ArrayBuffer(12 + samples.length * 2);
const view = new DataView(buf);
view.setUint8(0, slotId);
view.setBigInt64(1, BigInt(t0Ms), false); // big-endian
view.setUint16(9, samples.length, false);
view.setUint8(11, 0);
for (let i = 0; i < samples.length; i++) view.setInt16(12 + i * 2, samples[i], true); // samples are LE
return Buffer.from(buf);
}
ws.once("message", async (buf) => {
const { session_id, slots } = JSON.parse(buf.toString("utf8"));
const slotId = slots[0].slot_id;
console.log(`session: ${session_id}`);
for (let i = 0; i < 60; i++) {
const t0 = Date.now();
const samples = Array.from({ length: 100 }, (_, k) =>
Math.round(2048 + 500 * Math.sin(2 * Math.PI * 1.2 * k / 100))
);
ws.send(encodeFrame(slotId, t0, samples));
process.stdout.write(` frame ${i + 1}/60\r`);
await new Promise((r) => setTimeout(r, 1000));
}
console.log();
ws.close();
});RAEH_CLIENT_KEY=raeh_... RAEH_DEVICE_MODEL_ID=<uuid> node ingest-synth.mjsNode: subscribe
// subscribe.mjs
import WebSocket from "ws";
const KEY = process.env.RAEH_CLIENT_KEY;
const ws = new WebSocket(`wss://api.raeh.io/stream/subscribe?api_key=${encodeURIComponent(KEY)}`);
let acked = false;
ws.on("message", (buf) => {
const msg = JSON.parse(buf.toString("utf8"));
if (!acked) { acked = true; return; } // drop subscribe ack
if (msg.type) console.log(`${msg.type.padEnd(5)} ${String(msg.value).padStart(6)} ${msg.unit ?? ""}`);
});RAEH_CLIENT_KEY=raeh_... node subscribe.mjsBrowser: subscribe-only UI
Copy-paste into any HTML file and open in a browser. Great for prototyping a live-dashboard view.
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Raeh Live</title></head>
<body>
<h1>Raeh Live</h1>
<input id="key" placeholder="raeh_..." style="width:320px">
<button id="connect">Connect</button>
<div>HR: <span id="hr">–</span> bpm · SpO₂: <span id="spo2">–</span> % · RR: <span id="rr">–</span> brpm</div>
<pre id="log" style="height:200px;overflow:auto;background:#111;color:#0f0;padding:8px"></pre>
<script>
const $ = (id) => document.getElementById(id);
const log = (m) => $("log").textContent += m + "\n";
$("connect").onclick = () => {
const key = $("key").value.trim();
if (!key) return;
const ws = new WebSocket(`wss://api.raeh.io/stream/subscribe?api_key=${encodeURIComponent(key)}`);
ws.onopen = () => log("connected");
ws.onclose = (e) => log(`closed ${e.code}`);
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (!msg.type) return;
const el = $(msg.type);
if (el) el.textContent = typeof msg.value === "number" ? msg.value.toFixed(msg.type === "hr" ? 0 : 1) : msg.value;
log(`${msg.type}: ${msg.value} ${msg.unit ?? ""}`);
};
};
</script>
</body>
</html>Browser: ingest (rarely needed)
Browsers can open /stream/ingest too (same URL.searchParams pattern), but for production wearables the ingest side runs in firmware or a native app, not the browser. Use browser ingest only for prototyping from a USB-connected sensor via Web Bluetooth or Web Serial.