I tried three open-source JavaScript Gantt charts. Here’s what actually worked.

I needed a free Gantt chart for two real projects. One was a weekend hack night. The other was a school play schedule. I tested Frappe Gantt, dhtmlxGantt (GPL), and jsGanttImproved. I built real pages. I pushed them hard. I broke a few things. Then I fixed them.
If you’d like to see every benchmark screenshot and heap profile from that test run, I tucked them into a full lab notebook right here.

You know what? I have clear winners for each use case.

Project one: a small status board with Frappe Gantt (MIT)

I made a one-page status board for a volunteer sprint. Plain HTML. Vite build. No framework. I wanted fast setup and a clean look.

Here’s the exact code I used to get it on the page:

<div id="gantt"></div>
<script type="module">
  import Gantt from 'frappe-gantt';

  const tasks = [
    {
      id: 'DESIGN',
      name: 'Design mockups',
      start: '2025-03-01',
      end: '2025-03-04',
      progress: 40,
      dependencies: '',
      custom_class: 'bar-design'
    },
    {
      id: 'API',
      name: 'Build API',
      start: '2025-03-03',
      end: '2025-03-10',
      progress: 20,
      dependencies: 'DESIGN',
      custom_class: 'bar-api'
    },
    {
      id: 'QA',
      name: 'QA and polish',
      start: '2025-03-08',
      end: '2025-03-12',
      progress: 0,
      dependencies: 'API'
    }
  ];

  const gantt = new Gantt('#gantt', tasks, {
    view_mode: 'Week',
    date_format: 'YYYY-MM-DD',
    on_click: task => console.log('clicked', task.id),
    on_date_change: (task, start, end) => console.log('moved', task.id, start, end),
    custom_popup_html: task => `
      <div class="popup">
        <h3>${task.name}</h3>
        <p>${task.start} → ${task.end}</p>
        <p>Progress: ${task.progress}%</p>
      </div>`
  });
</script>

