Text-to-Chart

Text-to-chart is the process of describing a diagram in text form, and letting software render the actual output. I´ve been investigating the topic by examining Mermaid, a free software library created in 2014 by Knut Sveidqvist with a focus on development related charts, and ECharts, a flexible general-purpose diagramming solution that follows the code-to-chart approach. Unlike Mermaid, which uses a domain-specific language, ECharts is relying on JavaScript to describe the diagrams it renders.

Along the way I couldn´t resist to write two markdown-it plugins, allowing to integrate the charting solutions into your Markdown writing:

This document is my current state of learning the topic. All example charts are rendered by the mentioned plugins.

Text-to-chart, what is it good for?

The idea of creating charts from text is appealing to me since I got aware of Mermaid several years ago. When creating output for the web by writing Markdown, text-to-chart has similar advantages as Markdown.

Could there be problems?

There can be downsides.

Mermaid

Mermaid has its roots in creating development charts from text, like flowcharts, sequence diagrams, and state diagrams. The mind map chart can as well be seen to be in this domain. A strength of Mermaid is the lean syntax for describing the charts, and Sveidqvist´s intent to fight doc-rot is well addressed in my view. There are good ideas in Mermaid, but the actual execution could be more standardized. The different chart types sometimes appear to me as not generated by the same software but by different software stacks.

Mermaid supports class diagrams, but generating them directly from your code base with a different software might be more effective.

Mermaid Flowchart

You can create a flowchart by adding a few lines of text to your Markdown document, like below:

```mermaid
flowchart LR

start --> decision
decision --> optionA & optionB

start(["Start"])
decision{"Decision"}
optionA["Option A"]
optionB["Option B"]
```

The above chart definition will produce the following diagram, and the simplicity of the creation is a source of joy to me.

Start

Decision

Option A

Option B

A basic Mermaid-generated flowchart. I usually start with describing the flows between the nodes and then add a second block to describe how the nodes should be shaped.

The flowchart syntax is powerful and simple. It´s basically nodes and edges to connect nodes, which, as a general concept, can be applied in many communication situations. You get the complete syntax on the Mermaid site at Flowcharts – basic syntax. Visit the Mermaid docs to get an overview of all available diagram types.

Mermaid Radar Chart

Mermaid does also have more exotic chart types, like the radar or the Sankey.

MathScienceEnglishHistoryGeographyArtAliceBob
A Mermaid-generated radar chart, made with a few lines of text
How to embed into Markdown
```mermaid
radar-beta

axis m["Math"], s["Science"], e["English"], h["History"], g["Geography"], a["Art"]
curve a["Alice"]{85, 90, 80, 70, 75, 90}
curve b["Bob"]{70, 75, 85, 80, 90, 85}

max 100
min 0
```

Mermaid Sankey Chart

Agricultural wasteBio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNGasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind
A Sankey chart generated by Mermaid
How to embed into Markdown
```mermaid
---
config:
  sankey:
    showValues: false
---
sankey-beta

Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,NGas,40.719
Gas reserves,NGas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
NGas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```

Mermaid Pie Chart

Other examples are the xy chart or the pie chart. Those charts can be useful, but they are fairly simple and some might find they are not polished enough. The syntax isn´t consistent either. The xy chart requires a title to be in double quotes to avoid having all spaces removed from the title text, while for the pie chart double quotes are supported, but the title would be better without quotes because the quotes are rendered into the diagram. Also, the titles of the two diagram types are rendered in different font sizes. Generally, font sizes vary across the different Mermaid chart types. At the beginning of this document I mentioned standardized output creation from text definitions is a benefit of text-to-chart, but it is not to 100% achieved in this case. It is a probably a minor complaint, depending on your context, but I expect these things to be aligned when using a charting software.

90%6%4%What Voldemort doesn't have?FriendsFamilyNose
A simple pie chart generated by Mermaid
How to embed into Markdown
```mermaid
pie

title What Voldemort doesn't have?
"Friends" : 2
"Family" : 3
"Nose" : 45
```

Mermaid XY Chart

The week in dataMonTueWedThuFriSatSunDays of the week220200180160140120100806040Data
A simple xy chart generated by Mermaid. The bars represent data points for each day of the week. The line represents the cumulated data points from day to day. This visualization is clear as long as you have a single data series to visualize (like only bars, or only the line). When having more than one data series, I´m missing an automatically generated legend to explain the data within the chart. The explanation can only be given in addition to what Mermaid does, for example by putting your own description as some sort of legend into a figcaption, which is not ideal in my view. A legend should really be part of any information graphic.
How to embed into Markdown
```mermaid
xychart-beta

title "The week in data"
x-axis "Days of the week" [Mon, Tue, Wed, Thu, Fri, Sat, Sun]
y-axis "Data"
bar "Per day" [40, 30, 21, 30, 34, 40, 38]
line "Cumulated over days" [40, 70, 91, 121, 155, 195, 233]
```

