Skip to content

Sine Wave

The simplest example: stream sin(t) and cos(t) over WebSocket and display them in real-time charts with derived amplitude.

examples/sine-wave/server.ts
import { WebSocketServer } from "ws";
const PORT = 9002;
const wss = new WebSocketServer({ port: PORT });
console.log(`Sine wave WebSocket server listening on ws://localhost:${PORT}`);
wss.on("connection", (ws) => {
console.log("Client connected");
// Send info message
ws.send(
JSON.stringify({
type: "info",
description: "Sine wave generator",
dt: 0.1,
stream_interval: 0.1,
}),
);
const startTime = Date.now();
const interval = setInterval(() => {
const t = (Date.now() - startTime) / 1000;
ws.send(
JSON.stringify({
type: "state",
t,
value: Math.sin(t),
derivative: Math.cos(t),
}),
);
}, 100);
ws.on("close", () => {
console.log("Client disconnected");
clearInterval(interval);
});
ws.on("error", (err) => {
console.error("WebSocket error:", err);
clearInterval(interval);
});
});
examples/sine-wave/App.tsx
import { useEffect, useMemo, useRef, useState } from "react";
import {
IngestBuffer,
type TableSchema,
type TimeRange,
TimeSeriesChart,
useTimeSeriesStoreWorker,
} from "../../src/index.js";
interface SinePoint {
t: number;
value: number;
derivative: number;
}
const sineSchema: TableSchema<SinePoint> = {
tableName: "sine_data",
columns: [
{ name: "t", type: "DOUBLE" },
{ name: "value", type: "DOUBLE" },
{ name: "derivative", type: "DOUBLE" },
],
derived: [
{ name: "sine", sql: "value", unit: "" },
{ name: "cosine", sql: "derivative", unit: "" },
{
name: "amplitude",
sql: "sqrt(value*value + derivative*derivative)",
unit: "",
},
],
toRow: (p) => [p.t, p.value, p.derivative],
};
export function App() {
const bufferRef = useRef(new IngestBuffer<SinePoint>());
const [timeRange, setTimeRange] = useState<TimeRange>(null);
// Data source: WebSocket or mock (when ?mock is in URL)
useEffect(() => {
const isMock = new URLSearchParams(window.location.search).has("mock");
if (isMock) {
const startTime = Date.now();
const interval = setInterval(() => {
const t = (Date.now() - startTime) / 1000;
bufferRef.current.push({ t, value: Math.sin(t), derivative: Math.cos(t) });
}, 100);
return () => clearInterval(interval);
}
const ws = new WebSocket("ws://localhost:9002");
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "state") {
bufferRef.current.push({
t: msg.t,
value: msg.value,
derivative: msg.derivative,
});
}
};
return () => ws.close();
}, []);
const { data } = useTimeSeriesStoreWorker({
schema: sineSchema,
ingestBufferRef: bufferRef,
timeRange,
drainInterval: 100,
});
// Slice data for individual charts
const sineData = useMemo(
() => (data ? ([data.t, data.sine] as [Float64Array, Float64Array]) : null),
[data],
);
const cosineData = useMemo(
() => (data ? ([data.t, data.cosine] as [Float64Array, Float64Array]) : null),
[data],
);
const amplitudeData = useMemo(
() => (data ? ([data.t, data.amplitude] as [Float64Array, Float64Array]) : null),
[data],
);
return (
<div
style={{
padding: "1rem",
background: "#1a1a2e",
color: "#eee",
minHeight: "100vh",
}}
>
<h1>uneri Example: Sine Wave (Worker)</h1>
<div style={{ marginBottom: "1rem" }}>
<button type="button" onClick={() => setTimeRange(null)}>
All
</button>
<button type="button" onClick={() => setTimeRange(30)}>
30s
</button>
<button type="button" onClick={() => setTimeRange(60)}>
60s
</button>
</div>
<TimeSeriesChart title="sin(t)" yLabel="" data={sineData} color="#4af" />
<TimeSeriesChart title="cos(t)" yLabel="" data={cosineData} color="#f84" />
<TimeSeriesChart title="amplitude" yLabel="" data={amplitudeData} color="#8f4" />
</div>
);
}