<style>
  #gantt { max-width: 900px; margin: 20px auto; }
  .bar-design { fill: #4f46e5; }
  .bar-api { fill: #059669; }
  .gantt .bar.overdue { stroke: #dc2626; stroke-width: 2px; }
</style>

What I liked:

  • It took me about 90 minutes from zero to pretty. That’s quick.
  • The bars look good out of the box. The popup is easy to tweak.
  • The API is simple. I could teach it in one coffee break.

What bugged me:

  • With 400+ tasks, the scroll got laggy on my old laptop.
  • Month view labels got cramped. I had to bump font size and spacing.
  • No built-in critical path. No resource calendar. That’s fine for small stuff.

React note: I also wired it into a tiny React page. I had to use a ref and rebuild on prop changes. It worked, but it re-renders the whole chart. Here’s the piece I used:

import { useEffect, useRef } from 'react';
import Gantt from 'frappe-gantt';

export default function SprintGantt({ tasks, view = 'Week' }) {
  const el = useRef(null);
  const ganttRef = useRef(null);

  useEffect(() => {
    if (!el.current) return;
    if (ganttRef.current) el.current.innerHTML = '';
    ganttRef.current = new Gantt(el.current, tasks, { view_mode: view });
  }, [tasks, view]);

  return <div ref={el} style={{ maxWidth: 900, margin: '0 auto' }} />;
}

So, small project? Frappe Gantt felt smooth and light. It did the job.

Project two: heavier needs with dhtmlxGantt (GPL)

For a client, I had real PM needs. Links. Baselines. Drag and drop. Keyboard moves. Big data. I reached for dhtmlxGantt. It’s free under GPL, which means your app also needs to be GPL if you ship it as part of a product. That part matters. We were fine, since it was an internal tool.

Setup felt like a real tool. More knobs. More power.
I broke out an even more detailed, code-heavy walkthrough of this library—including auto-scheduling edge cases—in my hands-on review over here.

<div id="gantt_here" style="width:100%; height:500px;"></div>
<script>
  gantt.config.xml_date = "%Y-%m-%d";
  gantt.config.columns = [
    { name: "text", label: "Task", width: 200, tree: true },
    { name: "start_date", label: "Start", align: "center" },
    { name: "duration", label: "Days", align: "center" },
    { name: "add", label: "", width: 44 }
  ];

  gantt.plugins({ tooltip: true, auto_scheduling: true });

  gantt.init("gantt_here");

  gantt.parse({
    data: [
      { id:1, text:"Design", start_date:"2025-03-01", duration:4, progress:0.4 },
      { id:2, text:"API", start_date:"2025-03-05", duration:6, progress:0.2, parent:0 },
      { id:3, text:"QA", start_date:"2025-03-11", duration:3, parent:0 }
    ],
    links: [
      { id:1, source:1, target:2, type:"0" }, // finish-to-start
      { id:2, source:2, target:3, type:"0" }
    ]
  });

  // Baseline example
  gantt.addTaskLayer(function drawBaseline(task) {
    if (!task.baseline_start || !task.baseline_end) return false;
    const sizes = gantt.getTaskPosition(task, task.baseline_start, task.baseline_end);
    const el = document.createElement('div');
    el.className = 'baseline';
    el.style.left = sizes.left + 'px';
    el.style.width = sizes.width + 'px';
    el.style.top = sizes.top + gantt.config.bar_height + 6 + 'px';
    return el;
  });
</script>

<style>
  .baseline { position: absolute; height: 6px; background: #c084fc; border-radius: 3px; opacity: .6; }
</style>

What I liked:

  • Linking tasks by dragging felt great. Super clear.
  • It handled 1,200 tasks on my 2019 MacBook Air without drama.
  • I liked the columns, keyboard support, and auto scheduling.
  • Tooltips and baselines were clean to set up.

What bugged me:

  • GPL is not a fit for every product. If you ship closed source, this is a no.
  • The CSS is heavy. I spent time to make it match our brand.
  • It took me longer to learn the config. The docs are good, though.

If you need heavy PM features, this one’s the grown-up in the room.

Quick mention: jsGanttImproved

I used this for a school play schedule. Cast, rehearsals, props. Nothing fancy. It felt old-school, but it loaded fast and did the basics.
In case you’re hunting specifically for cost-free options, I cataloged every truly free Gantt library I could find—including jsGanttImproved—in a roundup you can skim here.

What worked:

  • No build step needed. Drop files, call a function.
  • Good for read-only plans and simple dates.

What didn’t:

  • Styling took more work.
  • Features are basic. I missed nice tooltips and smooth drag.

If you ever outgrow these options and want a broader charting toolkit that still supports timelines, EJSChart is a flexible alternative worth bookmarking. For other visualization types beyond timelines, you can also check my comparison of open-source chart tools here.

Real hiccups I hit (and fixed)

  • Time zones: The first day slipped by one day for a user in a DST zone. I fixed it by sending ISO dates (YYYY-MM-DD) and letting the chart parse them. No local Date math in my own code.
  • Mobile: Touch targets were tiny near the chart edges. I added 6–8px padding around bars
Published
Categorized as Vibe Coding

I Tried a Bunch of High-Performance JavaScript Charts. Here’s What Actually Felt Fast.

Hi, I’m Kayla. I make charts for a living. And for fun. I know, that’s a little nerdy. But I like when data moves smooth, and the page doesn’t hiccup. If you want the blow-by-blow numbers, my extended benchmark write-up lives here.

This past month I tested a bunch of chart libraries. I wanted speed. Smooth zoom. No weird stutter when I pan. I focused on the heavyweight contenders—the so-called “big” JavaScript chart tools—which I also covered in a separate post here.

Some tools surprised me. Some… didn’t. If you’re shopping for a broader roundup of popular libraries, you might like the notes I kept here. For an even deeper, third-party look at the landscape, check out this comprehensive comparison of JavaScript charting libraries, detailing features, performance, and use cases.

Let me explain. By the way, I dug through a bunch of white-papers on rendering pipelines at EJSChart and they shaped some of the test ideas you’ll see below.

My setup (so you know I’m not guessing)

  • Laptop 1: MacBook Pro (M1 Pro), 16GB RAM, Chrome.
  • Laptop 2: ThinkPad T14, 32GB RAM, Edge.
  • Screen: 60 Hz. Nothing fancy.

I ran each chart on both. I watched frame rate using Chrome’s perf tools. Mostly, I looked at feel. Does it drag? Does my fan spin? Does the UI freeze when I zoom?

What I charted (real stuff, not just fake lines)

  • 1,000,000 points of sensor-style data (sine wave with noise).
  • 120,000 minute stock prices (two years, one symbol).
  • 5,000 bars for sales by SKU (gross, but it happens).
  • A small dashboard with 6 charts live at once.

You know what? Big line charts are where most tools break a sweat.


Chart.js — friendly face, okay speed

I like Chart.js for small to mid charts. It feels cozy. The docs are clear. I used it in a school dashboard last spring.

  • With 120k points, the decimation plugin helped. Use “decimation: enabled, algorithm: lttb” and “parsing: false.”
  • Panning was okay. Zoom was a bit choppy. I got maybe 25–35 fps when moving fast.
  • With a million points, it crawled or froze. Decimation still helped, but it wasn’t happy.

Quirk: clicking legend items with lots of data can lag.

Verdict: great for most apps, not my pick for huge streams. For more on how other open-source chart packages stacked up, check out my dedicated comparison here.


Apache ECharts — fast and pretty, with tricks that matter

ECharts surprised me. It looks good right out of the box. The “large” mode and “progressive” settings saved the day.

What worked:

  • Line series with “showSymbol: false.”
  • “sampling: lttb,” “large: true,” and “progressive: 4000.”
  • Tooltips stayed usable up to 200k points.

Feel:

  • 120k points: smooth pan and zoom, around 45–50 fps.
  • 1M points: usable with the settings above, about 15–20 fps, and it didn’t freeze.

Quirk: I saw a tiny tooltip flicker once under heavy load. Not a dealbreaker.

Verdict: fast, stylish, and easier than rolling your own.


uPlot — silly fast for time series

If I only need line charts, uPlot is a rocket. It’s tiny and uses canvas. Not flashy, but wow.

  • 120k points: 55–60 fps while panning hard.
  • 1M points: stayed interactive, around 18–22 fps.
  • Memory stayed calm. Typed arrays help here.

It doesn’t come with every widget. You might add your own legend or date controls. But the core is quick and clean.

Verdict: my go-to for big time series when I want speed and I don’t need fancy shapes.


LightningChart JS — the speed champ (but bring your wallet)

This one is commercial. WebGL all the way. I used it for a short-term IoT demo with a gnarly stream.

  • 120k points: very smooth at 60 fps.
  • 1M points: still smooth, and zoom felt crisp.
  • Multiple charts on one page? It held up best of all.

It’s pricey, and licensing is a bit of a process. But if you need real-time charts with zero drama, this is the tank.

Verdict: best raw speed I saw. Great for trading, sensors, or labs.


D3 + Canvas — fast if you build it right

I love D3 for control. But SVG slows with big data. I switched to canvas for the draw calls.

  • With canvas, 120k points ran smooth, about 50–60 fps.
  • With 1M, I got 12–18 fps, and it stayed stable.
  • You do more work yourself. Axes. Zoom. Hit tests. All on you.

Tip: render lines on canvas, but keep axes and UI in DOM. It feels neat and still runs fast. If you want a deeper dive into my D3 canvas workflow, the full breakdown is here.

Verdict: great if you need custom rules or weird scales, and you don’t mind elbow grease.


Plotly.js — rich features, slower with huge data

Plotly is easy to explore with. You click, you get info. 3D. Stats. It’s loaded.

  • Up to ~50k points, it’s fine.
  • At 120k, panning dropped to about 8–15 fps.
  • With 1M, the tab groaned or locked up.

For dashboards with modest sizes, it shines. For giant series, not so much. I also spent time with Google Charts; spoiler: it’s fine for small dashboards but struggled in the same places—full notes here.

Verdict: choose it for features and quick wins, not for monster sets.


Recharts (React) — nice dev flow, not a big-data tool

I used Recharts in a React app for a sales team. It’s smooth for small stuff.

  • 5,000 bars? It worked, but hover felt laggy.
  • 120k points: panning was 2–5 fps. Not fun.
  • It’s SVG-based. That’s the limit.

Verdict: comfy for business charts, not made for heavy loads.


How they stacked up for me

These numbers mirror the condensed “7-library shoot-out” I published here.

Big line chart, 120k points (panning/zooming feel):

  • LightningChart JS: smooth 60 fps
  • uPlot: 55–60 fps
  • ECharts (progressive/large/sampling): 45–50 fps
  • D3 + Canvas: 50–60 fps
  • Chart.js (decimation): 25–35 fps
  • Plotly: 8–15 fps
  • Recharts: 2–5 fps

At 1,000,000 points:

  • LightningChart JS: ~45–60 fps
  • uPlot: ~18–22 fps
  • ECharts (large+progressive): ~15–20 fps
  • D3 + Canvas: ~12–18 fps
  • Chart.js: ~1–3 fps or freeze
  • Plotly: often unresponsive
  • Recharts: nope

Note: frame rates vary with your hardware and what else is on the page.

If you care about raw numbers and methodology, there’s also an in-depth analysis of high-performance JavaScript chart libraries, including benchmarks and performance tests that’s worth a read.


Bugs and quirks I hit

  • Chart.js: legend toggles hang with giant data.
  • ECharts: saw a brief tooltip flicker under heavy load.
  • uPlot: fewer built-ins; I wrote my own tooltip to match brand style.
  • LightningChart: licensing setup took time; docs are fine, just more steps.
  • Plotly: double-click to reset axes lagged with big sets.
  • Recharts: frequent re-renders in React can hurt; memoization helps a bit.

Small

Published
Categorized as Vibe Coding

I make charts with JavaScript. Here’s what actually worked for me.

I build dashboards for small teams and school projects. I also help my cousin run a tiny coffee shop site. So yes, I live in charts. I’ve used Chart.js, D3.js, ECharts, Highcharts, ApexCharts, and Plotly.js.
Lately I've also been testing the lean but capable EJSChart, which lets me stand up bar, line, and pie charts in minutes without extra bloat.
Some days they’re friendly. Some days they’re moody. You know what? That’s normal.

If you prefer the long-form story with more screenshots, I broke it down in this field report on making charts with vanilla JavaScript right here.

Let me walk you through what I liked, what bugged me, and real bits of code that I used.

What I need from a chart, plain and simple

  • Looks good without a lot of fuss.
  • Loads fast on phones.
  • Tooltips that don’t glitch.
  • Can theme to match brand colors.
  • Works with a screen reader, or at least doesn’t block it.

For developers chasing raw rendering speed on low-power devices, my benchmark notes on high-performance JavaScript chart libraries might help—have a peek at them in this deep dive.

That’s my short list. Not fancy. Just real.

Chart.js — my “get it done by lunch” buddy

For quick work, Chart.js feels like a friend. I used it for weekly coffee sales. Time was tight. The boss (my cousin) wanted bars by noon. We made it.

Example I shipped:

<canvas id="sales" height="120"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('sales').getContext('2d');

new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],
    datasets: [{
      label: 'Cups Sold',
      data: [34, 28, 41, 39, 45, 60, 52],
      backgroundColor: '#4e79a7'
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: { display: false },
      tooltip: {
        callbacks: {
          // I showed price per day at $3.50 per cup
          label: c => `$${(c.parsed.y * 3.5).toFixed(2)} revenue`
        }
      }
    },
    scales: { y: { beginAtZero: true } }
  }
});
</script>

What I loved:

  • Clean look right away.
  • Good docs.
  • Canvas is smooth with small to medium data.

What bugged me:

  • With thousands of points, it can feel slow.
  • Custom legends and advanced stuff take more code.

Still, if someone says “we need a chart today,” this is where my hands go.

For even more context, I compared Chart.js with several of its peers in a hands-on roundup, noting wins and misses, which you can read here.

D3.js — powerful, but bring snacks

I used D3 for a school fitness report. We tracked a heart rate line over time. I needed custom ticks and a highlight band for the target zone. D3 let me do all that. But it’s more like building a bike than riding one.

Tiny example I used as a base:

<svg id="hr" width="420" height="200"></svg>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
const data = [
  { t: 0, bpm: 72 }, { t: 1, bpm: 88 }, { t: 2, bpm: 110 },
  { t: 3, bpm: 124 }, { t: 4, bpm: 118 }, { t: 5, bpm: 95 }
];

const svg = d3.select('#hr');
const w = +svg.attr('width'), h = +svg.attr('height');
const m = { top: 10, right: 20, bottom: 25, left: 30 };
const x = d3.scaleLinear().domain(d3.extent(data, d => d.t)).range([m.left, w - m.right]);
const y = d3.scaleLinear().domain([60, 140]).range([h - m.bottom, m.top]);

svg.append('g')
  .attr('transform', `translate(0,${h - m.bottom})`)
  .call(d3.axisBottom(x).ticks(6));

svg.append('g')
  .attr('transform', `translate(${m.left},0)`)
  .call(d3.axisLeft(y).ticks(5));

const line = d3.line().x(d => x(d.t)).y(d => y(d.bpm));
svg.append('path')
  .datum(data)
  .attr('fill', 'none')
  .attr('stroke', '#e15759')
  .attr('stroke-width', 2)
  .attr('d', line);
</script>

What I loved:

  • Full control. Every tick, every color, every weird shape.
  • Great for teaching and data stories.

What bugged me:

  • Long ramp.
  • With big data in SVG, it slows down. I switch to canvas when points get heavy.

If you need a chart that doesn’t exist yet, D3 is your tool belt.

If open-source flexibility is your thing, you might enjoy my candid notes on a handful of other community-driven chart tools, available at this link.

ECharts — great defaults, dashboard friendly

I used ECharts for a city weather panel. Line charts, a tiny map, and a donut. It looked “wow” fast. Mobile pan and zoom felt smooth, too.

Real snippet I used:

<div id="temp" style="height:260px"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5"></script>
<script>
const chart = echarts.init(document.getElementById('temp'));
const option = {
  title: { text: 'Daily Temp' },
  tooltip: { trigger: 'axis' },
  xAxis: { type: 'category', data: ['Mon','Tue','Wed','Thu','Fri'] },
  yAxis: { type: 'value' },
  series: [{ type: 'line', data: [68, 71, 75, 78, 73], smooth: true, areaStyle: {} }]
};
chart.setOption(option);
window.addEventListener('resize', () => chart.resize());
</script>

What I loved:

  • Stylish themes.
  • Smooth zoom and tooltips.
  • Big data feels okay.

What bugged me:

  • The build is chunky. On slow phones, first load takes a bit.
  • Docs are huge; you can get lost.

Still, for dashboards with many charts, ECharts treats you well.

Speaking of circular visuals, I also documented my wins and fails while chasing a smooth animated pie chart in vanilla JavaScript—see the breakdown here.

Highcharts, ApexCharts, and Plotly — quick takes

Highcharts

  • I used it for a fund report with export buttons. PNG and CSV worked great.
  • Clear look, good legends, nice hover.
  • Paid for business use. Keep that in mind.

ApexCharts

  • I used it inside React. The wrapper was simple.
  • Nice defaults for line and area.
  • Some odd spacing with long labels, but fixable.

Plotly.js

  • I used it for a science lab plot.
  • Strong on zoom, hover compare, and saving images.
  • Bundle can feel heavy for tiny pages.

Here’s a tiny Highcharts example I sent to a client:

<div id="hc" style="height:240px"></div>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script>
Highcharts.chart('hc', {
  title: { text: 'Quarterly Score' },
  xAxis: { categories: ['Q1','Q2','Q3','Q4'] },
  series: [{ name: 'Score', data: [72, 78, 81, 86], color: '#59a14f' }],
  credits: { enabled: false }
});
</script>

And if you ever need finance-style candlestick visuals, I stress-tested seven different JavaScript options and posted the raw results in this write-up.

For an additional perspective, ApexCharts maintains a concise JavaScript chart comparison table you can skim, and the folks at Cybrosys share a helpful [comparison of popular JavaScript charting libraries](https

Published
Categorized as Vibe Coding

Easy JavaScript Charts: What Actually Worked For Me

Hi, I’m Kayla. I build small dashboards for real people—teachers, shop owners, and my own messy side projects. I’ve tried a bunch of chart tools in JavaScript. Some felt smooth. Some made me sigh. Here’s what I used, what I liked, and where I got stuck.
For the fuller origin story—including the weekend I almost gave up—read my recap, Easy JavaScript Charts: What Actually Worked For Me.


What I needed (and why it mattered)

  • Fast start, like “10 minutes before a meeting” fast
  • Pretty defaults that don’t need a design degree
  • Tooltips, legends, and responsive charts on phones
  • Can handle live data without choking
  • Clear docs when I’m tired and on coffee number two

Seems simple, right? It wasn’t always.


Chart.js: My friendly starter

Chart.js was the first one that clicked for me. It gave me clean charts with little setup. The defaults look nice. And it works on phones without me fighting CSS.

Pro tip: the full API reference lives in the official documentation—Chart.js Docs—and it’s worth scanning when you need custom ticks.

Here’s a real bar chart I used for a local bake sale report:

<canvas id="sales"></canvas>
<script>
  // assume Chart.js is loaded
  const ctx = document.getElementById('sales').getContext('2d');

  const chart = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
      datasets: [{
        label: 'Cupcakes Sold',
        data: [12, 19, 8, 14, 22, 28, 17],
        backgroundColor: '#6C9EEB'
      }]
    },
    options: {
      responsive: true,
      plugins: {
        legend: { position: 'bottom' },
        tooltip: { enabled: true }
      },
      scales: {
        y: { beginAtZero: true }
      }
    }
  });
