I Tested 7 JavaScript Candlestick Charts. Here’s What Actually Worked.

Hi, I’m Kayla Sox. I code by day and track stocks at night. I built three small trading tools this year. And yes, I used real data. My coffee got cold more than once. I wrote a deeper blow-by-blow of that seven-chart shoot-out in this full testing diary.

I tested these charts on:

  • A 2019 MacBook Air, Chrome
  • A cheap Windows laptop, Edge
  • An iPhone 12 and a beat-up Moto G7

Data came from Binance (BTCUSDT, 1m and 5m), Polygon (AAPL, MSFT, daily), and a tiny Node server I wrote for candles. I tried 5k to 20k candles, fast zoom, pan, crosshair, and live ticks. I checked bundle size with Vite and devtools. Not perfect science, but close enough to feel real.

You know what? Some charts made me smile. Some made me sigh. Here’s the straight talk.

How I Tested (Quick and plain)

  • Built small pages with React and vanilla JS.
  • Used WebSocket updates (one tick per second, sometimes faster).
  • Added simple indicators: SMA(20), EMA(50), RSI(14).
  • Looked for smooth zoom, clear tooltips, clean candles, and no weird lag.
  • Tried both dark and light themes. Because glare hurts.

Now, onto the charts I actually used.

1) TradingView Lightweight Charts — My Top Pick

This one felt right the second I panned the chart. If you want to dig into the API yourself, the official docs for TradingView Lightweight Charts are refreshingly clear.

  • What I loved: buttery zoom; crisp crosshair; nice spacing; easy series overlays.
  • What bugged me: indicators aren’t built in, so I wrote my own lines.
  • Real test: live BTC ticks at 1s. 10k candles stayed smooth. CPU stayed calm on my Air.
  • Mobile: pinch zoom worked great; no random jumps.
  • Bundle: small and friendly for the build.
  • License: open and fine for most use cases.

I used it in a crypto bot dashboard. Added buy/sell markers with tiny triangles. No drama. It just worked.

2) Highcharts Stock — The “I Need Everything” Choice

When a client wants depth and polish, I reach for this. It looks rich out of the box. The demo gallery on Highcharts Stock shows just how many knobs you can turn.

  • What I loved: built-in range selector; great annotations; solid gaps for market hours.
  • What bugged me: license cost for commercial work; bundle felt heavier.
  • Real test: AAPL daily with gaps and compare mode. Smooth and clear.
  • Indicators: plenty. I stacked MACD and RSI under the chart like a champ.
  • Mobile: fine, but a little heavier on older phones.

I shipped a report page with it. Boss smiled. I slept well.

3) ApexCharts — Fast Start, Friendly API

It’s good when you want to ship fast with React and not fight the API.

  • What I loved: simple props; tooltips are easy; candlestick colors are clear.
  • What bugged me: very large data sets felt a bit heavy past 15k candles.
  • Real test: MSFT daily, 10-year span. A touch slow on zoom, but still okay.
  • Add-ons: heatmaps and bars play nice if you want extra flair.

I built a one-page tracker for a friend. We had it running before lunch. That mini trading dashboard journey is written up in this short case study.

4) ECharts — Free, Flexible, Powerful

ECharts can look stunning with the right config. It takes a little care, but it pays off.

  • What I loved: tons of control; great tooltip format; clean OHLC.
  • What bugged me: config can get wordy; you need to tune it for speed.
  • Real test: 20k candles with EMA lines. It held up with smart sampling.
  • Mobile: good, with smooth pan if you keep the data trimmed.

I used it for a weekend hack. Swapped between bars and candles with one state tweak. Felt slick. Earlier I also tried a bunch of open-source JavaScript chart tools and noted what actually worked in this roundup.

5) Chart.js + Financial Plugin — Good If You Already Use Chart.js

I like Chart.js for simple stuff. Candles work with a plugin. It’s fine for light loads.

  • What I loved: one ecosystem; clear docs; easy tooltips.
  • What bugged me: not the fastest with big sets; advanced stock tools are basic.
  • Real test: daily candles for a year. Fine. Five years? It started to drag.
  • Great for: school projects, quick demos, and dashboards that don’t need speed.

I put it in a school finance club site. They were happy. It was simple.

6) Plotly.js — Data Nerd Heaven, But Heavy

If you need deep data work, Plotly is strong. But it’s not tiny.

  • What I loved: subplots; linked axes; export as PNG; lots of chart types.
  • What bugged me: bundle weight; panning felt heavier with many candles.
  • Real test: multi-panel layout with RSI and volume. Looked sharp, lagged on phone.
  • Best for: research, static reports, and slides.

I used it for a quarterly report. Pretty charts, zero fuss on sharing.

7) Klinecharts — Small, Focused, Finance-First

This one is built for candles. It’s lean and quick.

  • What I loved: fast draw; nice finance features; clean API.
  • What bugged me: fewer extras; docs feel thinner than the big guys.
  • Real test: 1m BTC stream at 1s. Ran smooth on my old Android.
  • Best for: simple trading UIs that need speed and not much else.

I dropped it into a small WebSocket app. It kept up just fine.

My Real Picks (Short and sweet)

  • Best overall feel: TradingView Lightweight Charts
  • Best for enterprise features: Highcharts Stock
  • Best free power and control: ECharts
  • Easiest React start: ApexCharts
  • Best tiny, finance-first: Klinecharts
  • Best if you already have Chart.js: Chart.js + Financial plugin
  • Best for analytics and reports: Plotly.js

Honorable mention: EJSChart also provides a super-lightweight candlestick component that feels snappy even with live data streams.

Small Things That Matter (I learned the hard way)

  • Time zones: Test New York vs. UTC. Gaps can look strange if you skip this.
  • Tooltips: Snap to candle, not the mouse. It feels cleaner.
  • Volume overlay: Keep it subtle. Too much color kills the eyes.
  • Live updates: Batch ticks by second. Don’t redraw every millisecond.
  • Indicators: SMA and EMA are easy. RSI can slow things down if you’re sloppy.
  • Theme: Pick fonts that don’t wobble on mobile. I like simple sans fonts.

Here’s the thing: speed is nice, but feel matters more. If the crosshair jitters, traders get annoyed. If zoom jumps, folks lose trust. I saw both. I fixed both by trimming data and easing animations.

What I Use Today

  • For my crypto bot: TradingView Lightweight Charts with custom EMA and small markers.
  • For client dashboards: Highcharts Stock or ECharts, based on budget.
  • For quick tests and classes: ApexCharts or Chart.js with the plugin.

I still try new tools. Markets change. Browsers change. My taste changes too. But this list holds up. Last month I even lined up seven general-purpose JavaScript chart libraries to see which ones hold up, and you can read the results here.

If you’re unsure, start with Lightweight Charts. Add one indicator. Pan around. If it feels good, you’re set. If not, ECharts is a solid plan B.

Got a weird use case? Tell me. I’ve probably spilled coffee on that problem already.

Just like some JavaScript libraries mature gracefully with age and feature depth, there are whole communities that appreciate maturity in other areas of life as well. If your late-night coding breaks could use a different kind of seasoned perspective, check out Mature Women on FuckLocal. You’ll find verified profiles of confident, experienced women ready for authentic conversation and no-pressure meet-ups—perfect for unwinding after a marathon debugging session.

And if you happen to be coding (or just decompressing) in the Twin Cities and want an equally streamlined way to connect with locals, swing by the classifieds at MegaPersonals Minneapolis for quick, location-based listings that update in real time—think of it as applying the same fast, filter-friendly logic of a good chart API to finding a low

Published
Categorized as Hard Coding

I Built a JavaScript Organizational Chart Three Ways — Here’s What Actually Worked

I had to ship an org chart for our team portal last spring. HR wanted photos, titles, and those tricky dotted-line reports. Oh, and it had to print clean for a board deck. Classic, right?

If you want the blow-by-blow on this exact project, I wrote a companion piece that walks through the three approaches step by step — read the full story here.

I tried three routes: Google Charts OrgChart, OrgChart JS by Balkan, and a custom D3 build. If you’re open to a fourth path, the new EJS Chart library ships an org-chart component that slots nicely between quick demos and full-blown custom builds. I used all three on real data. About 620 people. Mixed managers. New hires every week. It got messy. You know what? That’s where you see what holds up.
For quick reference, the official Google chart documentation is available here, and the OrgChart JS product page lives here.