Mermaid Chart Appearance

The appearance of each Mermaid chart can be configured through a frontmatter configuration within the chart definition, see Frontmatter config in the Mermaid documentation. In addition, a software developer can apply a custom styling to all diagrams by using a Mermaid config object and a theme CSS.

The application of styles to the diagrams is inconsistent, not well documented, and I didn´t find it straight forward. For example, you can set the colors of the bars and lines for the xy chart in the Mermaid config object (which is JSON), but the background of the xy chart needs to be set via the theme CSS. At some point I refrained from doing my own styling and now I just use the default. Unfortunately that means I do not have a dark mode switch, because Mermaid is not supporting an automatic switch by reacting to system settings out of the box, and the font size of the data series legends is too tiny, but I have to accept that.

In the end, the limitations of Mermaid´s general purpose charts made me watch out for other solutions and I came to ECharts.

ECharts

ECharts is a free and capable charting software under the Apache License 2.0. It covers general purpose charts in various flavours and can be used for specific charts that go well beyond of what I would expect to get from a charting software. The charts are rendered on the client and can be interactive in the sense that users are able to filter out certain data series to shift focus on other data series to better analyze the data.

I´m impressed by the ECharts demo page.

Other than Mermaid, ECharts does not rely on text to describe a chart. Instead the chart has to be defined with JavaScript. It´s a steeper learning curve, but the concepts are well thought through, pretty flexible, and the resulting charts can be tailored to certain needs much better than with Mermaid. To directly jump in, let´s pick up some chart types we rendered previously with Mermaid and do the same with ECharts.

ECharts Pie Chart

A simple pie chart rendered by ECharts. You will probably notice it´s more interactive, more polished, and much closer to the styling of the hosting website than the corresponding Mermaid chart.
How to embed into Markdown
```echarts
const config = {
  title: {
    text: 'What Voldemort doesn´t have',
  },
  tooltip: {
    trigger: 'item'
  },
  series: [
    {
      type: 'pie',
      radius: containerWidth > 600 ? '70%' : '50%',
      data: [
        { value: 2, name: 'Friends' },
        { value: 3, name: 'Family' },
        { value: 45, name: 'Nose' }
      ],
      label: {
        show: true,
        formatter: containerWidth > 600 ? '{b}: {c} ≈ {d}%' : '{b} {c}'
        // {b} = data name
        // {c} = raw value
        // {d} = percentage
      }
  }
  ]
};
```

ECharts XY Chart

The ECharts version of the xy chart, which is named a combined line/bar chart in ECharts. It´s easy to have a proper legend for the visualized data, and again, the chart is interactive.
How to embed into Markdown
```echarts
const config = {
  title: {
    text: 'The week in data',
  },
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    name: containerWidth > 600 ? 'Days of the week' : ''
  },
  yAxis: {
    type: 'value',
    name: containerWidth > 600 ? 'Data' : ''
  },
  legend: {
    data: ['Per day', 'Cumulated over days']
  },
  series: [
      {
      name: 'Per day',
      data: [40, 30, 21, 30, 34, 40, 38],
      type: 'bar'
    },
    {
      name: 'Cumulated over days',
      data: [40, 70, 91, 121, 155, 195, 233],
      type: 'line'
    }
  ]
};
```

ECharts Radar Chart

The radar chart, generated by ECharts
How to embed into Markdown
```echarts
const config = {
  legend: {
    data: ['Alice', 'Bob']
  },
  radar: {
     splitLine: {
      show: false
    },
    splitArea: {
      show: false
    },
    indicator: [
      { name: 'Math', max: 100 },
      { name: 'Science', max: 100 },
      { name: 'English', max: 100 },
      { name: 'History', max: 100 },
      { name: 'Geography', max: 100 },
      { name: 'Art', max: 100 }
    ]
  },
  series: [
    {
      type: 'radar',
      data: [
        {
          value: [85, 90, 80, 70, 75, 90],
          name: 'Alice'
        },
        {
          value: [70, 75, 85, 80, 90, 85],
          name: 'Bob'
        }
      ]
    }
  ]
};
```

ECharts Sankey Chart