</script>

What I loved:

  • It looked good right away.
  • The legend and tooltip worked without fuss.
  • The docs were clear enough for a tired brain.

What bugged me:

  • Big data made it slow for me (10k points felt sticky).
  • Some plugin stuff got fiddly.
  • Custom ticks took a few tries to get right.

Would I use it again? For small to mid charts, yes. It’s my “safe choice.”
I wrote an even deeper Chart.js breakdown in I Make Charts With JavaScript—Here’s What Actually Worked For Me.


ApexCharts: The slick one with nice extras

ApexCharts felt fancy and smooth. A lot of the polish comes from the built-in goodies tucked under the aptly named features page, so I didn’t have to reinvent hover states or animations. I used it for a real-time line chart at a school science night. Kids watched air sensor data move on screen. It won them over. It won me over too.

Here’s a simple area chart I used in a site traffic panel:

<div id="area"></div>
<script>
  // assume ApexCharts is loaded
  const options = {
    chart: { type: 'area', height: 280, animations: { enabled: true } },
    series: [
      { name: 'Visits', data: [31, 40, 28, 51, 42, 109, 100] }
    ],
    xaxis: { categories: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'] },
    fill: {
      type: 'gradient',
      gradient: { shadeIntensity: 0.4, opacityFrom: 0.7, opacityTo: 0.2 }
    },
    dataLabels: { enabled: false },
    stroke: { curve: 'smooth' }
  };

  const chart = new ApexCharts(document.querySelector('#area'), options);
  chart.render();
</script>

What felt great:

  • Tooltips and legends looked polished.
  • Animations were smooth without me tuning a ton.
  • Real-time updates were easy. I just pushed points.

What I didn’t love:

  • Bundle felt heavier than I wanted for small pages.
  • Matching my exact brand styles took time.
  • A few color themes leaned a bit “flashy” for serious reports.

My take: when I want pretty and lively with less fuss, this works.
If you’re comparing the free chart contenders side-by-side, my field notes in I Tried Free JavaScript Charts So You Don’t Have To might help.


ECharts: The powerhouse for big data

When I had a giant set (city bus GPS points—don’t ask), Chart.js tapped out. ECharts handled it. The charts felt fast even with many points. The config is large, but it’s worth it for heavy loads.

This is an actual line chart I used for 25k points (downsampled in code, still hefty):

<div id="bigLine" style="height:320px;"></div>
<script>
  // assume ECharts is loaded
  const chart = echarts.init(document.getElementById('bigLine'));
  const data = new Array(25000).fill(0).map((_, i) => [i, Math.sin(i/200) + (Math.random()*0.2)]);

  const option = {
    tooltip: { trigger: 'axis' },
    xAxis: { type: 'value' },
    yAxis: { type: 'value' },
    series: [{
      type: 'line',
      data,
      showSymbol: false,
      lineStyle: { width: 1 }
    }]
  };

  chart.setOption(option);
</script>

What I liked:

  • It handled large data without choking.
  • So many chart types: maps, heatmaps, candlesticks.
  • Zoom and pan felt smooth.

What wore me out:

  • The config object was long. Lots of properties.
  • Some doc parts felt a bit stiff or mixed in tone.
  • Styling took me a few passes to nail.

Use it when you have “wow, that’s a lot of data.”
I rounded up every library I could cram into a weekend in I Tried a Bunch of JavaScript Chart Libraries—Here’s What Actually Worked for Me. Spoiler: open-source tools came out ahead in I Tried a Bunch of Open-Source JavaScript Chart Tools—Here’s What Actually Worked.


A plain JS quickie: a tiny bar chart I use in emails

Sometimes I don’t need a full library. For a simple report email or a tight modal, I use a tiny DOM bar chart. It’s not perfect, but it’s fast and cute.

<div id="mini"></div>
<style>
  .bar { display:inline-block; width:20px; margin-right:6px; background:#8FD694; vertical-align:bottom; }
  .wrap { height:120px; border-left:1px solid #ccc; border-bottom:1px solid #ccc; padding:6px; }
</style>
<script>
  const data = [5, 12, 7, 9, 3, 15, 10];
  const max = Math.max(...data);

  const wrap = document.createElement('div');
  wrap.className = 'wrap';

  data.forEach(v => {
    const b = document.createElement('div');
    b.className = 'bar';
    b.style.height = (v / max * 100) + '%';
    wrap.appendChild(b);
  });

  document.getElementById('mini').appendChild(wrap);
</script>

Pros:

  • No big files. No fuss.
  • Works in a pinch.

Cons:

  • No tooltips. No legend. No axes labels unless I add them.
  • Not great for screen readers without extra work.

When I need more control than a DOM hack but lighter than a full framework, I still reach for D3; I shared that experience in I Made Real Charts With D3.js—Here’s How It Felt.


Real builds I shipped (quick stories)

  • My Etsy-ish sales board: I used Chart.js for daily sales and returns. It loaded fast on my phone. The stacked bars were clear for my boss. We spotted a Friday spike and ran a small promo on Saturdays. That actually helped.

  • School air demo: ApexCharts made a smooth live line. A kid waved a CO2 sensor by a plant. The line went up then down. The class clapped. I smiled.

  • City map test: ECharts handled a huge set of bus stop pings. I used a heatmap layer. It stayed snappy. My laptop fan still spun up, but the chart

Published
Categorized as Vibe Coding

I Tried 7 JavaScript Chart Libraries. Here’s What Actually Worked.

I build dashboards for a living. Sales, ops, app health—the whole mix. I’ve used a bunch of chart tools in JavaScript. Some made my day. Some made me cry in stand-up. You know what? The right pick depends on your data, your team, and your deadline. I even wrote a separate deep dive, I tried the big JavaScript chart tools—here’s what actually worked, if you want the blow-by-blow.

Here’s my honest take, with real bits from my code and my mess.


My Setup (so you know where I’m coming from)

  • Stack: React + Vite at work. Vanilla JS for small embeds.
  • Data: 5k to 120k points. Time series, bars, scatter, a few maps.
  • Must-haves: dark mode, tooltips, export, screen reader support.
  • Nice-to-haves: zoom, annotations, small bundle, smooth on mobile.

I’ll keep it simple and real. For a broader look at this same setup across a dozen contenders, see I tried a bunch of JavaScript chart libraries—here’s what actually worked for me.


Chart.js — The “I need something working by lunch” pick

I used Chart.js for a sales dashboard that had 8 charts. I was up and running in an hour. The defaults look clean. The docs are plain. It felt calm. It also features in my roundup I tried free JavaScript charts so you don’t have to where it came out near the top.

<canvas id="revenue"></canvas>
<script type="module">
  import { Chart } from 'https://cdn.jsdelivr.net/npm/chart.js';
  const ctx = document.getElementById('revenue');

  new Chart(ctx, {
    type: 'line',
    data: {
      labels: ['Mon','Tue','Wed','Thu','Fri'],
      datasets: [{
        label: 'Revenue',
        data: [120, 150, 90, 180, 210],
        borderColor: '#4f46e5',
        backgroundColor: 'rgba(79,70,229,0.15)',
        tension: 0.3,
        pointRadius: 0
      }]
    },
    options: {
      responsive: true,
      scales: {
        y: { beginAtZero: true }
      }
    }
  });
</script>

What I liked:

  • Good defaults. Labels don’t look wonky.
  • Time scales are easy. Tooltips are readable.
  • Plugins work fine (I use the datalabels one).

What bugged me:

  • Over 20k points, it stutters. Canvas gets hot.
  • Custom legends took a bit of glue code.
  • Mobile pinch zoom needs an add-on.

When I need fast and safe, I grab this.


Recharts — When I’m deep in React

I used Recharts for a fintech app with live data. The API feels like LEGO. I can compose parts. It makes sense with JSX. If you’re hunting open-source React options, I covered more in I tried a bunch of open-source JavaScript chart tools—here’s what actually worked.

import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';

const data = [
  { t: '09:00', v: 12 },
  { t: '10:00', v: 18 },
  { t: '11:00', v: 15 },
  { t: '12:00', v: 22 }
];

export default function Sparkline() {
  return (
    <ResponsiveContainer width="100%" height={140}>
      <LineChart data={data} margin={{ top: 8, right: 8, left: 8, bottom: 8 }}>
        <XAxis dataKey="t" hide />
        <YAxis hide domain={['dataMin', 'dataMax']} />
        <Tooltip />
        <Line type="monotone" dataKey="v" stroke="#10b981" strokeWidth={2} dot={false} />
      </LineChart>
    </ResponsiveContainer>
  );
}

Good stuff:

  • Very “React-y.” Composable. Easy to test.
  • Nice for dashboards with cards and grids.
  • Tooltips and responsive are simple.

Not so great:

  • Big datasets slow down. Virtualization helps, but still.
  • Axis control is okay, but not tiny-tiny precise.
  • SVG can choke on lots of points.

If you live in React, it’s friendly.


Apache ECharts — The power tool with a lot in the box

I moved a heavy time series (60k points) to ECharts. It stayed smooth. Zoom felt crisp. The built-in themes made dark mode look pro. ECharts also featured prominently in I tried a bunch of high-performance JavaScript charts—here’s what actually felt fast.

<div id="cpu" style="height:300px"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script>
  const chart = echarts.init(document.getElementById('cpu'));
  chart.setOption({
    tooltip: { trigger: 'axis' },
    xAxis: { type: 'category', data: ['09:00','10:00','11:00','12:00'] },
    yAxis: { type: 'value' },
    dataZoom: [{ type: 'inside' }, { type: 'slider' }],
    series: [{
      type: 'line',
      data: [32, 45, 38, 50],
      smooth: true,
      areaStyle: {}
    }]
  });
</script>

Why I keep it around:

  • Tons of chart types. Heatmaps, candlesticks, maps.
  • Data zoom, brush, and tooltips feel slick.
  • Good on large data with canvas.

Trade-offs:

  • Bundle is chunky if you’re strict on size.
  • The API is deep. New folks can get lost.
  • TypeScript types are… a lot.

When data is big or you need fancy, this is it.


Highcharts — Polished, rich, and… check your license

I used Highcharts for a healthcare client that needed exports, a11y, and clean PDFs. It looked like a report from day one. It saved time. I later leaned on the same export features for a side project—I built a tiny trading dashboard, here’s what I learned about financial charts in JavaScript.

<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script>
  Highcharts.chart('container', {
    title: { text: 'Appointments per Day' },
    xAxis: { categories: ['Mon','Tue','Wed','Thu','Fri'] },
    series: [{ name: 'Visits', data: [14, 18, 11, 20, 25], color: '#0ea5e9' }],
    accessibility: { enabled: true },
    exporting: { enabled: true }
  });
</script>

Why it shines:

  • Exports are easy. PNG, PDF, SVG—done.
  • Strong accessibility tools.
  • Solid docs and examples.

Heads-up:

  • Free for personal use, but business licenses cost.
  • The default look is “report-ish.” Good or bad, your call.
  • Custom themes take a minute.

If you have budget and need polish, it’s great.


ApexCharts — Quick wins for dashboards

I used ApexCharts for a marketing scorecard. Area charts with nice fades. Annotations were simple. Stakeholders smiled. The quick-win vibe is why it popped up in Easy JavaScript charts—what actually worked for me.

“`html

const options = {
series: [{ name: ‘Signups’, data: [30, 42, 28, 55, 61] }],
chart: { type: ‘area’, height: 280, toolbar: { show: false } },
dataLabels: { enabled: false },
stroke: { curve: ‘smooth’ },
colors: [‘#f59e0b’],
xaxis: { categories: [‘Mon’,’Tue’,’Wed’,’Thu’,’Fri’] },
fill: { type: ‘gradient’, gradient: { shadeIntensity: 0.3, opacityFrom: 0.5, opacityTo: 0.1 } }
};
new Apex

Published
Categorized as Vibe Coding

I built donut charts in JavaScript. Here’s what actually worked

I make dashboards for work. Quick stuff. Clean, readable, no drama. Last month I needed a donut chart fast. Actually, I needed four. I tried Chart.js, D3.js, ApexCharts, and ECharts. I used each one in a real page, and I kept notes like a snack-obsessed squirrel. For a side-by-side look at how these options stack up, this comprehensive comparison of JavaScript charting libraries, including Chart.js, D3.js, ApexCharts, and ECharts digs into features, performance, and common use cases.

In case you’d like a separate, blow-by-blow narrative of building donut charts from scratch, I documented every step in this detailed write-up.

You know what? They all work. But they feel different in the hand.


Quick take: which one should you grab?

  • Chart.js: Easy, fast, and friendly. Great for most cases.
  • D3.js: Full control. You can shape every pixel. More code though.
  • ApexCharts: Pretty labels and a sweet total number in the center.
  • ECharts: Heavy, powerful, and smooth with big data.

Need an even more specialized option? EJSChart ships focused donut and pie charts with zero-config defaults.

Earlier, I also put seven different JavaScript chart libraries through the wringer; you can skim the full comparison in I tried 7 JavaScript chart libraries—here’s what actually worked.

I’ll show you the exact code I ran and what tripped me up.


Chart.js — my fast win

I reached for Chart.js first because I was tired and hungry. It took me five minutes, maybe less. The defaults looked good. Hover felt smooth. The legend didn’t fight me. That alone was a relief.

For an even simpler, plug-and-play approach, I once pulled together a set of examples that show how to spin up charts with almost no configuration, which you can read in Easy JavaScript charts – what actually worked for me.

What I ran (Chart.js 4)

HTML

<canvas id="donut1" aria-label="Sales by channel donut chart" role="img"></canvas>

JavaScript

import { Chart, ArcElement, Tooltip, Legend } from 'chart.js';
Chart.register(ArcElement, Tooltip, Legend);

const ctx = document.getElementById('donut1');

new Chart(ctx, {
  type: 'doughnut',
  data: {
    labels: ['Web', 'Retail', 'Wholesale', 'Partners'],
    datasets: [
      {
        data: [48, 22, 18, 12],
        backgroundColor: ['#4F46E5', '#22C55E', '#F59E0B', '#EF4444'],
        borderWidth: 0
      }
    ]
  },
  options: {
    cutout: '60%',
    plugins: {
      legend: { position: 'bottom' },
      tooltip: {
        callbacks: {
          label: (ctx) => `${ctx.label}: ${ctx.formattedValue}%`
        }
      }
    },
    animation: { animateRotate: true, animateScale: true }
  }
});

What I liked

  • Setup felt painless.
  • Colors and legend looked clean out of the box.
  • Works fine in a small card layout.

What bugged me

  • The legend can wrap in narrow layouts. I had to tweak CSS.
  • If you need fancy center labels, you’ll need a plugin or custom draw.

If animations are your jam, I unpack the quirks of spinning slices in Animated pie charts in JavaScript—what worked, what didn’t.


D3.js — full control, strong coffee

I love D3. But let’s be honest: it’s more code. I used it when I needed custom arcs, gentle hover fades, and a label in the middle that doesn’t look like a sticker.

What I ran (D3 v7)

HTML

<div id="donut2"></div>

JavaScript

import * as d3 from 'd3';

const data = [
  { label: 'Marketing', value: 35 },
  { label: 'Dev', value: 40 },
  { label: 'Ops', value: 15 },
  { label: 'Other', value: 10 }
];

const width = 320, height = 320;
const radius = Math.min(width, height) / 2;

const color = d3.scaleOrdinal()
  .domain(data.map(d => d.label))
  .range(['#10B981', '#3B82F6', '#F43F5E', '#F59E0B']);

const pie = d3.pie().value(d => d.value).sort(null);
const arc = d3.arc().innerRadius(radius * 0.6).outerRadius(radius - 2);

const svg = d3.select('#donut2')
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .append('g')
  .attr('transform', `translate(${width / 2}, ${height / 2})`);

svg.selectAll('path')
  .data(pie(data))
  .enter()
  .append('path')
  .attr('d', arc)
  .attr('fill', d => color(d.data.label))
  .attr('opacity', 0.95)
  .on('mouseover', function () { d3.select(this).attr('opacity', 0.8); })
  .on('mouseout', function () { d3.select(this).attr('opacity', 0.95); });

svg.append('text')
  .attr('text-anchor', 'middle')
  .attr('dy', '0.35em')
  .attr('font-size', '16px')
  .attr('fill', '#111827')
  .text('Team Spend');

What I liked

  • It bends to your will. Every arc, every label.
  • Transitions feel silky.
  • I can add center text and custom legends with no hacks.

What bugged me

  • More code. More time.
  • You manage layout and a11y on your own.

ApexCharts — sweet center labels

One reason I reached for ApexCharts: donut labels. That total in the middle looks great on a KPI card. And the defaults? Polished. I shipped a report page with it in a single morning.

What I ran

HTML

<div id="donut3"></div>

JavaScript

const options = {
  series: [44, 55, 13, 33],
  labels: ['Apples', 'Bananas', 'Cherries', 'Dates'],
  chart: { type: 'donut', width: 320 },
  colors: ['#7C3AED', '#22C55E', '#F97316', '#06B6D4'],
  plotOptions: {
    pie: {
      donut: {
        size: '60%',
        labels: {
          show: true,
          name: { show: true },
          value: { show: true },
          total: {
            show: true,
            label: 'Total',
            formatter: () => '145'
          }
        }
      }
    }
  },
  legend: { position: 'bottom' },
  dataLabels: { enabled: true }
};

const chart = new ApexCharts(document.querySelector('#donut3'), options);
chart.render();

What I liked

  • Center total label out of the box.
  • Fast to theme and ship.
  • Good on mobile.

What bugged me

  • The API has many knobs. I had to test small things to get spacing right.

ECharts — heavy, strong, and smooth under load

I used ECharts for a big dashboard that ran on a TV screen in our hallway. Lots of data, long sessions. It held up. The tooltips are rich. The theme system is deep.

What I ran

HTML

<div id="donut4" style="width: 320px; height: 320px;"></div>

JavaScript

“`javascript
const chart = echarts.init(document.getElementById('donut4'));

chart.setOption({
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
legend: { bottom: 0 },
series: [
{
name: 'Traffic',
type: 'pie',
radius: ['55%', '75%'],
avoidLabelOverlap: true,
itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 2 },
label: { show: true, position: 'outside' },
data: [
{ value: 1048, name: 'Direct' },
{ value: 735, name: 'Email' },
{ value: 580, name: 'Search' },
{ value: 484, name: 'Social' }
]
}

Published
Categorized as Vibe Coding

“I Tried Making Beautiful JavaScript Charts. Here’s What Actually Worked.”

I make dashboards. Sales, traffic, signups, the usual mess. I care about looks, but I also care about time. Can I ship fast? Will it run smooth on a phone? Will my boss say “wow” and not “ugh”?

So I tried a bunch of chart tools. I used them on real work. I broke some things. I fixed them. And yeah, I have feelings about it. (I documented the whole experiment in a no-fluff recap you can skim here.)

You know what? Pretty charts are nice. But clear charts feel even better. If you just want the easiest path to sane defaults, there's a quick checklist over here.


My quick wish list

  • Clean by default
  • Fast on mobile
  • Dark mode that doesn’t look muddy
  • Good tooltips (no tiny text)
  • Exports that don’t blur when printed
  • A path for big data, like 50k points
  • Basic a11y (labels, focus, ARIA)

If you want a hosted, out-of-the-box option that already nails most of those bullets, give EJSchart a whirl. For a side-by-side look at seven popular libraries, check out this comparison.

That’s not a small ask. Still, a few tools got me close.


Chart.js — my easy go-to

I used Chart.js for a coffee shop dashboard. It showed daily revenue and average order size. I had two days to make it. Chart.js was like a buddy. It just worked.

What I like:

  • Good defaults and quick wins
  • Plugins for labels and notes
  • The “tension” line looks smooth
  • Built-in decimation helped with large sets

What bugged me:

  • Big bundle if you add lots of plugins
  • Past 50k points, it can drag

Here’s a small line chart I shipped. Pretty, soft, and readable.

<canvas id="revenue" height="160"></canvas>
<script>
const ctx = document.getElementById('revenue').getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 200);
gradient.addColorStop(0, 'rgba(99,102,241,0.6)');
gradient.addColorStop(1, 'rgba(99,102,241,0.05)');

new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Jan','Feb','Mar','Apr','May','Jun'],
    datasets: [{
      data: [12,19,9,17,23,28],
      borderColor: '#6366f1',
      backgroundColor: gradient,
      fill: true,
      tension: 0.35,
      pointRadius: 2
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: { display: false },
      tooltip: { callbacks: { label: c => `$${c.parsed.y}k` } },
      decimation: { enabled: true, algorithm: 'lttb' }
    },
    scales: {
      y: { ticks: { callback: v => `$${v}k` } }
    }
  }
});
</script>

A tiny note: use system fonts and enough white space. It breathes better. Need more inspiration for gorgeous yet lightweight visuals? This walkthrough lays out the small tweaks that make charts pop.


ECharts — big, bold, and kind of fun

For a school site, I needed traffic charts with pan and zoom. Parents loved to pinch and zoom on phones. Apache ECharts felt flashy in a good way. The zoom tool and export button were a hit. For the scoop on how the biggest chart tool names stack up, see this field report.

Good stuff:

  • Themes look rich
  • Zoom and pan feel smooth
  • Exports are crisp
  • Huge data didn’t choke

Tough stuff:

  • The API names are… a lot
  • You’ll read the docs more than you want

Real code I used (trimmed):

<div id="traffic" style="height:280px;"></div>
<script>
var chart = echarts.init(document.getElementById('traffic'));
chart.setOption({
  tooltip: { trigger: 'axis' },
  toolbox: { feature: { saveAsImage: {} } },
  dataZoom: [{ type: 'inside' }, { }],
  xAxis: { type: 'category', boundaryGap: false, data: datesArray },
  yAxis: { type: 'value' },
  series: [{
    name: 'Visits',
    type: 'line',
    smooth: true,
    areaStyle: {},
    emphasis: { focus: 'series' },
    data: visitsArray
  }]
});
</script>

I used the “dark” theme one night. It looked like a city at midnight. Kind of loved it.


D3 — total control, but bring snacks

I made a custom survey chart for a non-profit gala. They wanted a “lollipop” chart with cute dots. Off the shelf tools couldn’t do it. So, D3.

Why D3?

  • You control every pixel
  • Great scales and axes
  • Can be small if you pick parts

But:

  • You write more code
  • Data joins take a minute to learn

My tiny lollipop sample:

<svg id="lolli" viewBox="0 0 420 200"></svg>
<script>
const data = [
  { label:'Food', value:82 },
  { label:'Music', value:67 },
  { label:'Decor', value:41 },
  { label:'Venue', value:74 }
];

const svg = d3.select('#lolli');
const x = d3.scaleLinear().domain([0,100]).range([100,380]);
const y = d3.scaleBand().domain(data.map(d=>d.label)).range([20,180]).padding(0.4);

svg.append('g').attr('transform','translate(0,0)')
  .selectAll('line').data(data).join('line')
  .attr('x1',100).attr('x2',d=>x(d.value))
  .attr('y1',d=>y(d.label)+y.bandwidth()/2)
  .attr('y2',d=>y(d.label)+y.bandwidth()/2)
  .attr('stroke','#cbd5e1').attr('stroke-width',4);

svg.append('g').selectAll('circle').data(data).join('circle')
  .attr('cx',d=>x(d.value))
  .attr('cy',d=>y(d.label)+y.bandwidth()/2)
  .attr('r',8).attr('fill','#10b981');

svg.append('g').attr('transform','translate(0,0)').call(d3.axisLeft(y));
svg.append('g').attr('transform','translate(0,180)').call(d3.axisBottom(x).ticks(5).tickFormat(d=>d+'%'));
</script>

It felt hand-made, which was the point. If you're hunting for open-source options beyond the usual suspects, this write-up might help: I tried a bunch of open-source JavaScript chart tools—here's what actually worked.


Recharts (React) — smooth in React land

My team ships React. Recharts fits that mood. It uses components, so state and props feel natural. Not the flashiest, but it’s calm and steady.

Pros:

  • Great DX in React
  • Composable parts
  • Easy theming with CSS

Cons:

  • Heavy sets can lag
  • Less wild chart types

Tiny example:

<LineChart width={320} height={160} data={data}>
  <XAxis dataKey="day" />
  <YAxis />
  <Tooltip />
  <Legend />
  <Line type="monotone" dataKey="orders" stroke="#f59e0b" dot={false} />
</LineChart>

I used this in a sales card list. It felt snappy.


Highcharts — polished, but watch the license

For a finance client, I used Highcharts Stock. The range selector and flags saved me hours. It looks fancy without much work. But heads up: you need a license for paid work.

What I saw:

  • Great time series tools
  • Crisp labels and exports
  • A11y module is solid

Trade-off:

  • Cost, of course

Want to stay in the free tier? There’s a rundown of no-cost libraries right here.


Plotly.js — science class energy

I used Plotly for

Published
Categorized as Vibe Coding

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 = {

Published
Categorized as Vibe Coding

I Built Real Charts With JavaScript: My Honest, Hands-On Take

I’m Kayla. I make dashboards for work and for fun. Over the past few years, I’ve tried a bunch of graph chart JavaScript tools. Some felt smooth. Some felt like climbing a wall with socks on. I’ve got stories, small wins, and a few oops moments.

If you're hunting for a spoiler-filled comparison, my rundown of trying a bunch of JavaScript chart libraries and what actually worked for me may help frame the landscape.

For an even nerdier step-by-step, my earlier post about how I built real charts with JavaScript in a hands-on test walks through the bumps in code form.

Here’s what stood out when I used these libraries on real projects.

I also spent a weekend with EJSChart, a lightweight library that punches above its weight, though it hasn’t cracked my daily toolkit yet.
For a lighter primer, see how I drew charts in JavaScript so you don't sweat it.


Chart.js — My Easy Button

I first used Chart.js for a school fundraiser board. We needed a simple line chart and a doughnut chart. Setup took minutes. It felt friendly, like a toaster—plug in, toast out.
For the nitty-gritty details, see the official Chart.js site.

That experience mirrors my separate guide on easy JavaScript charts and what actually worked for me.

What I liked:

  • Quick setup
  • Nice defaults
  • Good docs

What bugged me:

  • Canvas only, so custom SVG tricks are harder
  • Deep custom themes take more work

A tiny real example I shipped (sales by month):

<canvas id="sales"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('sales');
new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Jan','Feb','Mar','Apr','May','Jun'],
    datasets: [{
      label: 'Sales',
      data: [12, 19, 13, 22, 17, 25],
      borderColor: '#4f46e5',
      tension: 0.3,
      fill: false
    }]
  },
  options: {
    responsive: true,
    plugins: { legend: { display: true } },
    scales: { y: { beginAtZero: true } }
  }
});
</script>

