Skip to content

Websocket Dashboard

DodaTech 6 min read

title: "WebSocket Real-Time Dashboard" description: "Build a real-time analytics dashboard with WebSocket for live data visualization, streaming metrics, and interactive charts." weight: 27 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "websocket"]


Real-time dashboards push live data to users without page refreshes. WebSocket enables streaming metrics, live charts, and instant updates for monitoring systems, analytics platforms, and operational dashboards.

## What You'll Learn

- Streaming metrics over WebSocket
- Real-time chart updates
- Server-side metric aggregation
- Dashboard data filtering
- Performance optimization for frequent updates

## Why It Matters

Monitoring and analytics require up-to-the-second data. WebSocket dashboards provide instant visibility into system health, business metrics, and operational status.

## Real-World Use

A DevOps monitoring platform streams server metrics (CPU, memory, disk, network) via WebSocket to a real-time dashboard. Operations engineers see infrastructure changes within milliseconds, enabling rapid incident response.

## Flow Chart

```mermaid
flowchart LR
    A[Metrics Sources] --> B[Metric Aggregator]
    B --> C[WebSocket Server]
    C --> D[Dashboard Client]
    D --> E[CPU Chart]
    D --> F[Memory Gauge]
    D --> G[Network Graph]
    D --> H[Alert Panel]

Code Examples

Example 1: Server-Side Metric Streaming

const WebSocket = require('ws');
const os = require('os');

const server = new WebSocket.Server({ port: 8080 });
const clients = new Set();

server.on('connection', (ws) => {
  clients.add(ws);
  console.log('Dashboard client connected. Total:', clients.size);

  ws.on('close', () => {
    clients.delete(ws);
    console.log('Dashboard client disconnected. Total:', clients.size);
  });
});

// Collect and stream metrics every second
setInterval(() => {
  const metrics = {
    type: 'metrics',
    timestamp: Date.now(),
    cpu: {
      loadAverage: os.loadavg(),
      cpus: os.cpus().length,
    },
    memory: {
      total: os.totalmem(),
      free: os.freemem(),
      used: os.totalmem() - os.freemem(),
      usagePercent: ((os.totalmem() - os.freemem()) / os.totalmem() * 100).toFixed(1),
    },
    uptime: os.uptime(),
    network: getNetworkStats(),
    processes: process.memoryUsage(),
  };

  const data = JSON.stringify(metrics);
  clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(data);
    }
  });
}, 1000);

function getNetworkStats() {
  const interfaces = os.networkInterfaces();
  const stats = {};
  for (const [name, addrs] of Object.entries(interfaces)) {
    if (addrs) {
      stats[name] = addrs.filter(a => a.family === 'IPv4').map(a => a.address);
    }
  }
  return stats;
}

Expected output: Server streams system metrics every second to all connected dashboard clients.

Example 2: Client-Side Chart Updates

class MetricsDashboard {
  constructor(wsUrl) {
    this.charts = {};
    this.historySize = 60; // 60 data points (1 minute at 1/sec)
    this.metricHistory = {
      cpu: [],
      memory: [],
      timestamps: [],
    };
    this.connect(wsUrl);
    this.initCharts();
  }

  connect(url) {
    this.ws = new WebSocket(url);
    
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      if (data.type === 'metrics') {
        this.updateDashboard(data);
      }
    };

    this.ws.onclose = () => {
      setTimeout(() => this.connect(url), 2000);
    };
  }

  initCharts() {
    const ctx = document.getElementById('cpu-chart').getContext('2d');
    this.charts.cpu = new Chart(ctx, {
      type: 'line',
      data: {
        labels: [],
        datasets: [{
          label: 'CPU Load Average (1m)',
          data: [],
          borderColor: 'rgb(75, 192, 192)',
          tension: 0.1,
        }],
      },
      options: {
        animation: false,
        scales: {
          x: { display: true, max: this.historySize },
          y: { min: 0, max: 100 },
        },
      },
    });

    // Memory gauge chart
    const memCtx = document.getElementById('memory-chart').getContext('2d');
    this.charts.memory = new Chart(memCtx, {
      type: 'doughnut',
      data: {
        labels: ['Used', 'Free'],
        datasets: [{
          data: [0, 100],
          backgroundColor: ['rgb(255, 99, 132)', 'rgb(75, 192, 192)'],
        }],
      },
      options: {
        animation: false,
        plugins: {
          tooltip: {
            callbacks: {
              label: (context) => {
                return `${context.label}: ${context.parsed.toFixed(1)}%`;
              },
            },
          },
        },
      },
    });
  }

  updateDashboard(metrics) {
    // Update CPU chart
    const cpuLoad = (metrics.cpu.loadAverage[0] / metrics.cpu.cpus) * 100;
    this.metricHistory.cpu.push(cpuLoad);
    this.metricHistory.timestamps.push(
      new Date(metrics.timestamp).toLocaleTimeString()
    );

    if (this.metricHistory.cpu.length > this.historySize) {
      this.metricHistory.cpu.shift();
      this.metricHistory.timestamps.shift();
    }

    this.charts.cpu.data.labels = this.metricHistory.timestamps;
    this.charts.cpu.data.datasets[0].data = this.metricHistory.cpu;
    this.charts.cpu.update('none');

    // Update memory gauge
    this.charts.memory.data.datasets[0].data = [
      metrics.memory.usagePercent,
      100 - metrics.memory.usagePercent,
    ];
    this.charts.memory.update('none');

    // Update metric displays
    document.getElementById('cpu-value').textContent =
      `${cpuLoad.toFixed(1)}%`;
    document.getElementById('memory-value').textContent =
      `${metrics.memory.usagePercent}%`;
    document.getElementById('uptime').textContent =
      formatUptime(metrics.uptime);
    document.getElementById('clients').textContent =
      metrics.clients || 'N/A';
  }
}

function formatUptime(seconds) {
  const days = Math.floor(seconds / 86400);
  const hours = Math.floor((seconds % 86400) / 3600);
  const mins = Math.floor((seconds % 3600) / 60);
  return `${days}d ${hours}h ${mins}m`;
}

// Initialize dashboard
const dashboard = new MetricsDashboard('wss://metrics.example.com/ws');

Expected output: Real-time dashboard updates CPU line chart, memory doughnut gauge, and metric displays every second.

Example 3: Filtered Metric Subscriptions

// Server with subscription filters
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });

server.on('connection', (ws) => {
  ws.filters = {
    metrics: ['cpu', 'memory', 'disk'],
    interval: 1000,
    aggregation: 'raw',
  };

  ws.on('message', (data) => {
    const message = JSON.parse(data);

    switch (message.type) {
      case 'subscribe':
        ws.filters.metrics = message.metrics || ws.filters.metrics;
        ws.filters.interval = message.interval || ws.filters.interval;
        ws.filters.aggregation = message.aggregation || ws.filters.aggregation;
        
        ws.send(JSON.stringify({
          type: 'subscribed',
          metrics: ws.filters.metrics,
          interval: ws.filters.interval,
        }));
        break;
    }
  });
});

// Per-client metric streaming
setInterval(() => {
  const allMetrics = collectAllMetrics();

  server.clients.forEach((client) => {
    if (client.readyState !== WebSocket.OPEN) return;

    const filteredMetrics = {};
    client.filters.metrics.forEach((metric) => {
      if (allMetrics[metric]) {
        filteredMetrics[metric] = allMetrics[metric];
      }
    });

    client.send(JSON.stringify({
      type: 'metrics',
      timestamp: Date.now(),
      ...filteredMetrics,
    }));
  });
}, 100);

Expected output: Dashboard allows users to subscribe to specific metrics, and the server sends only the requested data.

Common Mistakes

Mistake Explanation
Sending too much data too frequently High-frequency updates overwhelm clients; use throttling and aggregation
Not using animation: false in charts Animations cause performance issues with rapid updates; disable for streaming data
Forgetting to clean up old data points Memory grows indefinitely if old data is not discarded from chart datasets
Sending full metric snapshots every time Use delta updates for large datasets to reduce bandwidth
Not handling client backpressure Slow clients can cause server memory growth; implement client-side throttling

Practice Questions

  1. How do you optimize real-time charts for frequent updates?
  2. How do you implement metric subscription filtering?
  3. What is the difference between push and pull metric collection?
  4. How do you handle backpressure from slow dashboard clients?
  5. How do you aggregate metrics for different time windows?

Challenge

Build a real-time application performance monitoring (APM) dashboard that streams request latency, error rates, and throughput from a WebSocket server. Include charts for p50/p95/p99 latency, a heat map of error rates, and live request log.

FAQ

How do I handle timezone differences in dashboard data?

Send timestamps in UTC from the server. Convert to local timezone on the client side using Intl.DateTimeFormat.

What is the best charting library for real-time WebSocket data?

Chart.js with animation disabled works well. For high-performance needs, use Canvas-based libraries like uPlot or lightweight-charts.

How do I reduce bandwidth for dashboard updates?

Use binary formats (MessagePack, Protocol Buffers) instead of JSON. Send deltas instead of full snapshots when possible.

Can I store historical dashboard data?

Yes, store metric data in a time-series database (InfluxDB, TimescaleDB) and load historical data on dashboard initialization.

How do I handle dashboard refresh on page reload?

Store the last N data points on the server per client session. Send a snapshot on reconnection so the dashboard is immediately populated.

What metrics should I include in a server dashboard?

Essential metrics: CPU usage, memory usage, disk I/O, network I/O, request rate, error rate, active connections, and garbage collection stats.

Mini Project

Build a full-stack real-time monitoring dashboard for a web application. Include server metrics (CPU, memory), application metrics (request rate, response time, error rate), business metrics (active users, signups), and alerting when metrics exceed thresholds. Use WebSocket for all live updates.

What's Next

Learn WebSocket with Spring Boot

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro