Creating A Bubble Chart With NodeJS
Tags: datavisualization, javascript, node, tutorial
Published: 2022-08-16 07:58:53

##What's behind creating a colorful and playful bubble chart/graph?

![JS-Bubble-Chart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t81aazhd03i5hxxpq3xj.gif)

### Scatter Charts<a name="chapter-1"></a>
If you have any experience or background in finance or statistics, you'll easily detect that a bubble graph is simply a variation of a scatter chart where the bubbles size is a 3D representation of the data magnitude.

Here's a [normal scatter chart](https://www.arction.com/lightningchart-js-interactive-examples/examples/lcjs-example-0001-simpleScatter.html?theme=darkGreen&page-theme=dark) built using the PointSeries property of the LightningChart JS library.

This "basic" scattergram is perfect for visualizing the relationship between variables.

![Simple Scatter Chart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlirlf0rhlwexglqui0t.png)


And here's how a [scatter graph with 1 million data points](https://www.arction.com/lightningchart-js-interactive-examples/examples/lcjs-example-0016-largeScatterChartXY.html?theme=darkGreen&page-theme=dark) looks like. Ideally, these advanced variations come with interactivity features to zoom in/out such a large amount of data.

![1 million data points scatter chart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hm32a1b6pidoi3v1lqr0.png)