What I needed (and what bit me)

  • Fit 600+ nodes, with smooth zoom and pan
  • Collapse branches, but keep search fast
  • Show dotted-line (matrix) reports
  • Photos, titles, and a small badge for location
  • Export to PNG and PDF
  • Work on Chrome, Safari, iPad, and a stubborn Windows laptop in a kiosk

Now the fun part: how each tool behaved when I pushed it.

Google Charts OrgChart — fast start, small ceiling

Setup was easy. I had a chart on the page in 15 minutes. It looked clean. Collapsing worked. Search was okay with a simple list.

But I hit limits fast. Styling was tight. Dotted-line links? Not really. Printing was okay for a small team, but for 600 nodes, the layout got cramped and fuzzy.

What I liked

  • It’s free and simple
  • Great for small teams or a quick demo
  • Collapse and expand works out of the box

What bugged me

  • Hard to style cards (HTML allowed, but still fussy)
  • No clean dotted-line support
  • Slow with very large data

A tiny snippet I used:

<div id="org"></div>
<script>
  google.charts.load('current', {packages:['orgchart']});
  google.charts.setOnLoadCallback(drawChart);

  function drawChart() {
    const data = new google.visualization.DataTable();
    data.addColumn('string', 'Name');
    data.addColumn('string', 'Manager');
    data.addColumn('string', 'ToolTip');

    data.addRows([
      [{'v':'1', 'f':'Ava Lee<div style="color:#777">CEO</div>'}, '', ''],
      [{'v':'2', 'f':'Sam Patel<div>VP Sales</div>'}, '1', ''],
      [{'v':'3', 'f':'Rin Park<div>VP Eng</div>'}, '1', ''],
    ]);

    const chart = new google.visualization.OrgChart(document.getElementById('org'));
    chart.draw(data, {allowHtml: true, size: 'large'});
  }
</script>

It worked fine for a pilot. Not for my whole company.

For a broader look at how other open-source chart solutions fared in my tests, check out my separate roundup of contenders — I tried a bunch of open-source JavaScript chart tools, here’s what actually worked.

OrgChart JS (Balkan) — polished, paid, and practical

This one surprised me. It felt like it was built for busy folks like me. Templates, export buttons, drag and drop, and real-world stuff like assistant roles and partner lines.

We shipped this path first. It took me one day to set the base chart and two more days to tune badges, photos, and collapse rules. Export to PDF was crisp. It even handled our Hebrew labels right-to-left, which saved me a week.

What I liked

  • Looks great out of the box; templates like “olivia” and “ana”
  • Easy export to PNG/PDF
  • Built-in search is fast; collapse by level works
  • Dotted-line and assistant roles felt natural

What bugged me

  • It’s paid (not shocking, just a factor)
  • My Safari on macOS stuttered with 1,000+ nodes and huge photos
  • Custom link styles needed more tinkering than I hoped

Real snippet from my page:

<div id="tree" style="width:100%; height:700px;"></div>
<script src="orgchart.js"></script>
<script>
  const nodes = [
    { id: 1, name: "Ava Lee", title: "CEO", photo: "ava.jpg" },
    { id: 2, pid: 1, name: "Sam Patel", title: "VP Sales", photo: "sam.jpg" },
    { id: 3, pid: 1, name: "Rin Park", title: "VP Eng", photo: "rin.jpg" },
    // dotted-line relationship (reports to both Sales and Eng)
    { id: 22, pid: 2, name: "Mia Cho", title: "Ops", photo: "mia.jpg" },
    { id: 22_2, pid: 3, mid: 22 } // maps a second link to Mia
  ];

  const chart = new OrgChart(document.getElementById("tree"), {
    template: "olivia",
    mouseScrool: OrgChart.action.zoom,
    nodeBinding: {
      field_0: "name",
      field_1: "title",
      img_0: "photo"
    },
    collapse: { level: 2 },
    enableSearch: true,
    tags: {
      remote: { template: "olivia" }
    }
  });

  chart.load(nodes);

  // simple export button I wired up
  document.getElementById("export").onclick = () => chart.exportPNG();
</script>

That dotted-line trick (the extra entry with mid) was my “aha” moment. It looked tidy on screen and still printed well.

D3.js — full control, more work

I love D3 because I can bend it any way I want. And I did. I used d3.hierarchy and d3.tree, then added curved links. I even switched to canvas for a heavy view and kept SVG for labels. Super fast. Super custom.

But it took time. I wrote keyboard nav. I wrote a print layout. I wrote my own “search and reveal” logic. It felt good, but my team had a deadline, so we kept this build as a backup.

If you’re still shopping for visualization libraries, I also put seven of the most popular JavaScript chart libraries head-to-head — here’s what actually worked.

What I liked

  • Total control over style, links, and layout
  • Canvas mode flew with 2,000 nodes
  • Easy to shape custom badges and tooltips

What bugged me

  • More code, more testing
  • Printing took real work
  • Accessibility needed extra care

Here’s the tiny seed of my D3 build:

<svg id="chart" width="1200" height="800"></svg>
<script src="d3.v7.min.js"></script>
<script>
  const data = {
    name: "Ava Lee",
    children: [
      { name: "Sam Patel", children: [{ name: "Mia Cho" }] },
      { name: "Rin Park" }
    ]
  };

  const root = d3.hierarchy(data);
  const tree = d3.tree().nodeSize([100, 200])(root);

  const svg = d3.select("#chart");
  const g = svg.append("g").attr("transform", "translate(60,60)");

  g.selectAll(".link")
    .data(tree.links())
    .enter().append("path")
    .attr("class", "link")
    .attr("fill", "none")
    .attr("stroke", "#999")
    .attr("d", d3.linkHorizontal()
      .x(d => d.y)
      .y(d => d.x));

  const node = g.selectAll(".node")
    .data(tree.descendants())
    .enter().append("g")
    .attr("transform", d => `translate(${d.y},${d.x})`);

  node.append("rect")
    .attr("width", 150)
    .attr("height", 50)
    .attr("x", -75)
    .attr("y", -25)
    .attr("rx", 6)
    .attr("fill", "#fff")
    .attr("stroke", "#ccc");

  node.append("text")
    .attr("text-anchor", "middle")
    .attr("dy", "0.35em")
    .text(d => d.data.name);
</script>

Simple, clear, and yours to shape. But yes, it’s work.

Real week at work: the reorg crunch

We had a reorg drop on a Thursday. New VPs, a new PMO, and a dotted-line mess. I

Published
Categorized as Hard Coding

I Tried a Bunch of JavaScript Chart Libraries. Here’s What Actually Worked For Me.

I make dashboards for work. And sometimes for fun, like my kid’s school fundraiser page. I’ve used a bunch of chart libraries in JavaScript. Some saved my day. Some made me grumpy. Here’s the honest version, with real stuff I built and what went right (and wrong). If you’re after just the open-source angle, this write-up on OSS chart tools is a solid primer.

The quick take (so you don’t scroll forever)

  • Chart.js: Fast to set up. Great for small dashboards.
  • D3.js: Power tool. You can build anything, but you’ll sweat a little.
  • ECharts: Handles big data well. Nice zoom. Good on phones.
  • Highcharts: Polished and friendly for business folks. License for commercial use.
  • Recharts: Sweet with React. Ships quick. Good defaults.
  • ApexCharts: Clean stock charts. Easy mixed charts.
  • Plotly.js: Fancy science plots. Heavy bundle.
  • uPlot: Speed demon for huge time series. But very bare bones.

Looking for a shorter list? I once compared seven JavaScript chart libraries head-to-head.

One newcomer that impressed me is EJSchart, which slots somewhere between Chart.js and ECharts in terms of power-to-weight ratio.

Now, let me explain how I used each one.

Chart.js and the school fundraiser pie

I started with Chart.js (official site) for a simple school fundraiser site. We had three groups selling cookies. I made a pie (donut) chart and a line chart showing sales per day. Setup took me under 30 minutes.

What I liked:

  • The defaults look good right away.
  • Tooltips and legends just work.
  • It’s small and easy to add to a page.

What bugged me:

  • Custom labels took a bit of code.
  • Too many points made it choppy.

A tiny slice of how I set it up:

  • Type: "line"
  • Data: dates on the x-axis
  • Options: tension: 0.3 for smooth lines, responsive: true, maintainAspectRatio: false

Simple, right? It did the job. Parents could see the trend. And yes, chocolate chip won.

D3.js for a city budget map (I got picky)

I built a city budget map with D3.js (full teardown here) (official site) for a local council page. It was a choropleth (a color map) with a linked bar chart. When you hovered over a district on the map, the bars changed. Felt slick.

What I liked:

  • Total control. I tuned scales, axes, and transitions.
  • Smooth motion on hover. No jank.