That chart powered a tiny sales stand-up for months. No drama.

If polish is more your vibe, my experiment in making beautiful JavaScript charts shows a few extra tweaks you can steal for Chart.js.


D3.js — Wild Power, Sharp Edges

D3 feels like a toolbox and a puzzle at the same time. I used it for a custom bar chart with tooltips and keyboard support. It looked great. It also ate a weekend. Did I learn a ton? Yes. Did I mutter to myself? Also yes.
The full power docs live on the official D3.js page if you’re the reading type.

What I liked:

  • Full control
  • SVG magic
  • Great for custom stories

What bugged me:

  • Steeper curve
  • You build a lot by hand

Real bar chart I built for an ops report:

<svg id="chart" width="420" height="200"></svg>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
const data = [4, 8, 15, 16, 23, 42];
const svg = d3.select('#chart');
const x = d3.scaleLinear().domain([0, d3.max(data)]).range([0, 400]);

svg.selectAll('rect')
  .data(data)
  .enter().append('rect')
  .attr('y', (d,i) => i * 30)
  .attr('height', 20)
  .attr('width', d => x(d))
  .attr('fill', '#10b981');

svg.selectAll('text')
  .data(data)
  .enter().append('text')
  .attr('y', (d,i) => i * 30 + 14)
  .attr('x', d => x(d) + 5)
  .text(d => d)
  .attr('font-size', 12);
