Scatter plots with D3.js and D3FC.js

I explored various way of drawing scattered plots in D3.js depending on the size of datapoints.

1. What is D3.js

D3.js is a widely-used open-source javascript library made for data visualisation. It provides a low-level toolbox developers can easily combine to create a custom data visualisation. There are lots of community-provided examples available in Observable, and it was primarily written in Vanila javascript and is not easily translatable in React with ease.

2. React with D3.js

Combining D3.js with React has been a tricky job due to the different rendering schemes both libraries provide. After reading Amelia Wattenberger's post about the tips to Reactise D3.js, I decided to implement a scattered plot in React (with NextJS13) on my own to face and understand the difficulty through the actual experience.

3. Different number of data, different strategy

While implementing scatter plots, I realised different technical approaches are taken to visualise data with varying numbers of data points. D3 charts utilise SVG, a retained mode graphical model that is easier but performance-limited, to render the data with up to 10k data points.

However, as the number of data increases upto 10k, it is recommended to render charts using Canvas, which is an immediate mode graphical model.

However, as the number of data increases to 10k, it is recommended to render charts using Canvas, an immediate-mode graphical model. When the number of data points increases to 1M, it is better to use WebGL, which provides Javascript API to use GPU-accelerated graphics. Thankfully, I found this great blog post by Colin Eberhardt that showcases how to integrate D3FC library - a library that enables chart creation with D3 using WebGL.

4. Implementation

I created two example charts that manifests above two strategies when the number of data points exceeds 1K. For each chart, I made the chart zoomable and selectable by adopting D3 Quadtree search.

You may visit the full demo page or get the full code from the links below.

4.1. D3 with Canvas (~10k datapoints)

To implement the example, I used the conventional SVG to draw X and Y domains and overlayed two canvases on top. One canvas is to remove the datapoints, and another one to highlight the selected datapoints.

Fig1. D3 with Canvas

When integrating D3 with React, the easies route you can take is to implement all the code in a single useEffect() hook.

In her blog, Amelia Wattenberger suggests more React-friendly way of refactoring D3 code in her blog post.

It is fully upto you to take your preferred strategy depending on the circumstances you're in, but I tried my best to reduce the code in useEffect() by using other hooks(useRef, useMemo, useCallback)

4.2. D3FC with D3 (~1M datapoints)

The dataset used in this example is Hathi Trust Library Dataset created by Ben Schmidt, and it is the collection of digital library data, which includes the published year, language and author information.

The data is exposed at the endpoint/data.tsv in tsv format and streamed by a web worker and you can find the full worker code I wrote from here.

The loaded data is saved as a state variable in React (defined as "bigData" state in the code), and is bound with the quadTree object and d3 annotation object(data label to show when hovered) to be rendered in the WebGL canvas.

Please note again that the example is a reimplementation of Colin Eberhardt's example in React + Typescript.

5. Key Takeaway

When integrating D3 with React, I faced several difficulties and I wrote this down so that people reading this blog won't make similar mistakes.

  • 1. Make the best use of useRef() hook

    It would help if you had to make the best use of useRef() hook and try to reference the DOM when the first useEffect() is called.

  • 2. Be aware of React hook lifecycle.

    To reduce the repetitive redefinition of the variables or functions, I used lots of useMemo() or useCallback() hook. However, it sometimes causes errors caused by the different order the hooks are called in React's lifecycle.

    React lifecycle
    Fig3. React Lifecycle