What made me sigh:

  • It took time. Like, “late night coffee” time.
  • More code to maintain.
  • Accessibility needs extra care.

But hey, when you need custom stuff—like odd color breaks or weird labels—D3 lets you do it.

ECharts handled 60,000 points without crying

I had a power grid dashboard with 60,000 sensor points per series. Chart.js struggled. ECharts stayed smooth. Pinch-to-zoom on mobile felt great. I used data zoom, sampling, and a built-in dark theme.

What I liked:

  • Big data felt fine.
  • Built-in zoom, pan, and tooltip formatter.
  • Themes without fuss.

What I didn’t love:

  • The config can get long.
  • Bundle size is bigger than Chart.js.

If you’ve got lots of time series and want it to run well on phones, ECharts is a good bet. (I also toyed with JavaScript spider charts here—fun, but not for 60 k points!)

Highcharts for my finance team (they love exports)

Our finance folks needed clean line charts with data grouping, and they wanted one-click export to PDF and PNG. Highcharts nailed it. I wired up date pickers, added series toggles, and the charts looked “board ready.” For a deeper dive into finance dashboards, see how I built a tiny trading dashboard.

What I liked:

  • Built-in export that just works.
  • Good keyboard support and ARIA labels.
  • Data grouping helps with large date ranges.

Heads up:

  • License cost for commercial use.
  • Styling can feel a bit “Highcharts-y” unless you theme it.

But when execs say “Can we print this?” it’s nice to say yes.

Recharts for a React sprint (ship it fast)

I had a React app with a tight deadline. Recharts helped me ship in two days. I stacked bars, added custom tooltips, and used ResponsiveContainer so it looked good on any screen. No wrestling with refs. (I later swapped in radar charts for a KPI view—zero drama.)

What I liked:

  • Composable components (BarChart, Line, Tooltip, Legend).
  • Plays nice with React state.
  • Easy to test.

Tricky bits:

  • Heavy custom labels got messy.
  • Not as fast with very large data.

Still, for everyday product work in React, it’s a win.

ApexCharts for candlesticks and mixed charts

I built a small trading view: candlestick with volume bars below, plus a mini brush chart to zoom. ApexCharts felt made for this.

What I liked:

  • Candles look clean out of the box.
  • Mixed charts are simple.
  • Nice crosshairs and annotations.

Watch-outs:

  • Some docs pages felt light on edge cases.
  • Styling tiny details took hunting.

If you do finance charts, this one feels comfy.

Plotly.js for lab-style charts (3D and stats)

A research team needed a 3D scatter with hover labels and a density contour. Plotly.js did both, plus box plots, without me writing custom math. It felt like a Swiss Army knife. I also knocked out a quick bubble chart walkthrough while testing sizes and color scales—Plotly handled it fine.

What I liked:

  • Lots of chart types, especially for science.
  • Complex tooltips and hover modes.
  • Good for notebooks and quick prototypes.

Downside:

  • Big bundle. Slower loads.
  • The UI felt a bit heavy for small sites.

For research dashboards, it’s a strong pick.

uPlot when I had 1 million points (yes, a lot)

I tested a log viewer with 1 million points. Most libraries tapped out. uPlot was tiny and fast. But it’s bare. I had to wire my own legend, color scales, and downsampling.

What I liked:

  • Speed. Like, real speed.
  • Minimal footprint.

What I missed:

  • Fancy tooltips, themes, and helpers.
  • Built-in zoom polish.

Use it when speed is king and you can build the rest.

Performance notes from the trenches

  • SVG vs Canvas: For many points, Canvas wins. D3 SVG can lag with big sets.
  • Downsample on large time series. I use “min-max” buckets per pixel.
  • Lazy load charts. I load heavy libs only on pages that need them.
  • Avoid reflow storms. In React, memoize data and keep chart props stable.
  • Timelines your thing? Free Gantt chart libraries save a ton of custom work.

You know what? Sometimes the “boring” stuff—like cleaning data—matters more than the library.

If you’re pulling market or classifieds data to visualize—say, tracking how many listings appear in each city—having all the source URLs in one place saves hours of scraping prep; this exhaustive list of Craigslist sites gives you every city sub-domain in one shot, so you can automate the fetch and get straight to charting the trends.

Similarly, if your analysis zeroes in on adult classifieds traffic along the South Carolina coast, scraping the Myrtle Beach feed at Mega Personals Myrtle Beach lets you pull a focused, up-to-date dataset of posts you can pivot by date or category before feeding it into your charting layer.

Accessibility and touch, because people

Published
Categorized as Hard Coding

I Tried Making an ECG Chart with JavaScript. Here’s What Worked (and What Didn’t)

I build little health dashboards for clinics. I also teach kids to code on weekends. Funny mix, right? So I had to draw a live ECG line on a web page. Not a fake one. A real, moving line that keeps up with the heart. Smooth, fast, and clear.

If you’d like the full play-by-play of that initial experiment, you can read this deep-dive on building an ECG chart with JavaScript.


My Setup (real, messy, and honest)

  • Laptop: MacBook Air M2, 16 GB RAM
  • Browser: Chrome 127
  • Also tested on a cheap Windows mini PC and a Raspberry Pi 4
  • Data: 1-lead at 360 Hz and 500 Hz; then 12-lead at 500 Hz
  • Window size: 10 seconds on screen (so 5,000 points per lead at 500 Hz)

I also spilled coffee once and had to restart Chrome. That counts as “real life,” right?


What I Tested

  • Smoothie Charts (good for live streams)
  • uPlot (very fast, tiny, plain)
  • Chart.js (nice and friendly, but can get slow)
  • ECharts (feature rich; looks great)
  • Plotly (shiny; great for analysis and zoom)
  • D3 (roll-your-own; very flexible)
  • Raw Canvas (DIY; fastest if you keep it lean)
  • LightningChart JS (very fast; paid tier for big stuff)
  • JSCharting (robust commercial library; I didn’t benchmark it for ECG yet, but many teams swear by its real-time performance)

By the way, if you need a charting library purpose-built for ECG and other biosignals, take a look at EJsChart — it’s specialized for medical waveforms and trims away a lot of the plumbing.

I’ve used each of these in real projects. Some twice. Some only once. Some I still use every week.

I also published a broader rundown of the pros and cons when I tried a bunch of JavaScript chart libraries and noted what actually worked.


What I Needed the ECG Chart to Do

  • Keep 60 FPS when streaming 500 samples per second
  • Show 10 seconds of data with no jitters
  • Let me zoom and scroll when I pause
  • Handle 12 leads without my laptop begging for mercy
  • Look like a proper ECG: clean line, no jagged edges, steady baseline

Seems fair, right?


The Quick Winners

1) Smoothie Charts — Easiest Live Feed

I used Smoothie Charts to get a “it works!” demo in 10 minutes. It streams well and feels made for this.

What I liked:

  • Dead simple live line
  • Good on low power devices
  • Very little code

What bugged me:

  • Hard to show 12 leads without extra work
  • Not the best for zoom, pan, or fancy labels

Real code I used for a 1-lead feed at 500 Hz:

<canvas id="ecg" width="900" height="200"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/smoothie/1.36.0/smoothie.min.js"></script>
<script>
  const canvas = document.getElementById('ecg');
  const chart = new SmoothieChart({
    millisPerPixel: 2,       // ~10s on 1000px
    grid: { strokeStyle: '#ddd', verticalSections: 10 },
    interpolation: 'linear',
    maxValue: 2.0, minValue: -2.0
  });
  chart.streamTo(canvas, /* delay */ 0);

  const line = new TimeSeries();
  chart.addTimeSeries(line, { strokeStyle: 'rgba(200,0,0,1)', lineWidth: 2 });

  // fake sample source: 500 Hz
  setInterval(() => {
    const now = Date.now();
    const sample = Math.sin(now / 15) * 0.4 + (Math.random() - 0.5) * 0.02;
    line.append(now, sample);
  }, 2);
</script>

On my MacBook, this held 60 FPS for 1 lead. On Raspberry Pi 4, it was still smooth.


2) uPlot — Best Raw Speed for Many Points

uPlot is plain Canvas and very fast. It uses low memory. It doesn’t hand-hold you. That’s fine by me.

What I liked:

  • Super fast for 10-second windows
  • Great for 12 leads if you draw them in small strips
  • Small library; loads quick

What bugged me:

  • You wire up streaming yourself
  • Zoom is there, but you’ll tweak it

Real code from my clinic viewer (1 lead stream):

