Note to Readers:

Hey there! You’re about to read an early version of my latest blog post, introducing the BiomedWaveforms project. This isn’t the final cut – it’s more of a ‘work-in-progress’ showcase. There might be a few rough edges, a typo here, or a technical misstep there. But that’s where you come in! I’m eagerly seeking your feedback, insights, and suggestions. Whether you’re a physician, an engineer, or just someone fascinated by the intersection of these fields, your input is invaluable. Help me refine this post, enhance its accuracy, and enrich its perspective. I am looking forward to your constructive critiques and creative ideas!



I’m excited to introduce BiomedWaveforms, a project that started pulling at my heartstrings a few months ago. I developed the alpha version of BiomedWaveforms between the odd hours of my ICU sub-internship. It is a simple JavaScript framework – free, open-source, and dedicated to simulating biomedical waveforms like EKGs, capnograms, and more.

Screenshot of hospital monitor showing EKG, blood oxygenation, and respiration waveforms.
Hospital monitor with my vitals.

Like most medical students, I’ve spent countless hours staring at vital sign monitors in the hospital. These danger squiggles can give physicians an up-to-date and nuanced picture of a patient’s health. Each line provides a different view of the patient’s health and organs.

Usually, we will start with EKG and blood oxygen saturation monitoring, as these give us a lot of information without being invasive. As patients get sicker, we may get more information in the form of exhaled CO2 and invasive blood pressure. These can be measured in a real-time (effectively) continuous manner and then displayed on bedside monitors.

Background and Inspiration

Despite spending an ungodly amount of time with these monitors and their squiggles, I realized I don’t have a good way of replicating a squiggle other than drawing it manually. Replicating squiggles is essential for a variety of reasons. For example, as a learner, I’d love to expose myself to dangerous squiggles in non-dangerous settings, so simulating squiggles is immensely appealing. However, from a learner’s perspective, there are no ways to generate new waveforms other than finding ones previously collected from patients.

iMessage chat screenshot asking about EKG waveform generators.
Not a lot of choices on the EKG simulator front.

The engineer in me is deeply offended by this notion - “What do you mean you have no way to simulate this important data?!” There are simulators, but they are not easily accessible to all medical students and doctors. There are no free and widely available simulators for generating biomedical waveforms. This project aims to fill that gap.

Practically speaking, we are discussing a way to make squiggles on a screen. However, I hope BiomedWaveforms will make important healthcare data more accessible and interpretable. Which is what I’m all about. And we will have some fun along the way.

BiomedWaveforms Framework Overview

BiomedWaveforms is a JavaScript framework. You can find all the code on GitHub. The programmers in the audience can tell it’s not super advanced; it’s just a bunch of hand-rolled base JavaScript coded by ChatGPT.

It’s a bunch of smelly code that could use a shower and a refactor. But, it hits its initial objectives. My primary goal was to have a way to generate static and dynamic waveforms for EKGs and capnograms (✅). And it can be extended for other waveforms like blood oxygenation (✅).

You can generate a new waveform by calling a couple of lines of JavaScript. For example, here’s a simulated EKG with a wide QRS complex:

Wide QRS Example

The great thing about the framework is that it approaches the waveforms from a parameterized standpoint. You can describe most of the squiggles we see as numbers. I’ll break this down in a way guaranteed to offend the intelligence of engineers and physicians.

EKGs are voltage plots, in terms of millivolts (mV), over time, in milliseconds (ms). The line’s upward or downward deflection represents electrical changes that occur with cardiac muscle depolarization and repolarization throughout a heartbeat cycle. Each cycle can be broken down into a series of waves that are generated due to specific parts of the cardiac conduction system. Here’s a figure from Wikipedia that breaks down some of the critical components.

Electrocardiogram (ECG) waveform showing the QRS complex. The Q wave is the first short downward deflection, the R wave is the first upward deflection, and the S wave is the first downward deflection after the R wave. The diagram also shows other ECG waveform components like the P wave and T wave.
A schematic of an EKG highlighting the QRS complex, which includes the Q wave, R wave, and S wave. Other components, such as the P and T waves, are also labeled.