The Sankey chart, generated by ECharts
How to embed into Markdown
```charts
const config = {
  tooltip: {
    trigger: "item",
    triggerOn: "mousemove",
  },
  animation: true,
  series: [
    {
      type: "sankey",
      bottom: "10%",
      emphasis: {
        focus: "adjacency",
      },
      data: [
        {
          name: "Agricultural 'waste'",
        },
        { name: "Bio-conversion" },
        { name: "Biofuel imports" },
        { name: "Biomass imports" },
        { name: "Coal imports" },
        { name: "Coal reserves" },
        { name: "Coal" },
        {
          name: "District heating",
        },
        {
          name: "Electricity grid",
        },
        { name: "Gas imports" },
        { name: "Gas reserves" },
        {
          name: "Gas",
        },
        { name: "Geothermal" },
        { name: "H2 conversion" },
        { name: "H2" },
        { name: "Hydro" },
        { name: "Liquid" },
        { name: "Marine algae" },
        { name: "NGas" },
        { name: "Nuclear" },
        { name: "Oil imports" },
        { name: "Oil reserves" },
        { name: "Oil" },
        { name: "Other waste" },
        {
          name: "Pumped heat",
        },
        { name: "Solar PV" },
        {
          name: "Solar Thermal",
        },
        { name: "Solar" },
        {
          name: "Thermal generation",
        },
        { name: "Tidal" },
        {
          name: "UK land based bioenergy",
        },
        { name: "Wave" },
        { name: "Wind" },
      ],
      links: [
        {
          source: "Agricultural 'waste'",
          target: "Bio-conversion",
          value: 124.729,
        },
        { source: "Bio-conversion", target: "Liquid", value: 0.597 },
        { source: "Bio-conversion", target: "Losses", value: 26.862 },
        { source: "Bio-conversion", target: "Solid", value: 280.322 },
        { source: "Bio-conversion", target: "Gas", value: 81.144 },
        { source: "Biofuel imports", target: "Liquid", value: 35 },
        { source: "Biomass imports", target: "Solid", value: 35 },
        { source: "Coal imports", target: "Coal", value: 11.606 },
        { source: "Coal reserves", target: "Coal", value: 63.965 },
        { source: "Coal", target: "Solid", value: 75.571 },
        { source: "District heating", target: "Industry", value: 10.639 },
        {
          source: "District heating",
          target: "Heating and cooling - commercial",
          value: 22.505,
        },
        {
          source: "District heating",
          target: "Heating and cooling - homes",
          value: 46.184,
        },
        {
          source: "Electricity grid",
          target: "Over generation / exports",
          value: 104.453,
        },
        {
          source: "Electricity grid",
          target: "Heating and cooling - homes",
          value: 113.726,
        },
        { source: "Electricity grid", target: "H2 conversion", value: 27.14 },
        { source: "Electricity grid", target: "Industry", value: 342.165 },
        { source: "Electricity grid", target: "Road transport", value: 37.797 },
        { source: "Electricity grid", target: "Agriculture", value: 4.412 },
        {
          source: "Electricity grid",
          target: "Heating and cooling - commercial",
          value: 40.858,
        },
        { source: "Electricity grid", target: "Losses", value: 56.691 },
        { source: "Electricity grid", target: "Rail transport", value: 7.863 },
        {
          source: "Electricity grid",
          target: "Lighting & appliances - commercial",
          value: 90.008,
        },
        {
          source: "Electricity grid",
          target: "Lighting & appliances - homes",
          value: 93.494,
        },
        { source: "Gas imports", target: "NGas", value: 40.719 },
        { source: "Gas reserves", target: "NGas", value: 82.233 },
        {
          source: "Gas",
          target: "Heating and cooling - commercial",
          value: 0.129,
        },
        { source: "Gas", target: "Losses", value: 1.401 },
        { source: "Gas", target: "Thermal generation", value: 151.891 },
        { source: "Gas", target: "Agriculture", value: 2.096 },
        { source: "Gas", target: "Industry", value: 48.58 },
        { source: "Geothermal", target: "Electricity grid", value: 7.013 },
        { source: "H2 conversion", target: "H2", value: 20.897 },
        { source: "H2 conversion", target: "Losses", value: 6.242 },
        { source: "H2", target: "Road transport", value: 20.897 },
        { source: "Hydro", target: "Electricity grid", value: 6.995 },
        { source: "Liquid", target: "Industry", value: 121.066 },
        { source: "Liquid", target: "International shipping", value: 128.69 },
        { source: "Liquid", target: "Road transport", value: 135.835 },
        { source: "Liquid", target: "Domestic aviation", value: 14.458 },
        { source: "Liquid", target: "International aviation", value: 206.267 },
        { source: "Liquid", target: "Agriculture", value: 3.64 },
        { source: "Liquid", target: "National navigation", value: 33.218 },
        { source: "Liquid", target: "Rail transport", value: 4.413 },
        { source: "Marine algae", target: "Bio-conversion", value: 4.375 },
        { source: "NGas", target: "Gas", value: 122.952 },
        { source: "Nuclear", target: "Thermal generation", value: 839.978 },
        { source: "Oil imports", target: "Oil", value: 504.287 },
        { source: "Oil reserves", target: "Oil", value: 107.703 },
        { source: "Oil", target: "Liquid", value: 611.99 },
        { source: "Other waste", target: "Solid", value: 56.587 },
        { source: "Other waste", target: "Bio-conversion", value: 77.81 },
        {
          source: "Pumped heat",
          target: "Heating and cooling - homes",
          value: 193.026,
        },
        {
          source: "Pumped heat",
          target: "Heating and cooling - commercial",
          value: 70.672,
        },
        { source: "Solar PV", target: "Electricity grid", value: 59.901 },
        {
          source: "Solar Thermal",
          target: "Heating and cooling - homes",
          value: 19.263,
        },
        { source: "Solar", target: "Solar Thermal", value: 19.263 },
        { source: "Solar", target: "Solar PV", value: 59.901 },
        { source: "Solid", target: "Agriculture", value: 0.882 },
        { source: "Solid", target: "Thermal generation", value: 400.12 },
        { source: "Solid", target: "Industry", value: 46.477 },
        {
          source: "Thermal generation",
          target: "Electricity grid",
          value: 525.531,
        },
        { source: "Thermal generation", target: "Losses", value: 787.129 },
        {
          source: "Thermal generation",
          target: "District heating",
          value: 79.329,
        },
        { source: "Tidal", target: "Electricity grid", value: 9.452 },
        {
          source: "UK land based bioenergy",
          target: "Bio-conversion",
          value: 182.01,
        },
        { source: "Wave", target: "Electricity grid", value: 19.013 },
        { source: "Wind", target: "Electricity grid", value: 289.366 },
      ],
      label: {
        position: "top",
        fontSize: 10,
        lineHeight: 12,
        align: 'left'
      },
      nodeGap: 20,
      lineStyle: {
        color: "source",
        curveness: 0.5,
      },
    },
  ],
};
```