But scatter series also support [3D rendering](https://www.arction.com/lightningchart-js-interactive-examples/examples/lcjs-example-0908-3dRealtimePoints.html?theme=darkTurquoise&page-theme=dark). The main capability of this example is to constantly process new data as the processing happens in real-time.

![3D real-time scatter chart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zc9j9fio527z571t1xe6.png) 


Scatter charts are also used in science and medical research. Here's the **[Flow Cytometry](https://www.arction.com/lightningchart-js-interactive-examples/examples/lcjs-example-0019-flowCytometryChart.html?theme=darkGold&page-theme=dark)** scatter series variation. 

(Flow cytometry is a common technique in research and clinical practices. It allows to measure cells in heterogeneous samples).

![Flow Cytometry Chart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlpd5rqs717jbv312c89.png)

And with that explanation, <u>let's code a bubble chart</u> :)

---

##Setting Up The Project's Template

To follow this tutorial, please download the RAR project template. [You can download the template here.](https://lightningchart.com/wp-content/uploads/Customized%20JavaScript%20Chart%20-%20Bubbles.rar)

1.After downloading, open the folder in Visual Studio, and you'll see a file tree like this:

![File tree](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/unxxg2lce6kh8zncak2c.png)


2.Now, let's open a new terminal and as usual in a Node JS project, we will have to run our NPM Install command.

That'd be all for our initial setup.

---

##CHART.ts

Inside this file, there will be code for creating our application, configuring animations, and formatting data.

1.Declare the constant **lcjs** that will refer to our **@arction/lcjs library**.

2.Extract required classes from **lcjs**:

```javascript
// Import LightningChartJS
const lcjs = require('@arction/lcjs')

// Extract required parts from LightningChartJS.
const {
    lightningChart,
    SolidFill,
    SolidLine,
    ColorRGBA,
    emptyFill,
    FontSettings,
    AutoCursorModes,
    Animator,
    AnimationEasings,
    UIDraggingModes,
    UIOrigins,
    ColorPalettes,
    AxisTickStrategies,
    emptyLine,
    Themes
} = lcjs
```


<br>3.Now let's define the text and color settings:

```javascript
// Define colors to configure chart and bubbles.
const colors = {
    background: ColorRGBA(255, 255, 255),
    graphBackground: ColorRGBA(220, 255, 255),
    title: ColorRGBA(0, 100, 0),
    subTitle: ColorRGBA(0, 100, 0),
    bubbleBorder: ColorRGBA(0, 0, 0),
    bubbleFillPalette: ColorPalettes.fullSpectrum(100)
}
```
**Note:** Inside the colors class we will store common properties for easier access later.


<br>4.Now we have to define the font settings:

```javascript
// Define font settings.
const fonts = {
    subTitle:new FontSettings({
        size: 15,
    }),
        title: new FontSettings({
        size: 40,
        weight: 400
    })
}
```

<br>5.Creating the chart object:

```javascript
// Create chart with customized settings
const chart = lightningChart()
    .ChartXY({
        theme: Themes.lavender,        
    })
    .setTitle('Custom Styled Chart')
    .setTitleMarginTop(6)
    .setTitleMarginBottom(0)
    .setPadding({ left: 5, right: 5, top: 30, bottom: 30 })
    .setAutoCursorMode(AutoCursorModes.disabled)
    .setMouseInteractions(false)
```

Within the chart object, we need to define three properties, <em>theme</em>, <em>cursor mode</em>, and the <em>mouse interactions</em>.

<dl>
  <dt>Theme:</dt>
  <dd>the lcjs library has several default implementations and can be accessed by Themes. <a href="https://arction.com/lightningchart-js-api-documentation/v3.4.0/interfaces/themes.html"> More about themes in the documentation.</a></dd><br>

  <dt>setAutoCursorMode:</dt>
  <dd>allows to define the behavior of the <em>AutoCursor</em> in the chart.</dd> 
  <dd>Supported behaviors include <em>AutoCursor</em> disabled, <em>onHover</em>, and <em>snapToClosest</em>. <a href="https://www.arction.com/lightningchart-js-api-documentation/v1.0.0/enums/autocursormodes.html"> More about this here.</a></dd><br>

  <dt>setMouseInteractions:</dt>
  <dd>Set mouse interactions enabled. Implementations should update the mouse-interactions of any Shapes they may contain here.</dd><br>
</dl>

6.Adding the axes to the chart:

```javascript
// Get axes.
const axes = {
    bottom: chart.getDefaultAxisX(),
    left: chart.getDefaultAxisY(),
    top: chart.addAxisX(true),
    right: chart.addAxisY(true).setChartInteractions(false)
}
```

<dl>
  <dt>getDefaultAxisX / AxisY:</dt>
  <dd>Get the axis object. With this object, we can add more properties to the specific axis.</dd><br>

  <dt>addAxisX / addAxisY:</dt>
  <dd>this will return the axis created.</dd><br>

  <dt>setChartInteractions:</dt>
  <dd>this will set all the mouse interactions in the chart within the axis at once.</dd><br>
</dl>


7.Adding the UIelement to the chart: 

```javascript
chart.addUIElement(undefined, chart.uiScale)
    .setPosition({ x: 50, y: 93 })
    .setOrigin(UIOrigins.Center)
    .setText('- With Bubbles -')
    .setTextFont(fonts.subTitle)
    .setDraggingMode(UIDraggingModes.notDraggable)
    .setBackground((bg) => bg
        .setFillStyle(emptyFill)
        .setStrokeStyle(emptyLine)
    )
```
With the **addUIElement** we can create a single text element to the chart. In this case, we are creating and formatting the subtitle.

And you'll notice that almost all the properties are very familiar to CSS properties.

**setDraggingMode** makes non-draggable the text element. If the property is removed, then the subtitle element will be draggable.

<br>8.Configure the axes:

```javascript
// Axis mutator.
const overrideAxis = (axis) => axis
    .setTickStrategy(AxisTickStrategies.Empty)
    .setTitleMargin(0)
    .setMouseInteractions(undefined)
```

<dl>
  <dt>AxisTickStrategies:</dt>
  <dd><p>Collection of available AxisTickStrategies. AxisTickStrategies modify logic of drawing Axis Ticks and formatting to better suit different user applications. 

For example, a DateTime Axis is created by selecting AxisTickStrategies.DateTime. <a href="https://www.arction.com/lightningchart-js-api-documentation/v3.4.0/globals.html#axistickstrategies.">More about this in the documentation</a>.</p></dd>

  <dt>setMouseInteractions:</dt>
  <dd>Set if mouse and cursor interactions should be disabled during scrolling animations for the chart's series.</dd><br>

  <dt>Parameters:</dt>
  <dd><b>state: boolean</b>. <p>True if mouse and cursor interactions should be disabled during scrolling animations, false if not.</p></dd>
</dl>

<br>9.Configuring the bubbles:

```javascript
[axes.bottom, axes.left].forEach(axis => axis.setInterval(-100, 100).setScrollStrategy(undefined))
// Ratio between bubble ellipse width / height.
const bubbleWidthHeightRatio = {
    x: window.innerHeight / window.innerWidth,
    y: 1
}

// Create instance of ellipse series to draw bubbles.
const ellipseSeries = chart.addEllipseSeries()
let bubbleCount = 0
```

<dl>
  <dt><a href="https://www.arction.com/lightningchart-js-api-documentation/v3.4.0/classes/axis.html#setinterval">setInterval:</a></dt>
    <dd>Set axis scale interval.</dd><br>

  <dt><a href="https://www.arction.com/lightningchart-js-api-documentation/v3.4.0/classes/axis.html#setscrollstrategy">setScrollStrategy:</a></dt>
    <dd>Specify ScrollStrategy of the Axis. This decides where the Axis scrolls based on current view and series boundaries.</dd><br>

  <dt><u>const bubbleWidthHeightRatio:</u></dt>
    <dd><p>This constant will help to have an initial ratio size for axes X and Y. These values will be calculated for each bubble later.</p></dd>

  <dt><a href="https://www.arction.com/lightningchart-js-api-documentation/v3.4.0/classes/chartxy.html#addellipseseries">addEllipseSeries:</a></dt>
    <dd>Method for adding a new EllipseSeries to the chart. This series type visualizes a collection of ellipses.</dd><br>

  <dt><u>bubbleDragHandler:</u></dt>
</dl>

```javascript
const bubbleDragHandler = (figure, event, button, startLocation, delta) => {
    const prevDimensions = figure.getDimensions()
    figure.setDimensions(Object.assign(prevDimensions, {
        x: prevDimensions.x + delta.x * figure.scale.x.getPixelSize(),
        y: prevDimensions.y + delta.y * figure.scale.y.getPixelSize()
    }))
}
```
In order to simulate the drag of each bubble, we will need to obtain the **interval** and the **size** of the pixels of the scale direction.

To obtain the interval, the dimension of each axis will be obtained from the **figure object** and also we will sum the delta value to this result.

**Delta** is a variable that will contain the central value within the chart.

To adjust for interval changes, multiply the above result by the pixel size in each scaling direction.

To get the pixel size on each axis, the **getPixelSize()** function must be used.

Create resizeBubble array and sizeArray to store the values separately.

```javascript
const resizeBubble = []
const sizeArray = []
```
<br>
10.Now, let's add the bubbles

```javascript
const addBubble = (pos, size) => {
    const radius = size * 2.5
    const borderThickness = 1 + size * 1.0

    const color = colors.bubbleFillPalette(Math.round(Math.random() * 99))
    const fillStyle = new SolidFill({ color })
    const strokeStyle = new SolidLine({ fillStyle: colors.bubbleBorder, thickness: borderThickness })

    const figure = ellipseSeries.add({
        x: pos.x,
        y: pos.y,
        radiusX: radius * bubbleWidthHeightRatio.x,
        radiusY: radius * bubbleWidthHeightRatio.y
    })
        .setFillStyle(fillStyle)
        .setStrokeStyle(strokeStyle)

    // Make draggable by mouse.
    figure.onMouseDrag(bubbleDragHandler)
    bubbleCount++
    return figure
}
```

<br><p>The <b>addBubble</b> function will create the bubbles by collecting values from all previous constants and functions the we worked before.</p>

The **pos(position)** and **size** parameters, will be provided when this function is executed.

The **Radius** constant is equal to the size value. The multiplication works as a ratio increaser, you can increase or decrease it by removing or modifying the value of this.

**Color**, **fillStyle**, and **strokeStyle**, are UI properties that use values from the **colors** class that we created at the beginning of the project.

The **figure** object will execute the function **addEllipseSeries**, this function is provided by the LC library, and it creates ellipses figures for an XY chart.

Inside the **figure** constant, we will encapsulate the position and radius properties for each bubble.

Finally, the UI properties and the Drag handler function will be added to the bubble.

```javascript
const addRandomBubble = () => {
    const pos = {
        x: Math.random() * 200 - 100,
        y: Math.random() * 200 - 100
    }
    const size = 1 + Math.random() * 7.0
    sizeArray.push(size)
    resizeBubble.push(addBubble(pos, size))
}
```

The **addBubble** function will be executed by the **addRandomBubble**.

This function will create random position and size values.

If you want to use real data from a JSON object or database, you can modify this function.

```javascript
// Amount of bubbles to render.
const bubbleMaxCount = 100

// Animate bubbles creation.
Animator(() => undefined)(2.5 * 1000, AnimationEasings.ease)([[0, bubbleMaxCount]], ([nextBubbleCount]) => {
    while (bubbleCount < nextBubbleCount)
        addRandomBubble()
})
```

To finish with the code, we will need to execute the **Animator** function from the LC library.

The **AnimationEasings** collector, will help us to work with some values for the animation process. [You can learn more about this property here](https://www.arction.com/lightningchart-js-api-documentation/v3.4.0/globals.html#animationeasings).

**Note:** The multiplication values will affect the display speed of each bubble.

**bubbleMaxCount** will be the limit of how many bubbles we could create on the chart.

As long as **nextBubbleCount** is less than **bubbleCount**, a new bubble will be created.

---

##NPM Start

Finally, we need to run the _npm start_ command in a new terminal and the chart will be allocated in the local server _http://localhost:8080/_.

Control + click to follow the route. 

![local host](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13mzxurbjiztsxorc16m.png)

Here's our chart:


![Bubble-Chart-with-NodeJS-and-LightningChart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m8iqw6jlu44pf56z7h6d.png)

---

As seen, bubble charts (and generally scatter charts), are great ways to visualize data and compare different variables.

Scattergrams are easy to understand and can be used to quickly identify patterns and trends. 

Essentially, Bubble charts tend to be visually appealing and can be used to highlight specific data points.

Feel free to replicate this tutorial with your own data values.

See you in the next article!

---

**Written by:**
Omar Urbano | Software Engineer
[Find me on LinkedIn](https://www.linkedin.com/in/omarurbanocuellar/)