Skip to content

Signal Visualizer

The Signal Visualizer displays real-time scrolling PPG and respiratory waveform data alongside the camera feed. It provides immediate visual feedback about signal quality and measurement stability.

How It Works

During realtime estimation, the SDK continuously processes video frames to extract physiological signals. The Signal Visualizer captures this signal data and renders a scrolling waveform chart that updates with each processed frame. Multiple signal channels can be displayed simultaneously with customizable colors.

Configuration

The Signal Visualizer is created as a standalone component and connected to the camera's frame processing callback. It renders into any HTML container element.

typescript
import { createVitalSignCamera, SignalVisualizer } from 'ts-vital-sign-camera';

const camera = createVitalSignCamera({
  userId: 'user-123',
  apiKey: 'your-api-key',
  enableRealtimeEstimation: true
});

// Create the visualizer with a container element
const visualizer = new SignalVisualizer(
  document.getElementById('signal-chart'),  // Container element
  {
    signalColors: ['#ff6b6b', '#4ecdc4'],  // Colors for each signal channel
    windowSeconds: 10,                      // Display window size (default: 10)
    pixelsPerSecond: 60,                    // Horizontal resolution (default: 60)
    height: 200,                            // Chart height in pixels (default: 200)
    lineWidth: 2,                           // Stroke width (default: 2)
    backgroundColor: '#1a1a2e',             // Background color
    gridColor: '#16213e',                   // Grid line color
    enableAutoScale: true,                  // Auto-adjust Y-axis (default: true)
    yAxisMin: -1.5,                         // Fixed Y-axis minimum (autoScale: false)
    yAxisMax: 1.5,                          // Fixed Y-axis maximum (autoScale: false)
    showLegend: true,                       // Channel label legend (default: false)
    enableLowPassFilter: true,              // Noise reduction filter (default: false)
    lowPassFilterCutoff: 5,                 // Filter cutoff in Hz (default: 5)
    showGrid: true,                         // Grid lines (default: true)
    label: 'PPG Waveform',                  // Chart label string
  }
);

// Connect visualizer to frame processing
camera.visualizer = visualizer;

// Alternative: manual connection via callback
camera.onVideoFrameProcessed = (event) => {
  if (event.signalData) {
    visualizer.updateSignal(event.signalData, event.realtimeEstimation);
  }
};
tsx
import { VitalSignCamera, SignalVisualizer } from 'react-vital-sign-camera';
import { useEffect, useRef } from 'react';

const App = () => {
  const chartRef = useRef(null);
  const visualizerRef = useRef(null);

  useEffect(() => {
    if (chartRef.current) {
      visualizerRef.current = new SignalVisualizer(chartRef.current, {
        signalColors: ['#ff6b6b'],
        windowSeconds: 8,
        height: 180
      });
    }
    return () => visualizerRef.current?.destroy();
  }, []);

  return (
    <div>
      <VitalSignCamera
        enableRealtimeEstimation={true}
        visualizer={visualizerRef.current}
      />
      <div ref={chartRef} />
    </div>
  );
};
vue
<template>
  <div>
    <VitalSignCamera
      :enableRealtimeEstimation="true"
      :visualizer="visualizer"
    />
    <div ref="chartContainer" class="signal-chart" />
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { SignalVisualizer } from 'ts-vital-sign-camera';

const chartContainer = ref(null);
const visualizer = ref(null);

onMounted(() => {
  visualizer.value = new SignalVisualizer(chartContainer.value, {
    signalColors: ['#4ecdc4', '#ff6b6b'],
    windowSeconds: 10,
    height: 200,
    showLegend: true,
    enableLowPassFilter: true
  });
});

onUnmounted(() => {
  visualizer.value?.destroy();
});
</script>

<style scoped>
.signal-chart {
  width: 100%;
  max-width: 800px;
  margin-top: 16px;
}
</style>

SignalVisualizerConfig Interface

typescript
interface SignalVisualizerConfig {
  signalColors?: string[];          // CSS color strings for each channel
  windowSeconds?: number;           // Time window in seconds (default: 10)
  pixelsPerSecond?: number;         // Horizontal pixels per second (default: 60)
  height?: number;                  // Chart height in pixels (default: 200)
  lineWidth?: number;               // Stroke width in pixels (default: 2)
  backgroundColor?: string;         // Background CSS color
  gridColor?: string;               // Grid line CSS color
  enableAutoScale?: boolean;        // Auto Y-axis scaling (default: true)
  yAxisMin?: number;                // Fixed Y min (when autoScale: false)
  yAxisMax?: number;                // Fixed Y max (when autoScale: false)
  showLegend?: boolean;             // Show channel legend (default: false)
  enableLowPassFilter?: boolean;    // Enable signal smoothing (default: false)
  lowPassFilterCutoff?: number;     // Low-pass filter cutoff Hz (default: 5)
  showGrid?: boolean;               // Show grid lines (default: true)
  label?: string;                   // Chart label
}

Methods

MethodDescription
updateSignal(data, estimation?)Push new signal data point, optionally with estimation context
destroy()Clean up resources and DOM elements

VisualSignalType

typescript
enum VisualSignalType {
  PPG = 'PPG',
  Respiratory = 'Respiratory'
}

Tips

  • Place the visualizer container below or beside the camera element for best layout
  • Use enableLowPassFilter with a cutoff of 3-5 Hz to reduce high-frequency noise while preserving pulse waveform features
  • Multiple signal channels automatically map to the colors in signalColors[]; provide one color per expected channel