<link rel="stylesheet" href="https://unpkg.com/uplot/dist/uPlot.min.css">
<div id="ecg"></div>
<script src="https://unpkg.com/uplot/dist/uPlot.iife.min.js"></script>
<script>
  const N = 5000; // 10s * 500Hz
  const t = new Array(N);
  const y = new Array(N).fill(0);
  const start = Date.now();

  for (let i = 0; i < N; i++) t[i] = i;

  const u = new uPlot({
    width: 900,
    height: 220,
    series: [
      {},
      { label: "Lead I", stroke: "red", width: 2 }
    ],
    axes: [
      { scale: "x", values: (u, ticks) => ticks.map(v => (v/500).toFixed(1) + "s") },
      { scale: "y", space: 40, values: (u, ticks) => ticks.map(v => v.toFixed(1) + " mV") }
    ],
    scales: { x: { time: false }, y: { auto: true } }
  }, [t, y], document.getElementById('ecg'));

  let idx = 0;
  function pushSample(sample) {
    y[idx % N] = sample;
    idx++;
    // rotate window logically without copying
    const off = idx % N;
    const tView = t.map((_, i) => i);
    const yView = y.slice(off).concat(y.slice(0, off));
    u.setData([tView, yView]);
  }

  function tick() {
    // fake 500 Hz
    for (let i = 0; i < 8; i++) { // batch a bit
      const now = Date.now() - start;
      const s = Math.sin(now / 15) * 0.4 + (Math.random() - 0.5) * 0.02;
      pushSample(s);
    }
    requestAnimationFrame(tick);
  }
  tick();
</script>

Note: For 12 leads, I made 12 small charts stacked, all sharing time. Still smooth.

That sprint was part of a head-to-head where I pitted seven different JavaScript chart libraries against each other to see which could really keep up.


The “Nice but Careful” Picks

Chart.js

Chart.js looks friendly and feels safe. For slow data it’s great. But at 500 Hz with long windows, it lagged for me.

  • 1 lead at 500 Hz: OK for short windows
  • 12 leads: it got choppy; redraws were heavy

Plotly

I love Plotly for zoom and explore time. For live ECG? It was heavy after a few minutes with many points. Nice for playback, though.

ECharts

Pretty, rich, feature packed. For streaming ECG, it felt heavier than uPlot and Smoothie. Still fine for 1–3 leads.

D3 (custom)

I built one with D3 Paths. It worked but took more time. I had to tune it a lot. I wouldn’t start here unless I need full control.

If you’re curious about that rabbit hole, here’s the write-up on what actually worked (and didn’t) when I built charts with D3.js.

LightningChart JS

This one flew. WebGL helps. It handled 12 leads like a champ on my laptop. But if you need full features, you’ll hit the paid tier. Worth it for bigger teams or long-term use.


Raw Canvas: Fast, But You Become The Library

When I needed rock-solid speed for 12 leads, I wrote a tiny Canvas renderer. It’s not pretty, but it runs fast and steady.

What I liked:

  • Fast even on weak machines
  • I control every pixel
  • Easy to keep garbage collection low

What I dealt with:

  • I had to manage buffers and time axes
  • More code to maintain

If you’re

Published
Categorized as Hard Coding

I Tried Free JavaScript Charts So You Don’t Have To

I make lots of tiny dashboards. Some for work notes. Some for home stuff, like tracking snack costs for my kid’s soccer team. Charts help me see what’s going on fast. If you’re curious about even more nitty-gritty details, I logged the whole saga in this separate deep-dive on free JavaScript chart libraries.

But which free chart tool actually feels good to use?

Here’s what I learned, with real examples you can copy.


My quick setup (so you know the vibe)

  • Data size: small arrays up to a few thousand points
  • Devices: old ThinkPad, an iPhone SE, and a cheap Android
  • Needs: fast start, tooltips, labels, and easy colors
  • Bonus: I like nice defaults. I don’t want to fuss all day

While we’re on the topic of quick, low-overhead tools, students collaborating on class dashboards often need an equally friction-free space to discuss their findings in real time. For that, consider InstantChat for College—it gives campus groups free, private chat rooms, file sharing, and instant notifications so your project team can iterate on charts without endless email threads.

Simple, right? Now the charts.


Chart.js — the easy friend

Chart.js (official site) feels friendly. It looks good out of the box. The docs are clear. I can make a clean bar chart in minutes. Chart.js also placed near the very top when I tried seven different JavaScript chart libraries side-by-side.

What I like:

  • Good defaults and legends
  • Smooth hover and tooltips
  • Plugins for stuff like data labels

What bugged me:

  • Custom shapes take extra work
  • The config can feel deep once you get fancy

Tiny example (a bar chart for monthly snacks):

<canvas id="snackBar"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('snackBar');
new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Jan','Feb','Mar','Apr'],
    datasets: [{
      label: 'Snack Spend ($)',
      data: [42, 31, 58, 36],
      backgroundColor: '#4e79a7'
    }]
  },
  options: { responsive: true, plugins: { legend: { display: true } } }
});
</script>

It just works. And sometimes that’s all you need.


Apache ECharts — flashy and loaded with stuff

ECharts (official docs) has range. Maps, heatmaps, gauges—lots. It runs smooth, even with many points. Great for “wow” charts. I first appreciated how flexible ECharts can be when I tested a bunch of open-source chart tools.

What I like:

  • Big feature set
  • Themes look sharp
  • Handles big data better than most

What bugged me:

  • The config object gets long
  • I still peek at docs a lot

One pleasant surprise: ECharts can even power project-timeline visuals like Gantt bars, which I unpack in this look at free Gantt chart libraries.

Line chart with a subtle gradient:

