@chartts/websocket

WebSocket Streaming

Push real-time data to charts over WebSocket, Server-Sent Events, or HTTP polling. Built-in auto-reconnect and configurable message parsing.

Installation

npm install @chartts/websocket

This package provides three stream sources that push live data into a Chart.ts streaming chart: WebSocket, Server-Sent Events (SSE), and HTTP polling. All three implement the same StreamSource interface and support auto-reconnect.


createWebSocketStream()

Connects to a WebSocket server and pushes incoming messages to a StreamingInstance.

import { createWebSocketStream } from '@chartts/websocket'
import { createChart, createStreamingChart, Line } from '@chartts/core'
 
const chart = createChart('#chart', Line, { labels: [], series: [] })
 
const stream = createStreamingChart(chart, {
  windowSize: 200,
  seriesCount: 3,
  seriesNames: ['CPU', 'Memory', 'Disk'],
})
 
const ws = createWebSocketStream('wss://metrics.example.com/live')
ws.connect(stream)
 
// Later: disconnect cleanly
ws.disconnect()

Parameters:

ParameterTypeDescription
urlstringWebSocket URL (ws:// or wss://)
optionsStreamSourceOptionsOptional configuration (see below)

createSSEStream()

Connects to a Server-Sent Events endpoint. SSE is a good choice when you only need server-to-client data flow over HTTP.

import { createSSEStream } from '@chartts/websocket'
import { createChart, createStreamingChart, Line } from '@chartts/core'
 
const chart = createChart('#chart', Line, { labels: [], series: [] })
 
const stream = createStreamingChart(chart, {
  windowSize: 100,
  seriesCount: 1,
  seriesNames: ['Temperature'],
})
 
const sse = createSSEStream('https://api.example.com/temperature/stream', {
  eventName: 'reading',
})
sse.connect(stream)

Parameters:

ParameterTypeDescription
urlstringSSE endpoint URL
optionsStreamSourceOptions & { eventName?: string }Optional config. eventName listens for a specific SSE event type instead of the default message event.

createPollingStream()

Fetches data from an HTTP endpoint at a fixed interval. Use this when your server does not support WebSocket or SSE.

import { createPollingStream } from '@chartts/websocket'
import { createChart, createStreamingChart, Line } from '@chartts/core'
 
const chart = createChart('#chart', Line, { labels: [], series: [] })
 
const stream = createStreamingChart(chart, {
  windowSize: 60,
  seriesCount: 2,
  seriesNames: ['Requests/s', 'Errors/s'],
})
 
const poller = createPollingStream(
  'https://api.example.com/metrics/current',
  5000, // poll every 5 seconds
)
poller.connect(stream)

You can also pass a function instead of a URL for custom fetch logic:

const poller = createPollingStream(
  () => fetch('https://api.example.com/metrics', {
    headers: { Authorization: 'Bearer token123' },
  }),
  3000,
)

Parameters:

ParameterTypeDescription
urlstring | (() => Promise<Response>)URL to fetch, or a function returning a fetch Response
intervalnumberPolling interval in milliseconds
optionsStreamSourceOptionsOptional configuration

StreamSourceOptions

All three stream sources accept these options:

OptionTypeDefaultDescription
parse(message: unknown) => { values: number[]; label?: string | number | Date } | nullBuilt-in parserCustom function to extract values from incoming messages
reconnectbooleantrueAuto-reconnect on disconnect (WebSocket and SSE only)
reconnectIntervalnumber3000Milliseconds between reconnect attempts
maxReconnectsnumber10Maximum reconnect attempts before giving up

Message Format

The built-in parser accepts several message formats. No custom parse function is needed if your server sends one of these:

Single value:

42

Object with value:

{ "value": 42, "label": "10:30:15" }

Object with values array (multi-series):

{ "values": [72.5, 48.3, 91.0], "label": "10:30:15" }

Plain array of numbers:

[72.5, 48.3, 91.0]

String messages are auto-parsed as JSON. If JSON parsing fails, the parser attempts to read the string as a plain number.


Custom Parser

For non-standard message formats, provide a parse function:

const ws = createWebSocketStream('wss://exchange.example.com/trades', {
  parse: (msg) => {
    const data = JSON.parse(msg as string)
    return {
      values: [data.price, data.volume / 1000],
      label: data.timestamp,
    }
  },
})

Return null from the parser to skip a message (e.g., heartbeats or control frames).


Auto-Reconnect

WebSocket and SSE sources reconnect automatically when the connection drops. The reconnect counter resets on each successful connection.

const ws = createWebSocketStream('wss://metrics.example.com/live', {
  reconnect: true,
  reconnectInterval: 5000,  // wait 5s between attempts
  maxReconnects: 20,        // give up after 20 failures
})

To disable auto-reconnect:

const ws = createWebSocketStream('wss://example.com/data', {
  reconnect: false,
})

StreamSource Interface

All three factories return a StreamSource object:

interface StreamSource {
  connect(stream: StreamingInstance): void
  disconnect(): void
  readonly connected: boolean
}
MethodDescription
connect(stream)Attach to a StreamingInstance and start pushing data
disconnect()Close the connection and stop pushing data
connectedtrue when the source is actively connected

Full Example: Live Dashboard

import { createChart, createStreamingChart, Line } from '@chartts/core'
import { createWebSocketStream } from '@chartts/websocket'
 
// Create chart
const chart = createChart('#dashboard', Line, {
  labels: [],
  series: [],
}, {
  theme: 'dark',
  width: 800,
  height: 400,
})
 
// Set up streaming with a 120-point rolling window
const stream = createStreamingChart(chart, {
  windowSize: 120,
  seriesCount: 3,
  seriesNames: ['CPU %', 'Memory %', 'Network MB/s'],
  seriesColors: ['#6366f1', '#f59e0b', '#10b981'],
})
 
// Connect WebSocket source
const source = createWebSocketStream('wss://monitoring.internal/metrics', {
  reconnect: true,
  reconnectInterval: 2000,
  maxReconnects: 50,
  parse: (msg) => {
    const d = JSON.parse(msg as string)
    return {
      values: [d.cpu, d.memory, d.network],
      label: new Date().toLocaleTimeString(),
    }
  },
})
 
source.connect(stream)
 
// Pause and resume streaming
document.getElementById('pause')?.addEventListener('click', () => {
  stream.pause()
})
 
document.getElementById('resume')?.addEventListener('click', () => {
  stream.resume()
})
 
// Clean up on page unload
window.addEventListener('beforeunload', () => {
  source.disconnect()
  stream.destroy()
})

React Integration

import { useEffect, useRef } from 'react'
import { createChart, createStreamingChart, Line } from '@chartts/core'
import { createWebSocketStream } from '@chartts/websocket'
 
function LiveChart({ url }: { url: string }) {
  const containerRef = useRef<HTMLDivElement>(null)
 
  useEffect(() => {
    if (!containerRef.current) return
 
    const chart = createChart(containerRef.current, Line, {
      labels: [],
      series: [],
    })
 
    const stream = createStreamingChart(chart, {
      windowSize: 100,
      seriesCount: 1,
      seriesNames: ['Value'],
    })
 
    const source = createWebSocketStream(url)
    source.connect(stream)
 
    return () => {
      source.disconnect()
      stream.destroy()
    }
  }, [url])
 
  return <div ref={containerRef} className="h-64 w-full" />
}