ECharts Large Area Chart

The large area chart, with its level of interaction, is a case where ECharts shines. I add it here to highlight what ECharts can do.

A large area chart, generated by ECharts
How to embed into Markdown
```echarts
let base = +new Date(1968, 9, 3);
let oneDay = 24 * 3600 * 1000;
let date = [];
let data = [Math.random() * 300];
for (let i = 1; i < 20000; i++) {
  var now = new Date((base += oneDay));
  date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'));
  data.push(Math.round((Math.random() - 0.5) * 20 + data[i - 1]));
}
const config = {
  tooltip: {
    trigger: 'axis',
    position: function (pt) {
      return [pt[0], '10%'];
    }
  },
  title: {
    left: 'center',
    text: 'Large Area Chart'
  },
  toolbox: {
    feature: {
      dataZoom: {
        yAxisIndex: 'none'
      },
      restore: {},
      saveAsImage: {}
    }
  },
  xAxis: {
    type: 'category',
    boundaryGap: false,
    data: date
  },
  yAxis: {
    type: 'value',
    boundaryGap: [0, '100%']
  },
  dataZoom: [
    {
      type: 'inside',
      start: 0,
      end: 10
    },
    {
      start: 0,
      end: 10
    }
  ],
  series: [
    {
      name: 'Fake Data',
      type: 'line',
      symbol: 'none',
      sampling: 'lttb',
      itemStyle: {
        color: 'rgb(255, 70, 131)'
      },
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          {
            offset: 0,
            color: 'rgb(255, 158, 68)'
          },
          {
            offset: 1,
            color: 'rgb(255, 70, 131)'
          }
        ])
      },
      data: data
    }
  ]
};
```

Conclusion

Mermaid works well and allows to be quick with charts that fit into the software development domain. Though it is possible to influence the styling of the diagrams, it´s not straight-forward enough in my view so that I refrain from doing any custom styling and instead use the default Mermaid styles. I enjoy getting a proper flowchart from a few lines of text and being able to change the chart with ease. This is a good example for the power of text-to-chart when the chart syntax fits its context.

ECharts is my go-to choice for general purpose charts. The concepts are powerful yet simple enough, the results are beautiful, interactive, and can be styled to a certain look, including a dark-mode switch. The ECharts approach of enhancing accessibility by the automatic generation of aria-labels from the data shown is a smart move and a big plus on the feature list. I´m impressed of what is doable with this charting library.