<div id="lineChart" style="height:300px"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script>
const chart = echarts.init(document.getElementById('lineChart'));
chart.setOption({
  title: { text: 'Daily Steps' },
  xAxis: { type: 'category', data: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'] },
  yAxis: { type: 'value' },
  series: [{
    type: 'line',
    data: [5200, 6100, 6800, 7200, 4900, 8000, 7600],
    areaStyle: {}
  }],
  tooltip: { trigger: 'axis' }
});
</script>

It feels fast and glossy. Like a power suit, but for charts.


D3.js — total control, but you’ll work for it

D3 is a toolbox, not a chart kit. You get control over every mark. Every scale. Every tick. But it takes time. I use it when I need a custom look. My full field report on where D3 shines (and where it trips you up) lives over here: building charts with D3.js—what actually worked.

What I like:

  • Full control
  • Smart helpers (scales, axes)
  • Great for special charts

What bugged me:

  • More code for simple stuff
  • You handle layout and labels yourself

Simple bar chart:

<svg id="d3bar" width="400" height="220"></svg>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
const data = [42, 31, 58, 36];
const labels = ['Jan','Feb','Mar','Apr'];
const w=400, h=220, p=30;
const x = d3.scaleBand().domain(labels).range([p, w-p]).padding(0.2);
const y = d3.scaleLinear().domain([0, d3.max(data)]).nice().range([h-p, p]);

const svg = d3.select('#d3bar');
svg.append('g').attr('transform', `translate(0,${h-p})`).call(d3.axisBottom(x));
svg.append('g').attr('transform', `translate(${p},0)`).call(d3.axisLeft(y));

svg.selectAll('rect')
  .data(data).enter().append('rect')
  .attr('x', (_,i)=>x(labels[i]))
  .attr('y', d=>y(d))
  .attr('width', x.bandwidth())
  .attr('height', d=>h-p - y(d))
  .attr('fill', '#f28e2b');
</script>

If you want total freedom, this is it. Just bring patience. And if your data calls for shiny bubble visuals, here’s how that went for me in a real project—building bubble charts in JavaScript.


Recharts (for React folks) — props, props, props

If you live in React, Recharts feels natural. You pass props and get charts. It’s simple to read and easy to tweak.

What I like:

  • Composable parts
  • Clear props for common needs
  • Good for dashboards in React

What bugged me:

  • You’ll need React
  • Custom shapes still take time

Quick area chart:

import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';

const data = [
  { month: 'Jan', sales: 12 },
  { month: 'Feb', sales: 18 },
  { month: 'Mar', sales: 25 },
  { month: 'Apr', sales: 20 }
];

export default function SalesArea() {
  return (
    <ResponsiveContainer width="100%" height={250}>
      <AreaChart data={data}>
        <XAxis dataKey="month" />
        <YAxis />
        <Tooltip />
        <Area dataKey="sales" stroke="#59a14f" fill="#59a14f" fillOpacity={0.3} />
      </AreaChart>
    </ResponsiveContainer>
  );
}

Feels like Lego blocks for charts.


ApexCharts — business-y and fast to ship

ApexCharts gives you nice touches right away. Crosshairs, annotations, sparkline style—handy for reports. It’s also one of my go-tos when I need finance-flavored visuals like candlesticks; you can see the blow-by-blow in this test of seven JavaScript candlestick chart options.

What I like:

  • Pretty out of the box
  • Annotations and markers are simple
  • Good interactions

What bugged me:

  • Defaults look a bit “business”—you may tweak fonts
  • Events are good, but deep custom shapes are limited

Basic area chart:

“`html

const options = {
chart: { type: ‘area’, height: 260 },
series: [{ name: ‘Orders’, data: [15, 22, 19, 31, 28, 35] }],
xaxis: { categories: [‘Jan’,’Feb’,’Mar’,’Apr’,’May’,’Jun’] },
dataLabels: { enabled: false },
stroke: { curve: ‘

Published
Categorized as Hard Coding

I Used Google Charts JavaScript API. Here’s My Take.

I built a small dashboard last week, then another for a client shop. Both ran on Google Charts. I’ll keep it real. It was fast, fun, and a little fussy in a few spots.
More details landed in a dedicated write-up that you can skim here: I Used Google Charts JavaScript API—Here’s My Take.

You know what? I liked it more than I thought I would.

The very short version

  • Good stuff: quick setup, lots of chart types, smooth tooltips, easy image export.
  • Not so great: needs Google’s loader online, big data can feel slow, “Material” charts miss options, dates can be tricky.

Why I tried it

I had a sales story to tell. A simple one: months, totals, and a few spikes that needed context. I didn’t want a giant build. I wanted a chart on screen in minutes. This did that.

Setup in minutes (for real)

Here’s how I got a line chart on screen. This is the exact code I used in my test page. If you want the official step-by-step, Google’s own Quick Start guide walks through the same process in just a few lines.

<!-- Include the loader once -->
<script src="https://www.gstatic.com/charts/loader.js"></script>

<div id="line-chart" style="height:320px;"></div>

<script>
  google.charts.load('current', { packages: ['corechart'] });
  google.charts.setOnLoadCallback(draw);

  function draw() {
    const data = google.visualization.arrayToDataTable([
      ['Month', 'Sales'],
      ['Jan', 1200],
      ['Feb', 1350],
      ['Mar', 1420],
      ['Apr', 980]
    ]);

    const options = {
      title: 'Shop Sales',
      curveType: 'function',
      legend: { position: 'bottom' },
      height: 300,
      colors: ['#1a73e8'],
      chartArea: { width: '80%', height: '70%' }
    };

    const el = document.getElementById('line-chart');
    const chart = new google.visualization.LineChart(el);
    chart.draw(data, options);
  }
</script>

It loaded fast. The default styles looked clean. No fuss. Nice.

Real project: sales dashboard for a coffee shop

I tracked monthly bean sales, daily drink counts, and a top-flavors pie. I mixed chart types, all on one page. For a quick visual overview of every chart Google offers, the Chart Gallery is a handy reference.
If your dashboard ever needs an org structure instead of sales numbers, see how I built a JavaScript organizational chart three different ways and what finally stuck.

  • A line chart for monthly sales
  • A column chart for daily drinks
  • A pie chart for flavors (vanilla won, by a lot)

Column chart with click events

I wanted to click a bar and show the number. So I hooked into “select.” Worked fine.

<div id="drinks" style="height:320px;"></div>
<script>
  google.charts.setOnLoadCallback(drawDrinks);

  function drawDrinks() {
    const data = google.visualization.arrayToDataTable([
      ['Day', 'Drinks'],
      ['Mon', 210],
      ['Tue', 235],
      ['Wed', 250],
      ['Thu', 195],
      ['Fri', 310]
    ]);

    const options = {
      title: 'Drinks per Day',
      height: 300,
      legend: 'none',
      colors: ['#ea4335']
    };

    const el = document.getElementById('drinks');
    const chart = new google.visualization.ColumnChart(el);
    chart.draw(data, options);

    google.visualization.events.addListener(chart, 'select', () => {
      const sel = chart.getSelection()[0];
      if (!sel) return;
      const day = data.getValue(sel.row, 0);
      const count = data.getValue(sel.row, 1);
      alert(`${day}: ${count}`);
    });
  }
</script>

Simple. It felt “clicky” in a good way.

Live data: I updated every 15 seconds

I pulled fresh numbers from my own API. I had to rebuild the DataTable, then redraw. It was smooth for small data.
For bigger timelines—think project plans instead of coffee orders—I recently tried free Gantt chart JavaScript libraries so you don’t have to.

<div id="live" style="height:320px;"></div>
<script>
  google.charts.setOnLoadCallback(startLive);

  function startLive() {
    const el = document.getElementById('live');
    const chart = new google.visualization.LineChart(el);
    const options = { title: 'Live Orders', legend: 'none', height: 300 };

    function fetchAndDraw() {
      fetch('/api/orders.json')
        .then(r => r.json())
        .then(rows => {
          // rows example: [['12:00', 5], ['12:05', 7], ...]
          const data = new google.visualization.DataTable();
          data.addColumn('string', 'Time');
          data.addColumn('number', 'Orders');
          data.addRows(rows);
          chart.draw(data, options);
        })
        .catch(console.error);
    }

    fetchAndDraw();
    setInterval(fetchAndDraw, 15000);
  }
</script>

Tip: don’t redraw every second. Your fans will spin. Your users will frown.

Resize quirks and my tiny fix

When I resized the window, the chart looked cramped. So I throttled redraws. This kept it snappy.

<script>
  function debounce(fn, wait) {
    let t;
    return function() {
      clearTimeout(t);
      t = setTimeout(fn, wait);
    };
  }

  // Example: redraw an existing chart with stored data/options
  // window.addEventListener('resize', debounce(() => chart.draw(data, options), 150));
</script>

I know, it’s a small thing. But it helps.

Better labels and money format

Price looks nicer with commas and a dollar sign. This took one line.
Speaking of finance, I also tested seven JavaScript candlestick charts to find which ones nail the tiny highs and lows.

<script>
  // After you build your DataTable:
  const formatter = new google.visualization.NumberFormat({
    prefix: '$',
    groupingSymbol: ',',
    fractionDigits: 0
  });
  // Format column index 1 (Sales)
  formatter.format(data, 1);
</script>

I also added annotations for quick read. People love tiny labels right on the bar.

const data = google.visualization.arrayToDataTable([
  ['Month', 'Sales', { role: 'annotation' }],
  ['Jan', 1200, '$1.2k'],
  ['Feb', 1350, '$1.35k'],
  ['Mar', 1420, '$1.42k']
]);

Export to PNG for reports

My client wanted a PDF report. I clicked a button and grabbed the image URI.

<button id="save">Save Chart Image</button>
<script>
  // After you create your chart:
  document.getElementById('save').addEventListener('click', () => {
    const uri = chart.getImageURI();
    // You can open it or send it to your backend
    window.open(uri, '_blank');
  });
</script>

Worked great for email and print.

Dates: a tiny gotcha that bit me

For time charts, you use Date objects. JavaScript months start at 0. January is 0. Yep.

const data = new google.visualization.DataTable();
data.addColumn('date', 'Day');
data.addColumn('number', 'Sales');

data.addRow([new Date(2024, 0, 1), 120]); // Jan 1, 2024
data.addRow([new Date(2024, 0, 2), 150]); // Jan 2, 2024

I messed this up once and laughed at myself later. It happens.

Material vs Classic: I switched back

Material charts look fresh. But they miss some options. I needed more control, like annotations and series tweaks. So I used the “Classic” ones:

  • Material: google.charts.Bar
  • Classic: google.visualization.ColumnChart

Classic had the knobs I needed. It felt safer for a custom dashboard.
If you’re shopping around, my roundup where I tried seven JavaScript chart libraries might save you an afternoon.

Performance notes from my screen

  • Under 2,000 rows? Fine. Smooth tooltips.
Published
Categorized as Hard Coding

I made real charts with D3.js — here’s how it felt

I’m Kayla. I make web charts for real folks. Small teams. Local shops. A few teachers. I used D3.js this spring and summer on three real projects. It was fun, messy, and pretty powerful. You know what? I learned a lot. Turns out I'm not the only one—the folks in this candid D3 field report came away with very similar feelings.

Let me explain what worked, what didn’t, and show real code I used. Spoiler: another dev documented exactly what worked (and what absolutely didn't) when building charts with D3, and my notes line up.

Why I picked D3.js

  • I wanted full control over shapes, colors, axes, and little details.
  • I needed print-ready SVG.
  • I had to match brand colors, not just use a default theme.

I tried chart kits before. They were fast but stiff. Earlier, I'd tried a bunch of JavaScript chart libraries and ran into the same “fast-but-rigid” trade-off. With D3, I could make weird stuff like a stepped line with a glow. It took more time, but the charts looked “ours,” not “template.”
If you ever need a middle ground—quick setup without losing customization—consider the lightweight EJSChart toolkit that wraps common patterns while leaving room for creativity.

My first win: a simple bar chart for a school report

A school counselor asked me for a clean bar chart of attendance by weekday. No fancy flair. It had to paste into Google Docs and print without blur. SVG fit the bill.
If you’d like a deeper, code-heavy walkthrough of crafting bar charts, the guide at RisingStack’s “D3.js Tutorial: Bar Charts with JavaScript” is an excellent companion.

Data looked like this:

const data = [
  { day: "Mon", value: 23 },
  { day: "Tue", value: 31 },
  { day: "Wed", value: 28 },
  { day: "Thu", value: 35 },
  { day: "Fri", value: 19 }
];

Here’s the core code I used. I ran this in a tiny HTML page with a script tag, plus d3 from a CDN.

<svg id="chart" width="560" height="320"></svg>
<script>
const svg = d3.select("#chart");
const width = +svg.attr("width");
const height = +svg.attr("height");
const margin = { top: 20, right: 20, bottom: 40, 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.day))
  .range([0, w])
  .padding(0.1);

const y = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.value)]).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.day))
  .attr("y", d => y(d.value))
  .attr("width", x.bandwidth())
  .attr("height", d => h - y(d.value))
  .attr("fill", "#4f46e5"); // a calm indigo
</script>

It printed sharp. The counselor smiled. I went for a walk. Small wins matter.

The bakery job: a line chart with real dates

My neighbor runs a tiny bakery. Summer pie season went wild, and she wanted to track daily sales. I needed real dates, nice ticks, and a line that eased through the week.

const parse = d3.timeParse("%Y-%m-%d");
const sales = [
  { date: "2025-06-01", value: 120 },
  { date: "2025-06-02", value: 98  },
  { date: "2025-06-03", value: 143 },
  { date: "2025-06-04", value: 170 },
  { date: "2025-06-05", value: 132 }
].map(d => ({ date: parse(d.date), value: d.value }));

const svg = d3.select("#line").attr("width", 640).attr("height", 360);
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const w = +svg.attr("width") - margin.left - margin.right;
const h = +svg.attr("height") - margin.top - margin.bottom;
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);

const x = d3.scaleTime()
  .domain(d3.extent(sales, d => d.date))
  .range([0, w]);

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

g.append("g").attr("transform", `translate(0,${h})`).call(d3.axisBottom(x).ticks(5));
g.append("g").call(d3.axisLeft(y).ticks(5));

const line = d3.line()
  .x(d => x(d.date))
  .y(d => y(d.value))
  .curve(d3.curveMonotoneX);

g.append("path")
  .datum(sales)
  .attr("fill", "none")
  .attr("stroke", "#10b981") // teal
  .attr("stroke-width", 2)
  .attr("d", line);

I added a thin grid later with light gray lines. It made the chart easier to read when printed for a county fair flyer. Silly detail, big difference.

A tricky one: scatter plot with tooltips

A teacher wanted to show study time vs. test score. She asked, “Can we hover and see the student name?” Yes. But tooltips can get weird. They jump. They clip. They hide under the chart. Mine did too. I fixed it with a simple absolute-positioned div.

“`html

.tooltip {
position: absolute;
pointer-events: none;
background: #111;
color: #fff;
padding: 6px 8px;
border-radius: 4px;
font-size: 12px;
opacity: 0;
transition: opacity .15s;
}

const points = [
{ name: “Ana”, hours: 2, score: 71 },
{ name: “Luis”, hours: 5, score: 88 },
{ name: “Maya”, hours: 3, score: 79 },
{ name: “Jin”, hours: 6, score: 92 }
];

const svg = d3.select(“#scatter”);
const tip = d3.select(“#tip”);
const margin = { top: 20, right: 20, bottom: 40, left: 40 };
const w = +svg.attr(“width”) – margin.left – margin.right;
const h = +svg.attr(“height”) – margin.top – margin.bottom;
const g = svg.append(“g”).attr(“transform”, `translate(${margin.left},${margin.top})`);

const x = d3.scaleLinear().domain([0, d3.max(points, d => d.hours)]).nice().range([0, w]);
const y = d3.scaleLinear().domain([60, d3.max(points, d => d.score)]).nice().range([h, 0]);

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

const color = d3.scaleOrdinal(d3.schemeTableau10);

g.selectAll(“circle”)
.data(points)
.join(“circle”)
.attr(“cx”, d => x(d.hours))
.attr(“cy”, d => y(d.score))
.attr(“r”, 5)
.attr(“fill”, d => color(d.name))
.on(“mousemove”, (event, d) => {
const [px, py] = d3.pointer(event, svg.node());
tip.style(“left”, (px + 12) + “px”)
.style(“top”, (py + 12) + “px”)
.style(“opacity”, 1

Published
Categorized as Hard Coding

My Hands-On Review: JavaScript Gantt Chart Libraries I Actually Used

I build web tools for teams. I live in sprints and launch dates. I needed a Gantt chart that didn’t fight me. So I tested a few JavaScript libraries on a real project timeline. I used the same data set and tried them in Chrome, Safari, and Edge. I checked drag-and-drop, zoom, tooltips, and print.

You know what? They all work. But they don’t feel the same.

For the blow-by-blow details of that week-long experiment, you can skim my unfiltered project journal where I logged every surprise and shortcut.


My quick test setup (so you know what I did)

  • Data size: 200 tasks, 20 dependencies
  • Features I needed: drag, resize, day/week/month views, custom tooltips, export
  • Stack: plain JS and a small React app
  • Timezones: one user in PST, one in CET (hello, DST)

For a lightning-fast sanity check, I also pasted the same dataset into EJSchart to eyeball rendering consistency across browsers.

I made a simple “Web Redesign” plan. It had design, dev, QA, and launch. Nothing wild, but real enough.


Frappe Gantt — small, clean, and fast

This one is light. It looks cute out of the box. It’s good for simple needs. I used it in a marketing roadmap. The team loved that it didn’t feel heavy.
For deeper tweaks, I kept the Frappe Gantt Chart Documentation open in another tab; it covers all the props and events.

How it felt: snappy. Dragging was smooth. The code stayed tidy.

Where it shined:

  • Easy setup
  • Nice default look
  • Great for 50–150 tasks

Where it hurt:

  • No built-in dependencies editing on the chart
  • Month view felt tight for long projects
  • Print is basic

Real code I used:

<div id="gantt"></div>
<script>
  const tasks = [
    { id: 'T1', name: 'Design', start: '2024-11-01', end: '2024-11-05', progress: 30 },
    { id: 'T2', name: 'Frontend', start: '2024-11-06', end: '2024-11-20', progress: 10, dependencies: 'T1' },
    { id: 'T3', name: 'Backend', start: '2024-11-08', end: '2024-11-22', progress: 0, dependencies: 'T1' },
    { id: 'T4', name: 'QA', start: '2024-11-23', end: '2024-11-28', progress: 0, dependencies: 'T2,T3' },
    { id: 'T5', name: 'Launch', start: '2024-11-30', end: '2024-11-30', progress: 0, dependencies: 'T4' }
  ];

  const gantt = new Gantt('#gantt', tasks, {
    view_mode: 'Week',
    date_format: 'YYYY-MM-DD',
    custom_popup_html: task => `
      <div class="popup">
        <h5>${task.name}</h5>
        <p>${task._start.toDateString()} → ${task._end.toDateString()}</p>
        <p>Progress: ${task.progress}%</p>
      </div>
    `
  });
</script>

Tiny note: I had to tweak CSS to keep long task names from wrapping weird on small screens.

If budget is a hard constraint on your project, you might like the short piece I put together on the free JavaScript Gantt chart libraries I vetted.


DHTMLX Gantt — packed with features, made for serious work

This one has range. It’s heavier, yes. But it does the job when things get complex. I used it for a construction schedule with a lot of links and baselines. It handled it.
When I hit advanced scenarios, the DHTMLX Gantt Chart Documentation spelled out each config flag, so I rarely got stuck.

How it felt: powerful. Setup took a bit longer. Worth it when you need more.

Where it shined:

  • Dependencies, baselines, critical path
  • Big data holds up
  • Good API for custom rules

Where it hurt:

  • Learning curve
  • Styling took time
  • Some features need a paid plan

Real code I used:

<div id="gantt_here" style="width:100%; height:420px;"></div>
<script>
  gantt.config.date_format = "%Y-%m-%d";
  gantt.config.scale_unit = "week";
  gantt.config.subscales = [{ unit: "day", step: 1, date: "%D %d" }];
  gantt.config.drag_move = true;
  gantt.config.drag_resize = true;
  gantt.init("gantt_here");

  gantt.parse({
    data: [
      { id: 1, text: "Design", start_date: "2024-11-01", duration: 5, progress: 0.3 },
      { id: 2, text: "Frontend", start_date: "2024-11-06", duration: 10, progress: 0.1 },
      { id: 3, text: "Backend", start_date: "2024-11-08", duration: 11, progress: 0.0 },
      { id: 4, text: "QA", start_date: "2024-11-23", duration: 6, progress: 0.0 },
      { id: 5, text: "Launch", start_date: "2024-11-30", duration: 1, progress: 0.0 }
    ],
    links: [
      { id: 10, source: 1, target: 2, type: "0" },
      { id: 11, source: 1, target: 3, type: "0" },
      { id: 12, source: 2, target: 4, type: "0" },
      { id: 13, source: 3, target: 4, type: "0" },
      { id: 14, source: 4, target: 5, type: "0" }
    ]
  });

  gantt.templates.task_text = (s, e, task) => `${task.text} (${Math.round(task.progress*100)}%)`;
</script>

A small thing I hit: DST shifts in March moved bars by a pixel in Safari. I fixed it by locking date formats and snapping to days.


Highcharts Gantt — pretty, polished, and great labels

If you care about look and feel, this one sings. It has rich labels and nice tooltips. I used it on a public roadmap page. Stakeholders liked the polish.

How it felt: clean. Tooltips were easy to shape. Printing was strong.

Where it shined:

  • Styling
  • Export to PNG/PDF
  • Smooth zoom

Where it hurt:

  • Data prep takes care
  • Dependencies add setup
  • License for full use

Real code I used:

<div id="container" style="height: 420px"></div>
<script>
  Highcharts.ganttChart('container', {
    title: { text: 'Web Redesign Plan' },
    xAxis: { currentDateIndicator: true },
    series: [{
      name: 'Tasks',
      data: [
        { id: 'd', name: 'Design', start: Date.UTC(2024,10,1), end: Date.UTC(2024,10,5) },
        { id: 'f', name: 'Frontend', start: Date.UTC(2024,10,6), end: Date.UTC(2024,10,20), dependency: 'd' },
        { id: 'b', name: 'Backend', start: Date.UTC(2024,10,8), end: Date.UTC(2024,10,22), dependency: 'd' },
        { id: 'q', name: 'QA', start: Date.UTC(2024,10,23), end: Date.UTC(2024,10,28), dependency: ['f','b'] },
        { id: 'l', name: 'Launch', start: Date.UTC(2024,10,30), end: Date.UTC(2024,10,30), dependency: 'q', milestone: true }
      ]
    }],
    tooltip: {
      pointFormatter() {
        const s = new Date(this.start).toDateString();
        const e = new Date(this.end).toDateString();
        return `<b>${this.name}</b><br/>${s} → ${e}`;
      }
    }
  });
</script>

I did spend time on UTC dates. But after that, the timeline felt rock solid.


DayPilot Gantt — fast to start, friendly API

This one let me ship a small internal tool in one afternoon. The API is gentle. The grid is neat. It did weekends and work hours well too.

How it felt: simple.

Published
Categorized as Hard Coding

I tried the big JavaScript chart tools. Here’s what actually worked.

I make charts for real people. Parents. Bosses. My own messy brain. I’ve used these tools at work and at home, from a school bake sale dashboard to a fintech app. Some days I need a quick line chart. Other days I need a map with thousands of points. And yes, I’ve broken things. Many times. For the full war story of trying every major chart tool under the sun, you can read my extended field report here.

So, I spent the last year bouncing between Chart.js, D3, ECharts, Highcharts, Recharts, Nivo, ApexCharts, Plotly.js, Vega-Lite, and a tiny bit of Visx. I’ll keep this simple and honest. If you want to see how these libraries stack up feature-by-feature, check out this comprehensive comparison of JavaScript charting libraries—it breaks down licenses, supported chart types, and more.

Quick note: I build on macOS and Windows, mostly in Chrome and Safari. I test on an old iPhone 11 because it loves to find bugs.


The short version (because you’re busy)

  • Fast and simple: Chart.js
  • React apps: Recharts or Nivo (Nivo looks nicer; Recharts feels calmer)
  • Huge data and maps: ECharts
  • Fancy, custom visuals: D3 (with patience)
  • Finance charts with a range slider: Highcharts (paid at work)
  • Science-style plots and 3D: Plotly.js
  • Spec-driven and tidy: Vega-Lite
  • React + full control toolbox: Visx (but bring snacks)

If you're curious how those libraries behaved across an even wider range of projects, my candid notes are here.

Side note: when I wanted a single-file library even tinier than Chart.js, EJSChart rendered a basic bar chart faster than I could brew my espresso.

I’ll explain with real code and tiny stories.


Chart.js — “20 minutes and done”

I built a sales line chart for a local coffee shop. It had daily sales and a moving average. Chart.js felt like making toast. It just worked.

What I liked:

  • Light, fast, and free (MIT).
  • Good docs. Plugins like DataLabels and Annotation help a lot.
  • Works fine on phones.

What bugged me:

  • Big data (like 50k points) is rough.
  • Layout gets tight with long labels. I had to rotate text.

Here’s a tiny chart I used in a weekly report:

<canvas id="sales"></canvas>
<script>
const ctx = document.getElementById('sales');
new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],
    datasets: [{ label: 'Sales', data: [12, 19, 7, 13, 22, 30, 25], borderColor: '#3b82f6' }]
  },
  options: { responsive: true, maintainAspectRatio: false }
});
</script>

I changed the colors and added a simple tooltip. Done. Coffee time.


D3 — “It’s hard, then it’s art”

I used D3 for a music blog to draw a chord diagram of collabs. It took a week. I watched three talks, cried a little, then felt proud.

Good things:

  • You can draw anything. SVG, Canvas, events, math, all there.
  • Animations look smooth.

Hard parts:

  • Big learning curve.
  • You write a lot of code.

A tiny bar chart I used to teach a friend:

<svg id="bars" width="300" height="120"></svg>
<script>
// data
const data = [5, 8, 13, 21, 34];

// draw
const svg = d3.select('#bars');
const x = d3.scaleBand().domain(data.map((_, i) => i)).range([0, 300]).padding(0.2);
const y = d3.scaleLinear().domain([0, d3.max(data)]).range([120, 0]);

svg.selectAll('rect')
  .data(data)
  .enter().append('rect')
  .attr('x', (_, i) => x(i))
  .attr('y', d => y(d))
  .attr('width', x.bandwidth())
  .attr('height', d => 120 - y(d))
  .attr('fill', '#10b981');
</script>

I thought D3 was “too much.” Then I needed a custom layout. Guess what? It was perfect.


ECharts — “Heavy, but wow”

For a city transit story, I built a live bus delay heatmap with about 300k rows. ECharts kept it smooth. Zoom and pan felt good on my phone too. When you're dealing with datasets at that scale, it helps to skim an in-depth survey on big data visualization tools to see which engines stay fast once the numbers climb. If seven different libraries in one project sounds familiar, check out my summary of what actually worked across 7 tools.

What I liked:

  • Great for big data and maps.
  • Themes look sharp.
  • Rich tooltips and legends.

What I didn’t:

  • Bundle is chunky.
  • Docs are deep, but I had to search a lot.

Tiny bar chart:

<div id="chart" style="height:300px;"></div>
<script>
const chart = echarts.init(document.getElementById('chart'));
chart.setOption({
  xAxis: { type: 'category', data: ['Mon','Tue','Wed','Thu','Fri'] },
  yAxis: { type: 'value' },
  series: [{ type: 'bar', data: [120, 200, 150, 80, 70], itemStyle: { color: '#ef4444' } }]
});
</script>

If you need heatmaps, scatter with thousands of points, or custom maps, this tool shines.


Highcharts (and Highstock) — “Polished, but you pay”

At a fintech job, we needed seven charts on one screen and a range slider for time series. Highstock was my pick. The a11y add-on also helped us meet rules. For stock-style candlesticks specifically, you can see which libraries passed my test here.

Why I liked it:

  • Great docs and examples.
  • Linked tooltips and synced zoom.
  • Solid accessibility module.

The catch:

  • License for commercial use.
  • Bundle size is not small.

If you need “just works” for business charts, it’s worth it. We shipped faster.


Recharts — “Friendly for React folks”

I used Recharts in a React admin page for a warehouse. Setup was calm. JSX felt natural. I compared several open-source chart tools in another write-up that you can skim here.

Pros:

  • Simple API. Good defaults.
  • Works with React state.
  • Easy to theme.

Cons:

  • SVG can slow with huge data.
  • On my iPhone, tooltips jittered on a long scroll view. I set isAnimationActive={false}. That helped.

Quick sample:

<ResponsiveContainer width="100%" height={300}>
  <LineChart data={data}>
    <XAxis dataKey="day" />
    <YAxis />
    <Tooltip />
    <Line type="monotone" dataKey="units" stroke="#6366f1" />
  </LineChart>
</ResponsiveContainer>

If you use React daily, this feels like home.


Nivo — “Pretty out of the box”

I used Nivo for a charity board. It looked good with almost no tweaks. The legends are clean. The docs are friendly.

Good:

  • Nice themes.
  • Many chart types.
  • Canvas versions help with speed.

Watch out:

  • Props can get deep.
  • Some charts need careful padding.

For a quick dashboard that still looks pro, Nivo is sweet.


ApexCharts — “Easy wins, neat gauges”

I built a speed gauge for a network monitor using ApexCharts. The gauge and sparkline charts took minutes. It plays well with React and Vue wrappers. If your budget is exactly zero, you might like my roundup of free JavaScript charts I tested.

Good:

  • Clear docs and examples.
  • Annotations and mixed charts are simple.

Not so good:

  • Big data gets heavy.
  • Some styles felt same-y. I tweaked CSS.

It’s a good pick for KPIs, gauges, and small dashboards.

Often I wind up charting unexpected datasets. A recent side gig had me visualizing engagement numbers for a dating platform that caters to fans of confident, mature women. If you’re curious about the kind of real-world

Published
Categorized as Hard Coding

Animated Pie Chart JavaScript: What Worked For Me, What Didn’t

Note: This is a fictional first-person review written in first person.

I built a small dashboard for a fundraiser. We had four buckets: Books, Snacks, Prizes, and Fees. I wanted a pie chart that moves, looks clean, and does not burn the phone. Sounds simple, right? Well, yes and no.
If you’re curious about the fuller back-story, you can read my complete deep-dive on what really worked (and what didn’t) when building an animated pie chart in JavaScript.

Here’s what I tried, what I liked, and where I got stuck. Plus real snippets you can run today.

My Setup (quick and plain)

  • Data: [40, 25, 20, 15] for 100%
  • Goal: smooth spin on load, clear labels, works on mobile
  • Bonus: safe colors, simple export, and no motion if a user asks for that

You know what? Small touches like label tips matter more than we think.


Chart.js: The 10-Minute Win

(For full documentation see Chart.js.)

Chart.js gave me a fast start. If you ever want a no-code route that still looks pro, take a peek at EJSChart — its hosted editor spits out an animated pie in under a minute. The default spin looks friendly, not wild. I used it first for a team demo, and it landed well.

Code I used

<canvas id="pie"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<script>
  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const ctx = document.getElementById('pie').getContext('2d');

  new Chart(ctx, {
    type: 'pie',
    data: {
      labels: ['Books', 'Snacks', 'Prizes', 'Fees'],
      datasets: [{
        data: [40, 25, 20, 15],
        backgroundColor: ['#4F46E5', '#06B6D4', '#10B981', '#F59E0B'],
        borderWidth: 0
      }]
    },
    options: {
      animation: reduce ? false : {
        animateRotate: true,
        duration: 1200,
        easing: 'easeOutCubic'
      },
      plugins: {
        legend: { position: 'bottom' },
        tooltip: {
          callbacks: {
            label: (ctx) => `${ctx.label}: ${ctx.parsed}%`
          }
        }
      }
    }
  });
</script>

What felt great

  • Fast setup. One file, one call.
  • Nice default easing. It’s smooth.
  • Legend and tooltips are built in.

What bugged me

  • Labels can crowd if you have many slices.
  • Percent text may wrap on small phones.
  • Export takes another step (grab the canvas as PNG).

If you’re hunting for completely free options, you might like the field notes I kept when I tried a bunch of open-source JavaScript chart tools.

A small tip: if your data does not sum to 100, format the label to show the real percent. Don’t fake it.


D3.js: When I Needed Custom Motion

(Get the official docs for D3 here.)

I wanted a slice to grow from the center and ease like a soft spring. D3 let me shape the arc, control the tween, and pick every tiny thing. It took more code, yes. But the chart felt alive.

Code I used

<svg id="svg" width="260" height="220"></svg>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
  const data = [40, 25, 20, 15];
  const names = ['Books','Snacks','Prizes','Fees'];
  const color = d3.scaleOrdinal()
    .domain(names)
    .range(['#4F46E5','#06B6D4','#10B981','#F59E0B']);

  const w = 260, h = 220, r = 90;
  const g = d3.select('#svg')
    .append('g')
    .attr('transform', `translate(${w/2},${h/2})`);

  const pie = d3.pie();
  const arc = d3.arc().innerRadius(0).outerRadius(r);

  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  const paths = g.selectAll('path')
    .data(pie(data))
    .enter()
    .append('path')
    .attr('fill', (d,i) => color(names[i]));

  if (reduce) {
    paths.attr('d', d => arc(d));
  } else {
    paths.transition()
      .duration(1200)
      .attrTween('d', function(d) {
        const i = d3.interpolate({startAngle: 0, endAngle: 0}, d);
        return (t) => arc(i(t));
      });
  }
</script>

What felt great

  • Full control of motion and shape.
  • Fancy stuff is possible (ring charts, pulled slices).
  • Great for data stories or custom styles.

What bugged me

  • More code to manage.
  • Labels and legends need extra work.
  • Easy to make it heavy if you pile on effects.

During that same sprint I built five separate donut charts to see which felt right, and I captured the results in a short roundup: I built 5 JavaScript donut charts—here’s what actually worked.

If you go D3, plan your frame budget. Keep slices under 12 for phones. Fade and rotate, don’t animate every pixel at once.


ECharts: Bold Look, Handy Labels

I used ECharts for a more “wow” style. The labels looked sharp, even on a big TV. Legend is clean. Percent tags are simple. It feels like a strong middle ground.

Code I used

<div id="ec" style="width: 320px; height: 260px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5"></script>
<script>
  const chart = echarts.init(document.getElementById('ec'));
  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  chart.setOption({
    animation: !reduce,
    animationDuration: 1000,
    tooltip: { trigger: 'item' },
    legend: { bottom: 0 },
    series: [{
      type: 'pie',
      radius: '70%',
      data: [
        { value: 40, name: 'Books' },
        { value: 25, name: 'Snacks' },
        { value: 20, name: 'Prizes' },
        { value: 15, name: 'Fees' }
      ],
      label: { formatter: '{b}: {d}%' }
    }]
  });
</script>

What felt great

  • Labels and legend look sharp by default.
  • Percent text is built in.
  • Good on high-res screens.

What bugged me

  • Bundle size is larger than Chart.js.
  • Custom motion is not as free as D3.
  • Theme tweaks took a little time.

ECharts also showed up in my larger benchmark where I tried seven different JavaScript chart libraries and logged what actually worked.


Small Things That Made A Big Difference

If you just want the TL;DR across multiple libraries, my longer recap of the whole experiment — I tried a bunch of JavaScript chart libraries, here’s what actually worked for me — covers most of these pointers in depth.

Colors and contrast

Use strong contrast. Blue and green can look the same for some folks. I leaned on purple, teal, green, and amber. Test it in dark mode too.

Motion safety

Some people get motion sick. Respect that.

  • Check prefers-reduced-motion and turn off spin.
  • Keep duration near 800–1200 ms.
  • Ease out. Don’t snap.

Labels and numbers

  • Show name and percent. Keep font above 12px on phones.
  • If total is not 100, show raw value in the tooltip.
  • Round to one decimal. 33.3% reads well.

Keyboard and screen readers

  • Add a title and aria-label near the chart.
  • Offer a small data table below the chart for screen readers.

Quick example:

“`html