</script>

When I need custom maps or a special layout, I reach for D3. When time is tight, I don’t.

I later doubled down on SVG wizardry in this recap of how I built charts with D3.js—what actually worked and what didn't.

If open-source tooling is your jam, my take on a bunch of open-source JavaScript chart tools and what actually worked stacks D3 up against its peers.


ECharts — Flashy Without Fuss

I used ECharts for a live ops dashboard. We had CPU heatmaps, pie charts, and a geo map. Themes looked sharp right out of the gate. Tooltips felt rich without me fiddling too much.

What I liked:

  • Pretty themes
  • Smooth tooltips
  • Handles big data well

What bugged me:

  • Option objects get long
  • Docs are clear, but there’s a lot

A heatmap I used for server load:

<div id="heat" style="width:600px;height:300px"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5"></script>
<script>
const chart = echarts.init(document.getElementById('heat'));
const hours = [...Array(24).keys()];
const days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
const data = [];
for (let d = 0; d < 7; d++) {
  for (let h = 0; h < 24; h++) {
    data.push([h, d, Math.round(Math.random()*100)]);
  }
}
chart.setOption({
  tooltip: { position: 'top' },
  xAxis: { type: 'category', data: hours },
  yAxis: { type: 'category', data: days },
  visualMap: { min: 0, max: 100, calculable: true, orient: 'horizontal' },
  series: [{ type: 'heatmap', data }]
});
</script>