The example EKG tracing above has a “wide QRS complex”; this means that the Q, R, and S take a longer than normal time. The QRS complex corresponds to the depolarization & contraction of the heart’s ventricles. Typically, the QRS complex lasts about 80-100ms. The default EKG generated by BiomedWaveforms has a QRS complex duration of 95ms. However, conduction abnormalities can cause it to take longer or be “widened,” this is what is going on in our example, as it is 190ms.

BiomedWaveforms enables us to make this pathologic example efficiently by taking in parameters from the user; the user does this by specifying the duration of each constituent wave (Q, R, and S). For the example above, I set the Q wave’s duration to 60ms, 70ms for R, and 60 for S. These durations are some of the parameters. Others represent the duration of other waves, intervals between waves, and voltages (amplitudes) of waves. We can generate different EKG waveforms de novo by providing different values for these parameters. You can mess around with the full power of EKG parameterization here.

We aren’t limited to EKGs. This process can be repeated with other waveforms, such as capnograms.

Parameterization is BiomedWaveforms’s fundamental design paradigm. Plug in the key parameters you want, and you get a new wave, no patients needed! The underlying javascript code will take care of drawing the details. It’s like generative AI with no hallucinations because you have everything nailed down, and the code illustrates the waveform precisely how you want (if my code is correct).

How do I use it?

Great question.

First, you need to access BiomedWaveforms. It is freely available JavaScript code hosted on my GitHub, so you can download it and run it locally. Or you can take advantage of the fact that it automatically gets hosted on JSDeliv, and any webpage can access the codebase with the right code.

Some example code:

<html lang="en">
        <canvas id="wideQRSEKGMonitor" width="400" height="160"></canvas>

        <script type="module">
            import { DefaultEKGMonitor } from '';
                {qDuration: 60, rDuration: 70, sDuration: 60}, 

This is the code used to generate the wide QRS example from above. It’s an HTML wrapper around a concise JavaScript module.

I’m not very good at JavaScript or web development (I’m actually horrible at it and hate it). So, for my own sake and understanding, let’s walk through the JavaScript piece by piece.

First, we need to set up where we will draw the EKG.

<canvas id="wideQRSEKGMonitor" width="400" height="160"></canvas>

This code creates an empty HTML canvas named wideQRSEKGMonitor (a gorgeous variable name).

Second, we need to get the BiomedWaveforms code. Let’s use the JSDeliv approach, as it’s more straightforward than having to download my code and re-serve it with this code.

import { DefaultEKGMonitor } from '';

This code imports the BiomedWaveforms JavaScript codebase (alpha version v0.1.1) from JSDeliv, which is simple (although it took me several attempts to get it correct).

Third, we draw the EKG using parameters.

    {qDuration: 60, rDuration: 70, sDuration: 60}, 

The DefaultEKGMonitor is a helper function that does a bunch of stuff for you. It generates the EKG waveform using the parameters defined inside the curly brackets:{qDuration: 60, rDuration: 70, sDuration: 60} (this gives us our QRS W I D E N E S S). Additionally, it plots the EKG waveform to the wideQRSEKGMonitor canvas we defined above.

That’s it. You should be able to copy-paste that code into a blank file named whateveryouwant.html, and your browser should be able to plot a wide QRS EKG for you.

Another Example

If you don’t like EKGs (maybe you were traumatized by an orange textbook), here’s an example with capnography. Capnography shows the partial pressure of carbon dioxide in exhaled breath over time.

Tweaked Capnography Example

The code for this example is as follows:

<html lang="en">
        <canvas id="tweakedCapnographyMonitor" width="400" height="160"></canvas>
        <script type="module">
            import { DefaultCapnographyMonitor } from '';

                inspirationDuration: 1000,
                expiratoryUpstrokeDuration: 200,
                endExpiratoryCO2: 39,
                alveolarPlateauDuration: 1800,
            }, 'tweakedCapnographyMonitor');

