Real-time photoplethysmography (PPG) signal monitoring using smartphone camera
A JavaScript library for capturing and analyzing PPG signals through camera-based photoplethysmography, providing real-time vital signs monitoring including heart rate, signal quality metrics, and active user guidance.
Try the interactive demo: https://sontakey.github.io/ppg-js/
The demo showcases real-time PPG signal monitoring with visual feedback, quality metrics, and user guidance.
- πΉ Real-time camera-based PPG signal acquisition at 60 FPS
- π Advanced signal quality metrics: SNR, Perfusion Index, Heart Rate, IBI
- π― Visual feedback with color-coded finger placement guide
- π¬ Active user guidance for optimal signal quality
- π¨ Beautiful default UI with customization support
- π§ Headless mode for custom integrations
- β‘ High performance - maintains 60 FPS with < 1% overhead
- π¦ Zero external dependencies (all bundled)
- π Easy integration - drop-in library or npm package
npm install ppg-js<link rel="stylesheet" href="https://unpkg.com/ppg-js/dist/ppg-monitor.css">
<script src="https://unpkg.com/ppg-js/dist/ppg-monitor.min.js"></script>Download the latest release from GitHub Releases
import PPGMonitor from 'ppg-js';
import 'ppg-js/dist/ppg-monitor.css';
// Create monitor instance
const ppg = new PPGMonitor('#container');
// Start monitoring
ppg.start();const ppg = new PPGMonitor(null, {
ui: { enabled: false },
onQualityUpdate: (metrics) => {
console.log('SNR:', metrics.snr_dB, 'dB');
console.log('Heart Rate:', metrics.heartRate, 'BPM');
console.log('Perfusion Index:', metrics.perfusionIndex, '%');
console.log('Status:', metrics.qualityStatus);
// Update your custom UI
updateCustomUI(metrics);
},
onSignalUpdate: (signal) => {
// Real-time signal data
renderCustomChart(signal);
}
});
ppg.start();<div id="ppg-container"></div>
<script src="https://unpkg.com/ppg-js/dist/ppg-monitor.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/ppg-js/dist/ppg-monitor.css">
<script>
const ppg = new PPGMonitor('#ppg-container');
ppg.start();
</script>new PPGMonitor(container, options)Parameters:
container(string|HTMLElement|null): Container element or CSS selector. Passnullfor headless mode.options(Object): Configuration options
Options:
{
// UI options
ui: {
enabled: true, // Enable default UI
showVideo: true, // Show camera preview
showMetrics: true, // Show quality metrics panel
showChart: true, // Show real-time chart
theme: 'light' // UI theme (future feature)
},
// Signal processing options
signal: {
windowLength: 300, // Window size in samples (5s @ 60 FPS)
sampleRate: 60, // Target sample rate in Hz
cardiacBandLow: 0.75, // Lower cardiac frequency in Hz (45 BPM)
cardiacBandHigh: 4.0, // Upper cardiac frequency in Hz (240 BPM)
fftSize: 256 // FFT size (power of 2)
},
// Camera constraints
camera: {
maxWidth: 1280,
maxHeight: 720,
frameRate: { ideal: 60 },
facingMode: 'environment' // 'user' for front camera
},
// Callbacks
onReady: () => {}, // Called when monitoring starts
onQualityUpdate: (metrics) => {}, // Called every ~5 seconds with quality metrics
onSignalUpdate: (signal) => {}, // Called on each frame with signal data
onFrame: (data) => {}, // Called on each processed frame
onError: (error) => {} // Called on errors
}Start PPG monitoring.
await ppg.start();Returns: Promise<void>
Stop PPG monitoring and release camera.
ppg.stop();Get current signal quality metrics.
const metrics = ppg.getMetrics();
// Returns: { snr_dB, perfusionIndex, heartRate, ibi, qualityStatus, guidanceMessage, ... }Get current signal quality status.
const status = ppg.getSignalQuality();
// Returns: "Excellent" | "Good" | "Fair" | "Poor"Destroy the monitor and cleanup all resources.
ppg.destroy();The metrics object passed to onQualityUpdate contains:
{
snr_dB: number, // Signal-to-Noise Ratio in dB
perfusionIndex: number, // Perfusion Index (%)
heartRate: number, // Heart rate in BPM
ibi: number, // Inter-Beat Interval in ms
signalStability: number, // Signal stability (0-1)
qualityStatus: string, // "Excellent" | "Good" | "Fair" | "Poor"
guidanceMessage: string, // User guidance text
qualityFrameCount: number // Count of high-quality frames
}The library uses your smartphone's camera and flashlight to capture photoplethysmography (PPG) signals:
- Signal Acquisition: Camera captures color changes in fingertip at 60 FPS
- Red Channel Extraction: Only red channel is used for PPG signal
- Detrending: Linear detrending removes baseline drift every 5 seconds
- FFT Analysis: Fast Fourier Transform analyzes frequency components
- SNR Calculation: Signal power in cardiac band (0.75-4.0 Hz) vs noise
- Quality Metrics: Real-time calculation of SNR, Perfusion Index, heart rate
- User Guidance: Context-aware messages guide users to optimal placement
- Excellent: SNR > 10 dB (green indicator)
- Good: SNR 5-10 dB (light green indicator)
- Fair: SNR 0-5 dB (yellow indicator)
- Poor: SNR < 0 dB (red indicator)
- Click the "Measure" button
- Grant camera and flashlight permissions
- Cover both camera and flashlight with your finger
- Wait for the finger guide circle to turn green
- Follow on-screen guidance messages:
- "Cover camera completely" β No signal detected
- "Press finger more firmly" β Low perfusion
- "Hold finger still" β Motion detected
- "Good signal - hold steady" β Optimal signal
- Chrome/Edge 89+
- Safari 14.1+
- Firefox 88+
- Mobile browsers with camera access
Requirements:
- HTTPS (required for camera access)
- getUserMedia API support
- ImageCapture API for flashlight (optional but recommended)
Check the examples/ directory for:
- basic - Simple usage with default UI
- headless - Custom UI with callbacks
- demo - Original demo application
This library is for demonstration and research purposes only. It is not a certified medical device and should not be used for medical diagnosis or treatment.
The goal of this project is to demonstrate that:
- Modern smartphones can capture good quality PPG signals
- Signal quality can be quantified and optimized
- The captured signals can potentially be used for cardiovascular metrics research
# Clone repository
git clone https://github.com/sontakey/ppg-js.git
cd ppg-js
# Install dependencies
npm install
# Build library
npm run build
# Serve examples locally
npm run serveppg-js/
βββ src/
β βββ PPGMonitor.js # Main monitor class
β βββ SignalProcessor.js # Signal processing logic
β βββ UIRenderer.js # UI rendering
β βββ components/
β β βββ RealTimeChart.js # D3.js chart component
β βββ utils/
β β βββ detrend.js # Linear detrending
β β βββ fft.js # FFT operations
β β βββ helpers.js # Helper functions
β βββ styles/
β β βββ ppg-monitor.css # Styles
β βββ index.js # Entry point
βββ dist/ # Build outputs
βββ examples/ # Usage examples
βββ package.json
[1] Vandenberk T, et al. "Clinical Validation of Heart Rate Apps" JMIR Mhealth Uhealth 2017;5(8):e129 https://mhealth.jmir.org/2017/8/e129
MIT License - see LICENSE file
Sameer Sontakey
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Give a βοΈ if this project helped you!
For questions or feedback, please open an issue on GitHub.