It ran all week with live data pings. Not once did it stutter.

In a separate speed test, I poked at high-performance JavaScript charts to see what actually felt fast—ECharts scored high there too.


Highcharts — Polished, Especially For Finance

I used Highcharts at a fintech client. They had a license, which is needed for commercial work. The charts looked clean. The stock module saved me hours. Candles, ranges, zoom—done.

What I liked:

  • Very polished
  • Stock charts feel pro
  • Great interactions

What bugged me:

  • License cost for paid use
  • Some features hide in modules

A tiny stock-style line I used for a quick sparkline panel:

<div id="mini"></div>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script>
Highcharts.chart('mini', {
  chart: { height: 120 },
  title: { text: null },
  xAxis: { visible: false },
  yAxis: { title: { text: null } },
  series: [{ data: [1,3,2,4,3,5,4], color: '#ef4444' }],
  legend: { enabled: false },
  credits: { enabled: false }
});
</script>

It’s the kind of chart you add and forget because it just behaves.

For a lens on heavyweight options, my field notes on the big JavaScript chart tools and what actually worked lay out how Highcharts compares.


Recharts — Sweet Spot for React Folks

If you live in React land, Recharts feels cozy. I used it for a sales dashboard with Next.js. JSX charts read like UI pieces. I could pass props, map data, and reuse bits fast.
I’ve chronicled similar React-heavy workflows in I make charts with JavaScript—here’s what actually worked for me.