There are two main differences between this code and the code for the EKG. First, we are pulling a different JavaScript module, DefaultCapnographyMonitor, from BiomedWaveforms instead of the DefaultEKGMonitor. The second is that we are providing different parameters; as you can tell (for those of you who are docs) or guess (for those of you who are engineers), these parameters are specific to capnography.

Defaults & Parameter Lists

Here are links where you can find:

The parameters for these defaults are as follows:

//Default EKG Parameters
    frequency: 250,         //this is the sample frequency, in hertz
    pWaveDuration: 80,      //the duration of the P wave, in ms
    pWaveMagnitude: 0.25,   //the amplitude (maximum) of the P wave. in mV
    qDuration: 30,          //the duration of the Q wave, in ms
    qMagnitude: -0.1,       //the amplitude of the Q wave, in mV
    rDuration: 35,          //the duration of the R wave, in ms
    rMagnitude: 1.2,        //the amplitude of the R wave, in mV
    sDuration: 30,          //the duration of the S wave, in ms
    sMagnitude: -0.2,       //the amplitude of the S wave, in mV
    tWaveDuration: 160,     //the duration of the T wave, in ms
    tWaveMagnitude: 0.3,    //the amplitude of the T wave, in mV
    prInterval: 120,        //the duration between the start of the P and R waves, in ms
    prSegmentElevation: 0,  //the height of the segment between P and R waves, in mV
    qtInterval: 360,        //the duration between the Q and T waves, in ms
    stSegmentElevation: 0,  //the height of the segment between the S and T waves, in ms

//Default Capnography Parameters
    frequency: 250,                     //this is the sample frequency, in hertz
    inspirationDuration: 1000,          //the duration of inspiration, in ms
    expiratoryUpstrokeDuration: 100,    //the duration of the initial phase of expiration, in ms
    endExpiratoryCO2: 40,               //the partial pressure of CO2 at the end of the initial phase of expiration, in mmHg               
    alveolarPlateauDuration: 1800,      //the duration of the second (alveolar) phase of expiration, in ms
    endAlveolarCO2: 45,                 //the partial pressure of CO2 at the end of the second phase of expiration, in mmHg
    inspiratoryDownstrokeDuration: 100, //the duration of the third phase of expiration, in ms

Pathologic Examples

I’ve started a small list of pathologic examples of EKGs. Follow these links to find examples of peaked T waves, prolonged PR intervals, and wide QRS complexes. This is a limited starting list, and I would love to hear what you want to add.


This is just the alpha version of BiomedWaveforms. It’s got potential, but there’s a lot of room for improvement. We’ve got EKGs and capnograms, but a whole world of biomedical waveforms is out there waiting to be coded up. And let’s not forget the finer details like the elusive J wave or other nuanced parts of the EKG and capnography waveforms. They’re on my to-do list, but I still need to add them to the codebase.

Call to Action

Here’s where I put on my “help wanted” sign. I’m just one guy trying to marry engineering with medicine, and while ChatGPT has been a lifesaver, there’s only so much one person (and one AI) can do. This is where you come in. You may be a clinical expert with a unique use case in mind or a JavaScript wizard who can help clean up my spaghetti code. Whatever your skill set, I’m all ears. Let’s make BiomedWaveforms useful together.

Parting Thoughts

Engineering and medicine are like peanut butter and chocolate - two great things that are even better together. BiomedWaveforms is just one example of this beautiful synergy. At its core, it’s about using models to enhance our understanding of complex systems – in this case, the human body. It’s astonishing that in medicine, where stakes are high and lives are on the line, we often rely on learning methods like textbooks and question banks. High-quality computational models like BiomedWaveforms offer a new, interactive way to learn and understand. Imagine the possibilities if we embraced this approach more broadly.

This would have been a tougher sell 12 months ago. Heck, I wouldn’t have been able to put together this project at that time. But with the advent of LLM chatbots we can start to turn some of these ideas into reality without having to master all of the necessary technical skills. I am excited to continue to bring you projects like this. Hopefully, I can also convince you to join me by doing so.

Go ÖN Home