跪拜 Guibai
← All articles
Frontend

h5ECG: A Canvas Rendering Library That Draws Real ECG Paper, Not Just Curves

By 洪贺 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Most charting libraries treat ECG data as generic time-series lines, ignoring the strict calibration, grid ratios, and multi-lead layouts that make a waveform clinically legible. h5ECG offloads that domain-specific rendering so frontend teams building health dashboards or diagnostic viewers don't reinvent a fragile, incorrect ECG paper from scratch.

Summary

Drawing a medical-grade ECG waveform on the web requires more than connecting data points with a line. Raw values from acquisition devices must be calibrated from ADC units into millivolts, then plotted against a grid where speed and gain dictate the physical meaning of every millimeter. h5ECG handles this entire pipeline, from data calibration and multi-lead layout to pixel-bucket peak preservation during zoom.

The library ships with native, React, and Vue entry points. It supports standard 12-lead layouts like 3x4 and 6x2 in both synchronous and continuous modes, and a draggable ruler returns duration, voltage, and heart rate estimates directly from the rendered waveform. Grid and waveform layers are drawn separately, and only visible samples are processed to keep rendering performant.

A live demo and source are available on GitHub. The project is actively maintained and accepts contributions, with the author committing to fold real-world performance and data-adaptation fixes back into the repository.

Takeaways
Raw ADC integers from ECG devices must be converted to millivolts using a baseline and a units-per-mV factor before they carry any medical meaning on screen.
Speed (25 mm/s) and gain (10 mm/mV) define the physical scale of the grid; changing pixelsPerMillimeter only adjusts screen density without breaking those ratios.
During zoom-out, high-density samples are bucketed per pixel and their local peaks and valleys are preserved so that narrow spikes don't disappear.
The library supports 12-lead layouts including 12x1, 6x2, and 3x4 in both synchronous and continuous modes, accepting either an array or a named-lead object.
A draggable measurement ruler returns duration in milliseconds, voltage in millivolts, and an estimated heart rate in BPM, all derived from the calibrated rendering parameters.
h5ECG provides native, React, and Vue wrappers so teams can drop an ECG viewer into a report page with a single component and a data prop.
Conclusions

ECG rendering sits at an uncomfortable boundary where frontend convenience and medical correctness collide; getting the units wrong produces a waveform that looks plausible but is clinically meaningless.

Preserving peak-valley extrema during downsampling is a deliberate choice that prioritizes diagnostic signal integrity over smooth anti-aliased lines, which is the opposite of what most charting libraries optimize for.

The library's calibration defaults are tied to one specific device's ADC parameters, which means every adopter must consciously override them — a design that forces correctness rather than hiding it behind a magic black box.

Concepts & terms
ADC units per mV
The conversion factor that maps raw analog-to-digital converter integer values to millivolts. For the default h5ECG calibration, 1000 ADC units equal 1 mV.
ECG speed and gain
Standard ECG paper runs at 25 mm/s (so a 1 mm small square equals 40 ms) and 10 mm/mV gain (so a 10 mm deflection equals 1 mV). These ratios give the grid its diagnostic meaning.
Pixel-bucket peak preservation
When zooming out, multiple data samples map to a single screen pixel. Instead of averaging them, the renderer keeps the minimum and maximum values per pixel bucket so that narrow spikes remain visible.
From the discussion
Featured comments
圊妖

A question: for data like EEG, you often need real-time observation, meaning continuous rendering of the latest data (say, sampling every 10ms). With charts needing frequent updates (and the data volume still being large, e.g., 5000 samples), would that cause lag? And if a page renders multiple charts simultaneously, each with the same large data volume and update frequency, can it hold up?

京东菜鸟全球通快递小哥

worker

圊妖  → 京东菜鸟全球通快递小哥

But workers can only handle computation; they can't solve the rendering problem. My main concern here is still the frequent rendering of large data volumes.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