What I liked:

  • React-first flow
  • Easy to customize
  • Strong
Published
Categorized as Vibe Coding

I built horizontal stacked bar charts in JavaScript. Here’s what felt right.

I make dashboards for small teams. I like charts that say a lot with a little space. Horizontal stacked bars do that. They show parts and the whole at the same time. Think tasks done, in progress, and blocked. All on one line. Nice and tidy. If you’re curious about the nitty-gritty decisions that shaped my own implementation, I wrote a separate teardown that walks through every step over here.

I tried three tools on real work:

  • Chart.js
  • Apache ECharts
  • ApexCharts

I used them in Chrome on my Windows 11 laptop and on my iPhone 13. I also tested one old Lenovo in the office that wheezes when you open Figma. You know the type. I later expanded that bake-off to seven different JavaScript chart libraries, and the brutally honest results are summarized in this deeper comparison.

So, how did it go? Let me explain.

Quick read: my take

  • Chart.js felt the most friendly. Great for small to mid data. Easy to theme.
  • ECharts handled big data and long labels. Rich tooltips. A bit heavier.
  • ApexCharts was fast to set up. Good defaults. Clean UI.

If you just want the straight-to-the-point recipe for making JavaScript charts feel painless, my condensed notes live right here.

I kept all three in my toolbox. But I reach for Chart.js first when time is tight.


