I Drew Charts in JavaScript So You Don’t Sweat It

I’m Kayla. I make dashboards for real people with real deadlines. I’ve used a bunch of JavaScript chart tools in the wild—client demos, school fundraisers, even a scrappy sales board on an old iPad. Some made me smile. Some made me sigh. Here’s what stuck.

By the way, I documented the whole journey — from the first scribble to the last polish — in this behind-the-scenes write-up.

And yes, I’ll show real code I ran myself. Nothing fake here.

My Quick Picks (no fuss)

  • Need a chart fast? Chart.js.
  • Need full control? D3.
  • Need pretty charts with rich stuff built in? ECharts.
  • Need strong hovers and exports? Plotly.
  • Using React and want simple? Recharts.

If you’re hunting for a zero-dependency vanilla JS library, EJSChart packs a punch without the bloat.

For an even quicker, no-frills approach, check out my notes on easy JavaScript charts that actually worked for me.

Let me explain.


Chart.js — My “Get It Done” Friend

When I need a chart today, I go here. It’s a canvas chart lib. It’s small. It’s clean. The defaults look good. My PTA treasurer still thanks me for the donut chart I made in under 10 minutes.

Chart.js Official Website provides the authoritative docs, examples, and plugin ecosystem if you want to see everything the library can do.

What I like:

  • Nice defaults. Good colors out of the box.
  • Tooltips “just work.”
  • Plugins exist, but I don’t always need them.

What bugs me:

  • Custom shapes are rough.
  • Super complex charts can feel tight.

Here’s a real line chart I used for weekly sales. It ran in Chrome, Safari, and even an old Android tablet.

<canvas id="salesChart" width="400" height="200"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('salesChart');
new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],
    datasets: [{
      label: 'Sales ($)',
      data: [120, 90, 150, 80, 220, 300, 140],
      borderColor: '#3b82f6',
      backgroundColor: 'rgba(59,130,246,0.15)',
      tension: 0.25,
      pointRadius: 3
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: { display: true },
      tooltip: { mode: 'index', intersect: false }
    },
    scales: {
      y: { beginAtZero: true, grid: { color: '#eee' } },
      x: { grid: { display: false } }
    }
  }
});
</script>

Tip: If the chart feels slow on a phone, set animation: false. Your users won’t mind.

And if you’re curious how Chart.js stacked up against a bunch of other libraries I tested, my field report is right here.


D3 — The Power Tool (bring gloves)

D3 is not a chart lib. It’s a toolkit. You build charts from the ground up. When I needed a custom funnel with stepped colors and odd ticks, D3 saved me. The learning curve? Real. But it’s worth it when you want control.

D3.js Official Website is the go-to source for tutorials, API references, and hundreds of community examples if you’re ready to dig into the toolkit.

What I like:

  • Full control over SVG, scales, axes.
  • Data joins feel smart once they click.

What bugs me:

  • The first week is… rough.
  • You write more code. Like, a lot more.

Here’s a tiny bar chart I wrote for a hiring board. It shows hires by team. Simple, clean.

<svg id="bar" width="420" height="200"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const data = [
  {team: 'Design', hires: 3},
  {team: 'Eng', hires: 8},
  {team: 'Sales', hires: 5},
  {team: 'Ops', hires: 2}
];

const svg = d3.select('#bar');
const width = +svg.attr('width');
const height = +svg.attr('height');
const margin = {top: 20, right: 20, bottom: 30, left: 40};
const w = width - margin.left - margin.right;
const h = height - margin.top - margin.bottom;

const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);

const x = d3.scaleBand()
  .domain(data.map(d => d.team))
  .range([0, w]).padding(0.2);

const y = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.hires)]).nice()
  .range([h, 0]);

g.append('g')
  .attr('transform', `translate(0,${h})`)
  .call(d3.axisBottom(x));

g.append('g')
  .call(d3.axisLeft(y).ticks(5));

g.selectAll('rect')
  .data(data)
  .join('rect')
  .attr('x', d => x(d.team))
  .attr('y', d => y(d.hires))
  .attr('width', x.bandwidth())
  .attr('height', d => h - y(d.hires))
  .attr('fill', '#10b981');
</script>

One more thing: D3 makes custom tooltips, legends, and layout feel like Lego. But you build the Lego.

It was one of the seven libraries I pitted head-to-head—see the full showdown in this comparison.


ECharts — Pretty, Rich, and Fast

When a client says, “Can it look fancy?” I hear “ECharts.” Themes look polished. Legends pop. It handles big data better than I thought, and the tooltip feels lively. I’ve shipped this on TVs in a shop window. No joke.

What I like:

  • Themes and visuals feel pro.
  • Good zoom, good legends, good tooltips.

What bugs me:

  • Docs can feel uneven.
  • Resize bugs if you forget to call resize on window change.

A real pie chart for ad spend:

<div id="adPie" style="width:420px;height:280px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5"></script>
<script>
const chart = echarts.init(document.getElementById('adPie'));
chart.setOption({
  title: { text: 'Ad Spend', left: 'center' },
  tooltip: { trigger: 'item' },
  legend: { bottom: 0 },
  series: [{
    type: 'pie',
    radius: '60%',
    data: [
      {value: 1048, name: 'Search'},
      {value: 735, name: 'Social'},
      {value: 580, name: 'Display'},
      {value: 484, name: 'Video'}
    ],
    emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.2)' } }
  }]
});
window.addEventListener('resize', () => chart.resize());
</script>

Export to PNG? Use the getDataURL method. It works great for slide decks.

If ‘beautiful’ is what you’re after, I dug deeper into making gorgeous charts in this story.


Plotly — Strong Hovers, Quick Science Feels

Plotly shines when I need deep hovers, zoom, and exports without fuss. I used it for a lab-style time series with ± error bands. The hover labels made the demo land.

What I like:

  • Built-in zoom and pan.
  • Export to PNG with one click.

What bugs me:

  • Bundle size is chunky.
  • Styling to match my brand took a bit.

Here’s a real scatter chart with trend-ish lines:

“`html

const trace1 = {
x: [1,2,3,4,5,6],
y: [2,3,2.5,4,3.5,5],
mode: ‘markers+lines’,
name: ‘Series A’,
marker: { color: ‘#ef4444’ }
};
const trace2 = {