Real example: team status with Chart.js

Use it when you want a simple setup with canvas. It’s light and easy. I used this for a weekly standup board. For an authoritative walkthrough of the available props and caveats, the official Chart.js horizontal bar chart documentation is a handy companion.

HTML

<canvas id="statusChart" height="300"></canvas>

JavaScript (Chart.js v4+)

const ctx = document.getElementById('statusChart');

new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Design', 'Frontend', 'Backend', 'QA'],
    datasets: [
      { label: 'Done', data: [8, 6, 5, 7], backgroundColor: '#22c55e', stack: 'total' },
      { label: 'In Progress', data: [3, 5, 6, 2], backgroundColor: '#f59e0b', stack: 'total' },
      { label: 'Blocked', data: [1, 2, 1, 3], backgroundColor: '#ef4444', stack: 'total' }
    ]
  },
  options: {
    indexAxis: 'y', // this makes it horizontal
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      legend: { position: 'bottom' },
      tooltip: {
        callbacks: {
          label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.x}`
        }
      }
    },
    scales: {
      x: { stacked: true, title: { display: true, text: 'Tickets' } },
      y: { stacked: true }
    }
  }
});

What I liked:

  • Colors looked crisp on canvas. Even on my older Lenovo.
  • Stacking “just worked.” No fuss.
  • The legend was easy to move.

What bugged me a bit:

  • Long labels can get chopped. I had to shorten “Frontend Platform” to “FE Platform.”
  • Data labels need a plugin. It’s fine, but it’s one more step.

Tiny tip:

  • If you want data labels, add chartjs-plugin-datalabels and set formatter to show values on each bar.

For even more real-world Chart.js war stories, I hammered together an entire project and documented the wins and bruises in this write-up.


Real example: budget vs actual with ECharts

I used ECharts for a Q3 report. We had 40 rows and long team names. It held up well. You can also sanity-check the stacking logic against the Apache ECharts stacked bar handbook if you need to dig deeper into advanced features.

HTML

<div id="budgetChart" style="height:400px;"></div>

JavaScript

const chart = echarts.init(document.getElementById('budgetChart'));

const option = {
  tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
  legend: {},
  grid: { left: '2%', right: '3%', bottom: '3%', containLabel: true },
  xAxis: { type: 'value', name: 'USD (k)' },
  yAxis: { type: 'category', data: ['Design', 'Frontend', 'Backend', 'QA'] },
  series: [
    { name: 'Planned', type: 'bar', stack: 'total', data: [120, 150, 180, 90], itemStyle: { color: '#60a5fa' } },
    { name: 'Spent', type: 'bar', stack: 'total', data: [100, 140, 170, 80], itemStyle: { color: '#34d399' } },
    { name: 'Over/Under', type: 'bar', stack: 'total', data: [20, 10, 10, 10], itemStyle: { color: '#fbbf24' } }
  ],
  aria: { enabled: true }
};

chart.setOption(option);
window.addEventListener('resize', () => chart.resize());

What I liked:

  • Long labels wrap. No sweat.
  • Tooltips felt rich and fast.
  • It has built-in aria support. My PM loved that.

What didn’t land:

  • Bundle is bigger. Load time on weak Wi-Fi felt slower.
  • The API has many options. Powerful, but you can get lost.

Side note:

  • I used aria.enabled = true for screen readers. It’s not perfect, but it’s there.

For a quick visual walkthrough of drawing similar charts from scratch, you can skim the screenshots in this guide.


Real example: hiring pipeline with ApexCharts

This was for HR. We tracked candidates by stage. Clean look. Quick start.

HTML

<div id="hiringChart"></div>

JavaScript

const options = {
  chart: { type: 'bar', height: 360, stacked: true, toolbar: { show: false } },
  plotOptions: { bar: { horizontal: true, barHeight: '60%' } },
  series: [
    { name: 'Phone Screen', data: [8, 5, 6] },
    { name: 'Onsite', data: [4, 3, 2] },
    { name: 'Offer', data: [2, 1, 1] }
  ],
  xaxis: { categories: ['Design', 'Engineering', 'Data'] },
  colors: ['#93c5fd', '#f59e0b', '#22c55e'],
  legend: { position: 'bottom' },
  dataLabels: { enabled: true, formatter: (v) => v }
};

const chart = new ApexCharts(document.querySelector('#hiringChart'), options);
chart.render();

What made me smile:

  • Data labels were easy to show. No plugin.
  • Legend and spacing looked neat by default.

Where I had to tweak:

  • On tiny phones, labels can overlap. I bumped barHeight down to 50% and it felt better.
  • If you push very large data, it can feel snug.

Small real-world gotchas I hit

  • Colors and contrast: My first theme looked cute but failed contrast checks. I bumped text to #111 and used deeper greens and blues. Much better.
  • RTL text: A teammate in Tel Aviv asked for Hebrew labels. ECharts handled it fine. For Chart.js, I had to set font and check spacing. It worked, but I had to test more.
  • Printing: Canvas charts can look soft in print. SVG (like ECharts) stays sharp. I export PNGs from canvas at 2x for reports.
  • Tooltips on touch: Long press works, but not everyone knows it. I added data labels to help.
  • Export: I used toBase64Image() in Chart.js for quick exports. Handy for slides.

Sharing chart snapshots sometimes means firing them off in disappearing-photo apps. If you’re tempted to drop your latest graph into a Snapchat message, this primer on snap sexting explains the platform’s privacy quirks and offers tips to make sure your images don’t stick around longer than you intend.

On a related privacy note, if you’re interested in how modern classified personals sites approach image handling, moderation, and user anonymity in a city-specific setting, you can explore [MegaPerson

Published
Categorized as Vibe Coding