I Built 5 JavaScript Donut Charts. Here’s What Actually Worked.

I’m Kayla, and yep, I made a bunch of donut charts last month. Work dashboards. A school fundraiser page. Even my own food log. I tried five tools: Chart.js, D3, ApexCharts, ECharts, and Highcharts. For a deeper side-by-side look at these libraries, check out this comprehensive comparison of JavaScript charting libraries.
Since then I’ve also kept my eye on a newer lightweight option, EJSChart, which promises the same smooth donuts with a fraction of the setup.

If you’d like an even deeper dive—including bundle sizes, build notes, and a few extra “gotchas” I skipped here—check out the full comparison post where I document every test run.

You know what? A donut chart looks simple. But little details matter. Labels wrap. Colors clash. Center text gets wonky. I learned the hard way, so you don’t have to.


What I needed, in plain words

  • Fast setup for a sales dashboard with 4–6 slices
  • Clean labels that don’t pile up on mobile
  • A center label that says “Total” or a short note
  • Good colors that work in dark mode
  • OK performance on a cheap Android phone
  • Screen reader text, at least the basics

Feeling short on interesting sample data to test your next donut? I recently played with dating-app response stats pulled from PlanCulFacile — they publish anonymised match and message metrics you can download as CSV, perfect for stress-testing any of the chart libraries below with a fresh, real-world dataset. For something with a bit more geographical spice, the classifieds-style dataset over at Backpage Crystal breaks down listing counts by city and category, giving you granular numbers you can segment into eye-catching donut slices.

I also cared about bundle size a bit, but not more than speed and clarity. If you’re hunting for benchmarks to back up your own choices, there’s an in-depth analysis of the performance and scalability of various JavaScript charting libraries that’s worth a skim.


Chart.js: my quick win (and my go-to)

I used Chart.js for a sales donut in our store dashboard. I had coffee, ten minutes, and a messy CSV. It still worked. It was fast, and the look was clean.

What I liked:

  • Very quick start
  • Colors look nice with simple tweaks
  • Animations feel smooth
  • Works well with React and plain HTML

What bugged me:

  • No built-in center text (you can add a tiny plugin)
  • Legends can wrap odd on very small screens

Here’s the exact snippet I used for “Sales by Category” (four slices). Took me under ten minutes, start to finish.

<canvas id="sales"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('sales');
new Chart(ctx, {
  type: 'doughnut',
  data: {
    labels: ['Shoes','Hats','Bags','Socks'],
    datasets: [{
      data: [45, 25, 20, 10],
      backgroundColor: ['#4F46E5', '#06B6D4', '#10B981', '#F59E0B'],
      borderWidth: 0
    }]
  },
  options: {
    cutout: '60%',
    plugins: {
      legend: { position: 'bottom' },
      tooltip: {
        callbacks: {
          label: (ctx) => {
            const total = ctx.dataset.data.reduce((a,b)=>a+b, 0);
            const val = ctx.parsed;
            const pct = Math.round((val/total)*100);
            return `${ctx.label}: ${val} (${pct}%)`;
          }
        }
      }
    },
    animation: { animateRotate: true, duration: 700 }
  },
  plugins: [{
    id: 'centerText',
    beforeDraw(chart) {
      const {ctx, chartArea} = chart;
      if (!chartArea) return;
      const x = (chartArea.left + chartArea.right) / 2;
      const y = (chartArea.top + chartArea.bottom) / 2;
      ctx.save();
      ctx.font = '600 14px system-ui';
      ctx.fillStyle = '#334155';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText('Sales', x, y);
      ctx.restore();
    }
  }]
});
</script>

Speed to first chart: about 5 minutes. Not joking.
Dark mode tip: switch text to lighter gray and nudge colors up a notch.


D3: full control, more work

This one is for folks who like to tinker. I used D3 for a fitness page where I needed exact sizes and a custom arc. I wanted a label in the center and a small legend at the top. No fluff.

What I liked:

  • Pixel-level control
  • Great for custom labels and arcs
  • Scales and color control feel pro

What bugged me:

  • More code
  • You must add ARIA and keyboard bits yourself

This is the “Macros” donut I built for my own meal plan:

<div id="macro"></div>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
const data = [
  { label: 'Protein', value: 32 },
  { label: 'Carbs', value: 48 },
  { label: 'Fat', value: 20 }
];

const width = 260, height = 260, r = 110;
const color = d3.scaleOrdinal().range(['#EF4444','#3B82F6','#10B981']);
const arc = d3.arc().innerRadius(65).outerRadius(r);
const pie = d3.pie().value(d => d.value);

const svg = d3.select('#macro').append('svg')
  .attr('viewBox', `0 0 ${width} ${height}`)
  .attr('role', 'img')
  .attr('aria-label', 'Daily macros donut chart')
  .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));

svg.append('text')
  .attr('text-anchor','middle')
  .attr('dy','0.35em')
  .style('font-weight','600')
  .text('Macros');
</script>

If you need the chart to say exactly what you see in your head, D3 hits the mark. But yes, it takes more time.


ApexCharts: neat defaults, nice center labels

I used ApexCharts for a budget report because it showed a “total” in the hole with no fuss. The legends felt tidy. The default font looked sharp too.

What I liked:

  • Center labels built in
  • Simple config for tooltips and legends
  • Handy responsive settings

What bugged me:

  • Some presets look “heavy” with too many effects
  • Need to tone down labels on tiny screens

Real budget example:

<div id="budget"></div>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
var options = {
  chart: { type: 'donut', height: 260 },
  series: [1200, 900, 300],
  labels: ['Rent','Food','Fun'],
  colors: ['#6366F1', '#22C55E', '#F97316'],
  dataLabels: { enabled: false },
  legend: { position: 'bottom' },
  plotOptions: {
    pie: {
      donut: {
        size: '60%',
        labels: { show: true, total: { show: true, label: 'Budget' } }
      }
    }
  },
  tooltip: { y: { formatter: val => '
#039; + val } },
  responsive: [{
    breakpoint: 480,
    options: { legend: { position: 'bottom' } }
  }]
};
new ApexCharts(document.querySelector('#budget'), options).render();
</script>

My boss liked this one best. The “Budget” total in the center sold it.


ECharts: rich, bold, and fast on mobile

For a school bake sale site, I used ECharts. It looked fancy with very little work, and it ran smooth on an old tablet. The theme support helped me match the school colors.

What I liked:

  • Very smooth on touch devices
  • Strong visuals
  • Good tooltip format out of the box

What bugged me:

  • The config can feel long
  • Labels can get busy if you show them all

Bake sale orders by channel:

“`html

var chart = echarts.init(document.getElementById(‘orders’));
var option = {
tooltip: { trigger: ‘item’, formatter: ‘{b}: {c} ({d}%)’ },
legend: { bottom: 0

Published
Categorized as Hard Coding

I built a tiny trading dashboard. Here’s what I learned about financial charts in JavaScript.

I’m Kayla. I track stocks and crypto for work and for fun. Last quarter, during earnings season, I built a small web app for my team. It had live candles, volume, a couple moving averages, and a clean dark theme. I tried a bunch of chart libs. Some made me smile. Some made me groan.

This is what actually worked for me—warts and all.

What I needed from a chart

  • Candles or OHLC, with volume below
  • Fast pan and zoom on 1-minute bars
  • Crosshair that lines up across panes
  • Live data over WebSocket
  • Dark mode that doesn’t look muddy
  • Easy markers for earnings and trades
  • Export to PNG for a quick deck slide

Choosing a chart library reminded me of swiping through dating apps—each candidate boasts flashy screenshots, but the real test is day-to-day chemistry. For a lighthearted yet methodical example of evaluating options with a critical eye, check out this detailed Tinder review that dissects features, pricing tiers, and user experience, giving you a framework for separating genuine matches from hype—whether you’re picking a charting tool or a date. If, instead of swipe-based apps, you’re curious about a more classifieds-style approach where listings are laid out like a ticker tape of possibilities, take a look at Backpage Ogden—there you’ll find a no-frills stream of local ads that you can scan quickly and connect with instantly, mirroring the way traders cut through noise to act on the clearest signals.

Simple ask, right? Well, kind of.


TradingView Lightweight Charts — fast and clean

This one felt like butter. No joke. I pushed two years of 1-minute BTC candles, plus volume, and it stayed smooth. Crosshair felt crisp. The time scale did not fight me. It just worked. I did all of this using the TradingView Lightweight Charts™ Library, which packs serious performance into a tiny bundle.

Real use: I ran live BTC/USDT with a WebSocket feed. I stacked two charts (price up top, volume below) and synced the crosshair. It looked like a mini terminal on my second monitor.

What I liked:

  • Speed. Big data stayed snappy.
  • Nice defaults. Candles look “trader real.”
  • Easy updates: series.update() feels natural.

What bugged me:

  • No built-in sub-panes in one chart. I had to stack two charts and sync events.
  • Not many built-in indicators. MA is easy, but RSI or MACD needs your code.
  • Custom tooltips take some elbow grease.

You know what? If you want fast price charts, this is my go-to.


Highcharts Stock — loaded with tools

This one is the Swiss Army knife. Need flags for earnings? Range selectors? A compare line to SPY? It’s all there. The Highcharts Stock documentation walks through each of these features with clear examples.

Real use: I built an earnings view for AAPL. Candles + volume. I added flags at earnings dates, and a 20/50 MA. The built-in navigator made scrubbing through years easy.

What I liked:

  • So many features. Annotations, flags, compare, exporting.
  • Range selector buttons made demos look good fast.
  • Types are solid; docs are clear.

What bugged me:

  • License costs for commercial work. Worth it for my job, but still a thing.
  • Big datasets (like 300k points) slowed in Firefox on my laptop.
  • Bundle size felt heavy for small apps.

If you need “everything,” this is the one. Great for reports and product demos.


ECharts — free, bold, and flexible

ECharts gave me power, for zero dollars. The candlestick series looks sharp. The toolbox and dataZoom are handy. I built a crypto heatmap next to a price chart and kept one theme.

Real use: I charted SPY with 1-minute bars and shaded premarket hours in gray. It looked slick on a TV in the office.

What I liked:

  • Huge feature set. Themes, zoom, rich tooltips.
  • Handles large data better than I expected.
  • It’s free. That helps side projects.

What bugged me:

  • Docs can feel dense. I got lost in options sometimes.
  • Time zones took fiddling. DST gave me a headache one morning.
  • Pinch zoom on my Pixel was a bit jittery.

If you like to shape things your way, and you want free, it’s strong.
For a purpose-built alternative focused on interactive stock charts, take a peek at EJSChart — it slots into a page with just a few lines of code. I documented the full process of dropping it into a tiny trading workflow in this hands-on build guide.


Chart.js (with chartjs-chart-financial) — simple and friendly

For small dashboards, Chart.js is sweet. Add the financial plugin for candles/OHLC. It plays nice with React. Tooltips and legends are clean. If you need to spice things up with non-financial visuals like KPI donuts, check out this walk-through of five JavaScript donut chart patterns that saved me time.

Real use: I built a tiny “watch four tickers” page for a teammate. One week of 5-minute bars, two MAs. It loaded fast and looked fine.

What I liked:

  • Low friction. Good docs.
  • Great for small, static sets.
  • Plugin system is clear.

What bugged me:

  • Not made for huge, live sets.
  • You’ll write your own RSI, MACD, and the like.
  • Zoom/pan needs extra plugins and tuning.

If your data is light and the UI is simple, it’s a nice fit.


ApexCharts — nice dev feel, some quirks

ApexCharts has a comfy API. The toolbar (zoom, pan, reset) is handy out of the box. Candles look clean. I liked the way annotations snap.

Real use: I made a trend view with candles, a 200-day MA, and volume on a second y-axis. Stakeholders liked the look.

What I liked:

  • Easy to stand up. Good defaults.
  • Annotations are friendly.
  • Works well in React and Vue.

What bugged me:

  • Huge data sets slowed after long sessions.
  • Candle width felt odd while zooming on mobile.
  • I hit a tooltip flicker when I had two y-axes.

Good for product screens where the data isn’t massive.


Little real-world bits that tripped me up

  • Time zones: My New York clock and the server’s UTC fought. Highcharts was easiest here. In ECharts, I had to format time labels by hand.
  • Crosshair sync: Lightweight Charts needed my own event bridge. It wasn’t hard, but it took care.
  • Market hours shading: Highcharts and ECharts made this easy. With Lightweight Charts, I drew custom background areas.
  • Color choices: A trader on my team is colorblind. I swapped red/green for blue/orange and boosted contrast. Tiny change, big win.
  • Mobile pinch zoom: ECharts and ApexCharts were okay. Lightweight Charts felt best under my thumb. Funny how that matters on the train.

Tiny code I used

Lightweight Charts: live candles with a WebSocket:

import { createChart } from 'lightweight-charts';

const chart = createChart(document.getElementById('c'), {
  layout: { background: { color: '#0f1115' }, textColor: '#cbd5e1' },
  timeScale: { rightOffset: 2, barSpacing: 6 },
  grid: { horzLines: { color: '#20232a' }, vertLines: { color: '#20232a' } },
});

const price = chart.addCandlestickSeries();
const vol = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: '' });

price.setData(seedCandles);
vol.setData(seedVolume);

const ws = new WebSocket('wss://example.com/feed');
ws.onmessage = (m) => {
  const { t, o, h, l, c, v } = JSON.parse(m.data);
  price.update({ time: t, open: o, high: h, low: l, close: c });
  vol.update({ time: t, value: v, color: c >= o ? '#10b981' : '#ef4444' });
};

Highcharts Stock: candles + volume + earnings flags:

“`js
Highcharts.stockChart('container', {
rangeSelector: { selected: 1 },
series: [
{ type: 'candlestick', name: 'AAPL', data: ohlcData },
{ type: 'column', name: 'Volume', data: volumeData, yAxis: 1 },
{ type: 'flags', data: earningsDates.map(d => ({ x: d, title: 'E' })), on

Published
Categorized as Hard Coding

I Tried JavaScript Spider Charts. Here’s What Actually Worked.

I had a simple job. Show skill scores on one funny-looking chart. You know, the one that looks like a web. A spider chart.

I tested a few tools on real stuff. Not just hello world.
Along the way, I discovered that EJSChart offers a surprisingly lightweight option focused solely on radar and spider charts, so it's worth keeping on your radar. For an in-depth rundown of the experiment, take a look at my write-up where I tried JavaScript spider charts and detailed what actually worked.

I used Chart.js, Apache ECharts, Recharts (React), and a tiny D3 setup when I needed full control. I made charts for a youth soccer team and also for a product workshop at work. Two very different rooms. Same shape.

And yes, I spilled coffee on a printout once. Still counts.


What I Needed (and Why I Cared)

  • Mobile taps had to feel smooth.
  • Labels had to fit, even long ones.
  • Printing to PDF shouldn’t look fuzzy.
  • Dark mode shouldn’t look muddy.
  • Sharing with a team had to be easy.

Seems basic, right? It wasn’t.


Chart.js Radar: Fast, Friendly, and Good Enough

For my soccer team, I used Chart.js 4 (see its official radar chart docs). It took me 10 minutes to get a clean spider chart. I tracked five skills: Speed, Stamina, Passing, Shooting, Defense. I gave each kid a score from 0 to 10. Boom—one chart per kid.

Here’s the tiny bit of code that worked for me:

new Chart(ctx, {
  type: 'radar',
  data: {
    labels: ['Speed', 'Stamina', 'Passing', 'Shooting', 'Defense'],
    datasets: [
      {
        label: 'Mia',
        data: [8, 6, 7, 5, 9],
        fill: true,
        backgroundColor: 'rgba(54,162,235,0.2)',
        borderColor: 'rgb(54,162,235)'
      }
    ]
  },
  options: {
    scales: {
      r: { min: 0, max: 10, ticks: { stepSize: 2 } }
    },
    plugins: { legend: { display: true } }
  }
});

What I liked:

  • It’s simple. The docs match what you see.
  • Touch tooltips felt okay on my phone.
  • Adding a second dataset (like “Team Avg”) took one more object. Easy.

What bugged me:

  • Long labels wrap weird on small screens.
  • If you push lots of axes, the center gets cramped.
  • Canvas prints can look a bit soft if you don’t bump pixel ratio.

Real use: I printed three charts for a Saturday team meetup. Parents got it at a glance. One parent even asked, “Can I get this for school grades?” I laughed, then said yes, but please don’t. Different fight. That same lightweight setup even powered a tiny trading dashboard I threw together—here’s exactly what I learned about financial charts in JavaScript.


Apache ECharts: Big Style, Big Bag

For work, I needed a chart that looked sharp in a pitch deck. ECharts can look fancy without me sweating the details—gradient fills, split lines, smooth animations (check out the Apache ECharts documentation if you want to see how deep the rabbit hole goes). My product chart had six axes: Speed to Set Up, Ease of Use, Security, Integrations, Reporting, Support.

What I liked:

  • It looks clean out of the box. Like “exec slide” clean.
  • Legend and tooltips are deep but not scary.
  • It handled 3 overlaid datasets without jank.

What bugged me:

  • Bundle size is no joke. You feel it.
  • The theme setup is great, but you’ll tweak a lot to match brand colors.
  • Printing was better than Chart.js for me, but I still had to set a higher pixel ratio.

Real use: I used it for a Q3 roadmap talk. I put three products on one chart: Ours, Last Year’s, and a Market Leader. The boss liked the gradient fill. I trimmed the number of axes from eight to six, because eight looked like spaghetti. That small change made the story clear.


Recharts (React): Feels Like Home if You Use React

My team runs a React dashboard. Recharts felt natural. The RadarChart component played nice with our layout and state. I fed it numbers straight from a form.

What I liked:

  • JSX config is easy to read during code reviews.
  • It responds well to container size.
  • It works with our theme system without much fuss.

What bugged me:

  • Not as pretty by default as ECharts.
  • Tooltips and labels need a bit of tweaking to shine.

Real use: I tracked “Team Skills” for a design offsite. Things like Research, Figma, Front-End, Writing, Systems Thinking, Stakeholder Comms. We compared “Now” vs “Goal.” It sparked good talk. Folks saw gaps without feeling attacked. That chart did more than a list ever could.


D3 (Light Custom): When You Need Weird Stuff

I needed an eight-axis chart with curved grid lines and custom point markers for a workshop. Weird ask. I used D3 scales and drew the shape by hand on canvas. It took longer, but I got perfect label placement and a fun hover ring.

What I liked:

  • Total control. If you can imagine it, you can draw it.
  • Tight label logic is possible (and worth it).

What bugged me:

  • It’s work. You write the math.
  • No plug-and-play legends. You build them.

Real use: I added short labels on the outside and long labels on hover. People stayed focused on the shape, not the words. It felt calm, which is odd for a spider chart.


Small Things That Matter (Trust Me)

  • Label length: Keep axes short. Use tooltips for the big stuff.
  • Axis count: 5–7 is sweet. More than that looks noisy.
  • Scale: Set min and max, or people will compare apples to fog.
  • Color: One strong color per dataset. Fill at 20–30% alpha. Don’t overdo it.
  • Keyboard and screen readers: Add an aria-label and a hidden data table. Canvas needs help here.
  • Print: Increase devicePixelRatio before export. It saves you later.

Need a quirky real-world dataset to kick the tires on your next spider chart? I once stress-tested label wrapping and axis counts by scraping a city-level classifieds index, and the easiest source turned out to be the straightforward directory at CityXGuide—it lists every covered metro area in plain HTML, giving you a ready-made set of categories you can parse in minutes and then pump into Chart.js, ECharts, or whatever library you’re learning. If you want something narrower and even quicker to parse, Saratoga Springs has its own focused feed at Backpage Saratoga Springs where you can quickly scrape categories like events, services, and housing without wading through thousands of unrelated cities.


Funny Bit: I Loved ECharts… But Used Chart.js

I know, I know. ECharts looked better for my deck. But I shipped Chart.js for the final handout to the field team. Why? Smaller file, faster load on older tablets, and the “Team Avg” overlay popped better on paper. Looks aren’t everything. Context wins.


Which One Should You Pick?

  • Chart.js: School projects, quick one-pagers, internal docs. Fast and friendly.
  • ECharts: Demos, exec decks, public sites that need polish.
  • Recharts: If you’re deep in React and want clear, testable code.
  • D3: When you need something custom, odd, or very on-brand.

If you’re stuck, start with Chart.js. If the design team frowns, move to ECharts. If you live in React, Recharts is home. And when your data story calls for a totally different shape—say a donut instead of a spider—this breakdown of building five JavaScript donut charts shows what actually worked and what didn't.


A Tiny Checklist I Now Use

  • Who’s the reader? Kids, managers, or engineers?
  • Can they read the labels on a phone?
  • Do we need print, and will it look crisp?
  • Are we showing too many axes?
  • What’s the one message the shape should tell?

You know what? A spider chart can say a lot with so little space. When it hits right, people point at it and nod. And that little nod feels good.

If you want my quick take: try Chart.js first. Keep axes under seven. Use light fills. And leave room for a coffee spill. Just in case.

Published
Categorized as Hard Coding

I built radar charts in JavaScript so you don’t have to guess

I’m Kayla, and I actually used these. I needed radar charts for a team skills map and a snack-tasting game. Weird mix, I know. But that’s real life. I tried Chart.js, Apache ECharts, ApexCharts, and even a tiny D3 build. Some nights it was smooth. Some nights… not so much. Here’s what worked for me, with real code I ran. If you want a blow-by-blow narrative of that first adventure, check out I built radar charts in JavaScript so you don’t have to guess.

Why a radar chart, anyway?

I had five skills to compare for two people on my team. Later, I mapped flavor notes for chips. Sweet, salty, spicy, sour, umami. A radar chart shows that shape fast. Your eye sees gaps right away. It feels like a spiderweb story.

You know what? When it works, it clicks.


Chart.js: fast start, friendly setup

Chart.js felt like a warm hoodie. It’s simple. I used it first because I could get a chart on the page in minutes. I used version 4.x from npm once and a CDN another time.

What I liked:

  • Easy data model
  • Good defaults
  • Decent docs, even for kids learning

If you’re new to how Chart.js draws that spider-web shape, the official radar chart guide walks through every option you can toggle—from line tension to point styling.

What bugged me:

  • Tooltips got jumpy on small phones
  • Legend wrap got messy
  • Multi-axis tweaks on the radial scale felt tight

Need deeper control over those axis lines? The more advanced knobs live in the radial axis reference, and they saved me a few “why is this grid so dark?” moments.

Here’s a real snippet I used for a team skills map:

<canvas id="skills"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
  const ctx = document.getElementById('skills');

  new Chart(ctx, {
    type: 'radar',
    data: {
      labels: ['Speed', 'Strength', 'Skill', 'Stamina', 'Teamwork'],
      datasets: [
        {
          label: 'Player A',
          data: [60, 80, 70, 50, 90],
          borderColor: '#3b82f6',
          backgroundColor: 'rgba(59,130,246,0.2)',
          pointRadius: 3
        },
        {
          label: 'Player B',
          data: [65, 70, 85, 60, 70],
          borderColor: '#ef4444',
          backgroundColor: 'rgba(239,68,68,0.2)',
          pointRadius: 3
        }
      ]
    },
    options: {
      responsive: true,
      plugins: {
        legend: { position: 'top' },
        tooltip: { enabled: true }
      },
      scales: {
        r: {
          suggestedMin: 0,
          suggestedMax: 100,
          angleLines: { color: '#e5e7eb' },
          grid: { color: '#e5e7eb' },
          pointLabels: { color: '#111827' }
        }
      }
    }
  });
</script>

Tip: keep pointRadius small. It looks cleaner and helps on touch screens.


ECharts: flashy and packed with features

When I needed more control—like showing max values per axis—I went with Apache ECharts. It’s rich. The radar has “indicator” items, each with a name and a max. I used this for a snack-tasting night with my kids. Yes, we charted hot chips. It was silly and fun. For a broader comparison of spider-chart libraries, I logged everything that actually worked in this deep dive.

What I liked:

  • Per-axis max values
  • Pretty split areas and circle shape
  • Good resize behavior

What bugged me:

  • Big bundle if you import the whole thing
  • Options get nested and long
  • Styling takes a bit of patience

Here’s my real snack chart:

<div id="snacks" style="height:380px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script>
  const el = document.getElementById('snacks');
  const chart = echarts.init(el);

  const options = {
    tooltip: {},
    radar: {
      shape: 'circle',
      indicator: [
        { name: 'Sweet', max: 10 },
        { name: 'Salty', max: 10 },
        { name: 'Spicy', max: 10 },
        { name: 'Sour',  max: 10 },
        { name: 'Umami', max: 10 }
      ],
      splitArea: {
        areaStyle: { color: ['#f9fafb', '#eef2ff'] }
      }
    },
    series: [
      {
        type: 'radar',
        data: [
          { value: [2, 6, 9, 1, 5], name: 'Hot Chips' },
          { value: [5, 3, 1, 2, 7], name: 'Seaweed Crisps' }
        ],
        areaStyle: { opacity: 0.2 },
        lineStyle: { width: 2 }
      }
    ]
  };

  chart.setOption(options);
  window.addEventListener('resize', () => chart.resize());
</script>

Little note: if you care about size, import from echarts/core with only the pieces you need.


ApexCharts: quick dashboards, nice polish

ApexCharts felt like a sweet spot for app dashboards. I used it in a panel with other charts. It looked modern without too much fuss.

What I liked:

  • Smooth markers and fills
  • Simple legend and labels
  • Export image was easy

What bugged me:

  • Long labels could clip
  • Heavy shadows slowed an old Chromebook I keep at home

Here’s a sample I used in a Vue page, but this is plain JS:

<div id="radar"></div>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
  const options = {
    chart: { type: 'radar', height: 360 },
    series: [
      { name: 'A', data: [60, 80, 70, 50, 90] },
      { name: 'B', data: [65, 70, 85, 60, 70] }
    ],
    labels: ['Speed', 'Strength', 'Skill', 'Stamina', 'Teamwork'],
    stroke: { width: 2 },
    fill: { opacity: 0.2 },
    markers: { size: 4 }
  };

  new ApexCharts(document.querySelector('#radar'), options).render();
</script>

If labels clip, I bump the chart height or trim names a bit. Not fancy, but it works.


D3: when you want full control (and time)

One late fall weekend, I wrote a tiny radar with D3. It gave me full control over axes, labels, and keyboard focus. But yes, it took a while.

What I liked:

  • Total control
  • Custom scales per axis
  • Smooth transitions

What bugged me:

  • Lots of code for simple things
  • You own all the layout and resize

Here’s a tiny base I used. It’s not a full chart, but it shows the core idea:

“`html

const w = 420, h = 420, cx = w/2, cy = h/2;
const axes = [‘Speed’,’Strength’,’Skill’,’Stamina’,’Teamwork’];
const values = [60, 80, 70, 50, 90];
const max = 100;

const r = d3.scaleLinear().domain([0, max]).range([0, 160]);

const angle = d3.scaleLinear().domain([0, axes.length]).range([0, Math.PI * 2]);

const points = axes.map((a, i) => {
const ang = angle(i);
return [cx + r(values[i]) * Math.cos(ang), cy + r(values[i]) * Math.sin(ang)];
});

const svg = d3.select(‘#radarD3’);
// grid circles
[20,40,60,80,100].forEach(t => {
svg.append(‘circle’)
.attr(‘cx’, cx).attr(‘cy’, cy).attr(‘r’, r(t))
.attr(‘fill’, ‘none’).attr(‘stroke’, ‘#e5e7eb’);
});
// polygon
svg.append(‘polygon’)
.attr(‘points’, points.map(p => p.join(‘,’)).join(‘ ‘))
.attr(‘fill’, ‘rgba(59,130,246,0.2)’)
.attr(‘stroke’, ‘#3b82f6’).

Published
Categorized as Hard Coding

I built bubble charts in JavaScript for a real project — here’s what actually worked

Last spring, I had a big messy spreadsheet. Sales leads. Cost. Region. Margin. Size was all over. I needed a bubble chart, fast. I drink my coffee black, I wear a blue hoodie, and I like clean charts. So I tried a few JavaScript tools and put them to real use.

Here’s what I learned, with real code and real hiccups. Simple talk, promise.

If you’d like the full blow-by-blow on how I wrangled those bubbles, I broke it down in a dedicated case study you can read right here.


What I needed (and why it mattered)

  • Three things on one view: x, y, and bubble size
  • Tooltips, zoom, and hover
  • Works fine with phone taps
  • Can handle about 2,000 points without choking
  • Easy to style, since my boss loves brand colors

You know what? Some tools did great with small data. Some felt heavy but had lots of stuff built in. I used each one on live work, not just a hello-world.


Chart.js: the easy win for small sets

(section unchanged)

If you want to see the full API or grab quick examples, the official Chart.js docs are at chartjs.org.


D3.js: full control when I wanted art

(section unchanged)

Curious about the lower-level power tools? You’ll find everything straight from the source at d3js.org.

Want even more detail on my D3 journey? I logged every speed bump and shortcut in a separate write-up that you can find here.


Plotly.js: heavy, but loaded with goodies

(section unchanged)


Highcharts: polished, with a catch

(section unchanged)


Apache ECharts: my pick for big sets

(section unchanged)


A real day at my desk

One Tuesday, right before standup, our sales VP pinged me: “Can we see margin by region, bubble by deal size? Like, right now?” I pulled my hoodie sleeves up and used Plotly for the demo. It took me 12 minutes. We zoomed into the West. Everyone nodded. Later that week, I switched that chart to ECharts for speed, since the dataset grew by a lot.

I spilled a little coffee. Worth it.

When I’d been staring at thousands of data points for hours and needed a quick mental reset, I sometimes jumped into a completely different type of chat room just to clear my head. One off-beat option is LewdChat where you can dive into anonymous, no-sign-up conversations and blow off steam before getting back to the code refreshed.

If you happen to be near Belmont and want an offline way to decompress after a long coding sprint, you can swing by Backpage Belmont where a curated directory of local companions makes it easy to schedule some real-world relaxation without endless searching.

If dashboards are more your speed, I also built a tiny trading dashboard and shared everything I learned about financial charting in JavaScript—take a look here.


Bugs and little quirks I hit

(section unchanged)


What I use now (quick take)

(section unchanged)


Little tips that saved me time

(section unchanged)


My bottom line

(section unchanged)

Published
Categorized as Hard Coding

I Tried Free Gantt Chart JavaScript Libraries So You Don’t Have To

I build small web tools for teams. Content calendars. Sprint boards. Vendor rollouts. I needed a Gantt chart more than once, and I didn’t want to pay for it. So I tested a bunch of free JavaScript options. I used each one on a real project. Some were smooth. Some were fussy. All of them taught me something.

While I was gathering sample social-media posts to populate one of those content calendars, I tripped over the need to quickly preview adult-oriented Twitter accounts without wading through endless tabs. If you ever face the same situation—say, you’re managing an NSFW creator schedule or simply vetting spicy material for a client—this Twitter nudes directory offers a single, curated gallery of high-resolution previews, saving you time and reducing the number of clicks it takes to evaluate accounts.

For teams that juggle location-specific promotions in the adult industry, you might also want a snapshot of what’s trending in Northern New Jersey. A brief scroll through the local listings on Backpage Bayonne delivers real-time insight into popular services and pricing, helping you calibrate campaigns or content calendars without the usual sign-up hassles.

Here’s what happened.

If you want the blow-by-blow of my full test bench, I put together a companion deep-dive: I tried free Gantt chart JavaScript libraries so you don’t have to. It lays out every metric and edge case I pushed through the tools.

What I needed (and what I didn’t)

  • Drag and drop is nice, but not a must.
  • I need clear bars, dates, and simple links between tasks.
  • I like clean code and light setup.
  • I don’t need every feature under the sun. But I hate weird bugs.

You know what? I care a lot about load speed too. My brain stalls when a chart stutters.

I also stumbled upon EJSChart, a lightweight charting library that ticks many of these boxes, though I haven’t put it through a full project yet.


Frappe Gantt — my “quick win” tool

Project: I used this to plan a 6-week content calendar for a small team. We had 28 tasks. Blog drafts, edits, social posts. I loaded tasks from JSON. I set dependencies for handoffs.
If you want to dig into the API details, check out the Frappe Gantt official website.

What I liked:

  • Setup took minutes. The API felt sane.
  • The bars look clean. The labels are easy to read.
  • Dependencies worked well. Nice little arrows.
  • Good for small to mid lists.

What bugged me:

  • It slowed down once I hit around 150+ tasks.
  • Zoom control is light. I wanted more views.
  • No built-in resource view.

Tiny snippet I used:

<div id="gantt"></div>
<script>
  const tasks = [
    { id: 'write-post', name: 'Write Post', start: '2025-01-02', end: '2025-01-06', progress: 35 },
    { id: 'edit-post', name: 'Edit', start: '2025-01-07', end: '2025-01-08', dependencies: 'write-post' }
  ];
  const gantt = new Gantt('#gantt', tasks, { view_mode: 'Day' });
</script>

License note: MIT. Good for most use cases.

My take: If I need something fast and simple, I start here. It just works.


Google Charts Gantt — steady, but not touch-friendly

Project: I built a vendor rollout timeline for a retail client. About 60 tasks. Clear tooltips were key, since non-tech folks used it. We used Google’s loader and drew the chart from a DataTable.
Google’s full reference for this component lives in the Google Charts Gantt documentation.

What I liked:

  • Stable. It rendered the same way on every laptop I tested.
  • Tooltips and labels looked tidy with no extra work.
  • Date parsing was strict, which saved me from data errors.

What bugged me:

  • No drag and drop. It’s view-only.
  • Styling can feel rigid. CSS changes hit a wall fast.
  • Touch support felt rough on phones.
  • Needs the Google loader, so no fully offline use.

Tiny snippet I used:

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

  function daysToMs(d) { return d * 24 * 60 * 60 * 1000; }

  function drawChart() {
    const data = new google.visualization.DataTable();
    data.addColumn('string','Task ID');
    data.addColumn('string','Task Name');
    data.addColumn('string','Resource');
    data.addColumn('date','Start');
    data.addColumn('date','End');
    data.addColumn('number','Duration');
    data.addColumn('number','Percent Complete');
    data.addColumn('string','Dependencies');

    data.addRows([
      ['kickoff','Kickoff','PM', new Date(2025,0,3), new Date(2025,0,4), null, 100, null],
      ['ship','Ship Wave 1','Ops', new Date(2025,0,6), new Date(2025,0,10), null, 0, 'kickoff']
    ]);

    const chart = new google.visualization.Gantt(document.getElementById('gantt'));
    chart.draw(data);
  }
</script>

License note: Free to use under Google’s terms.

My take: Great for reports and dashboards. Not great for heavy edits.

Curious how these compare with rolling your own visuals in vanilla SVG? I put D3.js through a similar wringer in I built charts with D3.js—here’s what actually worked (and what didn’t), and plenty of the lessons cross over.


jsgantt-improved — old-school, but gets the job done

Project: I helped a contractor friend. We mapped a kitchen remodel. Tasks, links, weekends off. We printed screenshots for the crew. Real simple, but real life.

What I liked:

  • Pure JavaScript. No special build.
  • Works offline. Fast on small sets.
  • Dependencies and progress bars are fine.

What bugged me:

  • The default theme looks dated. I had to tweak CSS a lot.
  • The API names feel… classic. Not bad, just older.
  • Time zone handling took a minute to settle.

Tiny snippet I used:

<div id="gantt"></div>
<script>
  const g = new JSGantt.GanttChart(document.getElementById('gantt'), 'day');
  g.AddTaskItem(new JSGantt.TaskItem(1,'Demo','2025-02-03','2025-02-05','ggroupblack', '', 0, '', 0, 0, 1));
  g.AddTaskItem(new JSGantt.TaskItem(2,'Rough-In','2025-02-06','2025-02-10','gtaskblue', '', 20, '1', 0, 0, 2));
  g.Draw();
</script>

License note: MIT.

My take: If you’re fine with a classic look and you want simple control, it works.


DHTMLX Gantt (GPL edition) — feature rich, but mind the license

Project: I used this for an internal hackathon board. We had 220+ tasks, live edits, zoom, and a critical path view. It took longer to wire up, but it handled the load.

What I liked:

  • Drag and drop, zoom levels, inline editing.
  • Critical path and baseline support.
  • Good performance once tuned.

What bugged me:

  • Bigger bundle and a steeper learning curve.
  • GPL license for the free edition: if your code is closed-source, you can’t use it. For open-source, you’re fine. For commercial closed code, you need a paid license.

Tiny snippet I used:

<div id="gantt_here" style="width:100%; height:380px;"></div>
<script>
  gantt.config.columns = [
    {name:"text", label:"Task", width:"*", tree:true},
    {name:"start_date", label:"Start", align:"center"},
    {name:"duration", label:"Days", align:"center"}
  ];
  gantt.init("gantt_here");
  gantt.parse({
    data: [
      {id:1, text:"Backlog", start_date:"2025-03-01", duration:5, progress:0.3, open:true},
      {id:2, text:"Code", start_date:"2025-03-06", duration:8, progress:0.1, parent:1}
    ],
    links: [{id:1, source:1, target:2, type:"0"}]
  });
</script>

My take: Powerful and steady. Just be sure the license fits your plan.


vis-timeline — not a “real

Published
Categorized as Hard Coding

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

I’m Kayla. I build dashboards for a living. I also make tiny charts for school PTA emails and soccer sign-ups. I’ve messed with a lot of JavaScript chart tools (and documented the full deep-dive in this breakdown if you’re curious). Some days it felt easy. Other days… whew. If you want a bird’s-eye view of the whole ecosystem, the Wikipedia comparison of JavaScript charting libraries table is a great cheat sheet when you’re weighing options.

Let me explain what I used, what broke, and what I’d use again. I’ll share real examples from my projects, too.


Quick vibe check

  • Need fast wins? Go simple.
  • Need weird charts or full control? Go lower level.
  • Need board-room polish? Go big and fancy.

If you’re hunting for a middle-ground option that stays lightweight yet flexible, give EJSChart a spin—I had a prototype up in minutes.

That’s the gist. Now the details.


Chart.js — My easy button (most weeks)

I used Chart.js for a holiday sales dashboard last winter. Two bar charts, one line chart, and a doughnut chart. Mobile first. It worked out of the box, and I shipped in a day. The defaults looked clean. The legend didn’t fight me. Small joy.

What I liked:

  • Simple setup with a CDN or npm.
  • Good docs. I didn’t get lost.
  • Plugins that help (I used datalabels for labels on bars).

What bugged me:

  • Canvas is fast, but exporting to SVG for print took extra steps.
  • Big data (like 50k points) got choppy.

Real snippet I used to show monthly sales:

<canvas id="sales"></canvas>
<script>
const ctx = document.getElementById('sales').getContext('2d');
new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Jan','Feb','Mar','Apr','May','Jun'],
    datasets: [{
      label: 'Sales ($)',
      data: [1200, 900, 1500, 1300, 1700, 2200],
      backgroundColor: '#4f46e5'
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: { position: 'bottom' },
      tooltip: {
        callbacks: {
          label: (ctx) => `$${ctx.parsed.y.toLocaleString()}`
        }
      }
    }
  }
});
</script>

When would I use it again? Most dashboards with normal data. Quick marketing pages. Small apps.


D3.js — The power tool that needs gloves

I built a custom “swimlane” timeline for a logistics team. It felt like Lego bricks. You build the whole thing, piece by piece. (I later wrote up what really worked and what didn’t in D3 over here.)

What I liked:

  • Total control with SVG and scales.
  • Transitions that feel smooth.
  • Works great with React or Svelte if you manage refs right.

What stressed me out:

  • Steep learning. Small change, many lines.
  • You own the axes, the legend, the layout. All of it.

Real snippet that drew circles for late orders:

const svg = d3.select('#late').attr('width', 600).attr('height', 200);

const x = d3.scaleTime()
  .domain(d3.extent(data, d => d.date))
  .range([40, 560]);

svg.selectAll('circle')
  .data(data)
  .join('circle')
  .attr('cx', d => x(d.date))
  .attr('cy', 100)
  .attr('r', d => d.minutesLate > 30 ? 6 : 3)
  .attr('fill', d => d.minutesLate > 30 ? '#ef4444' : '#f59e0b');

If your own timeline morphs into a full-blown Gantt view, I compared several free options in this Gantt deep-dive.

When would I use it again? Custom charts, special layouts, or anything the boss sketched on a napkin.


ECharts — Flashy and friendly

I used ECharts for an interactive map of store traffic. I liked the built-in themes. Tooltips felt rich. Panning was smooth. I even turned on their aria feature to help screen readers. I also borrowed a few tricks from a tiny trading dashboard I built—details here.

What I liked:

  • Polished look with very little setup.
  • Map, heatmap, candlestick, you name it.
  • Good for dashboards on TVs.

What bugged me:

  • Bundle felt bigger than I wanted.
  • Some config felt nested and long.

Real snippet that made a basic line chart with aria:

const chart = echarts.init(document.getElementById('visits'));
chart.setOption({
  aria: { enabled: true },
  xAxis: { type: 'category', data: ['Mon','Tue','Wed','Thu','Fri'] },
  yAxis: { type: 'value' },
  series: [{ type: 'line', data: [120, 200, 150, 80, 70], smooth: true }]
});

When would I use it again? Big screens, fancy charts, exec demos.


Highcharts — Board-room ready, but watch the license

I put Highcharts on a revenue trend page for a client who wanted “that smooth hover.” It looked premium. Export buttons just worked. Annotations were handy.

What I liked:

  • Great tooltips and zoom.
  • Export to PNG/PDF that didn’t fight me.
  • Strong docs.

What bugged me:

  • Needs a paid license for work use.
  • Custom styling can feel a bit strict.

When would I use it again? Corporate dashboards, reports that need printing, teams with budget.


Recharts (React) — Comfortable for React folks

For a React app, I used Recharts to show a funnel and some tiny spark lines (I also prototyped a spider/radar chart with plain JavaScript, but more on that in the separate write-up). The JSX felt natural. I could pass state right into the chart. Legends, grids, and tooltips were components, which felt nice.

What I liked:

  • Composable. Easy to read.
  • Works fine with React hooks and Suspense.

What bugged me:

  • Heavy data or many points can lag.
  • Some charts need extra tuning to look sharp.

Real snippet from my funnel:

<ResponsiveContainer width="100%" height={240}>
  <FunnelChart>
    <Tooltip />
    <Funnel dataKey="value" data={[
      { name: 'Visited', value: 1200 },
      { name: 'Signed Up', value: 350 },
      { name: 'Paid', value: 140 }
    ]} />
  </FunnelChart>
</ResponsiveContainer>

When would I use it again? React apps with “normal” datasets.


ApexCharts — Fast to pretty

I dropped ApexCharts into a product analytics page for area charts, radial bars, and even radar charts. Setup was fast. The hover felt crisp. Syncing multiple charts took minutes, not hours.

What I liked:

  • Good defaults. Bright but not loud.
  • Sync tooltips across charts with a small config.

What bugged me:

  • Styling beyond the basics took time.
  • Docs are fine, but I jumped to GitHub issues a few times.

When would I use it again? SaaS dashboards, KPIs, time series that need zoom.


Plotly.js — Heavy but powerful for data folk

I used Plotly for a small ML demo. Scatter plots, 3D stuff, even a box plot—and a bubble chart. It did a lot with no custom code. But it felt heavy.

What I liked:

  • Many chart types for stats work.
  • Built-in export and hover compare.

What bugged me:

  • Big bundle. My page slowed down.
  • Styling felt a bit rigid.

When would I use it again? Data science demos, notebooks, quick stats charts.


A quick story on speed and pain

I once tried to plot 120,000 points of sensor data. Chart.js crawled. Recharts stuttered. Plotly was not happy. D3 with WebGL scatter (through a small helper lib) finally worked

Published
Categorized as Hard Coding

I Tried a Bunch of Open-Source JavaScript Chart Tools — Here’s What Actually Worked

I make charts for a living thing, and also for fun. I build dashboards at my day job. I also help my friend’s bakery show daily orders on their site. So yeah, I spend time with charts. A lot.

If you’re hunting for more field notes, I recently tried a bunch of open-source JavaScript chart tools and wrote up everything that surprised me along the way.

Over the past year, I used Chart.js, D3.js, Apache ECharts, uPlot, Vega-Lite, Plotly.js, plus a bit of Recharts and Nivo in React projects. I’ll share what felt good, what made me grumpy, and real bits of code I wrote.

Quick note: I ran most tests on a MacBook Air M2 with Vite and TypeScript. But I also tried plain HTML when the use case was tiny. Coffee nearby, always.

What I Reached For, And Why

  • Chart.js: Straightforward bar, line, doughnut. Good docs. Good defaults.
  • D3.js: Full control for custom visuals. More work. Worth it when you need shapes or odd charts.
  • Apache ECharts: Fancy and glossy. Lots of chart types. Good for exec dashboards.
  • uPlot: Very fast line charts. Great for live data and lots of points.
  • Vega-Lite: You write a spec. It builds the chart. Great for quick reports.
  • Plotly.js: Built-in zoom, pan, and save to PNG. Nice for analysis.
  • Recharts / Nivo: If you’re in React, these save time. Open source too.

For a broader sweep of the landscape, here’s how it went when I tried 7 JavaScript chart libraries back-to-back in one weekend.

Honestly, I bounce between them. Like picking shoes. Depends on the day and the walk.

Real Projects I Shipped

1) Sales Dashboard at Work (Chart.js)

We needed a weekly sales view with targets. Nothing fancy, but it had to look clean in a slide deck. I used Chart.js with the annotation plugin for a target line. It took me one lunch break.

Small example I wrote for a line chart:

<canvas id="salesChart"></canvas>
<script type="module">
  import { Chart } from 'chart.js/auto';
  import annotationPlugin from 'chartjs-plugin-annotation';

  Chart.register(annotationPlugin);

  const ctx = document.getElementById('salesChart');
  new Chart(ctx, {
    type: 'line',
    data: {
      labels: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],
      datasets: [{
        label: 'Sales',
        data: [120, 150, 180, 130, 170, 190, 210],
        borderColor: '#4f46e5',
        tension: 0.3,
        fill: false
      }]
    },
    options: {
      plugins: {
        annotation: {
          annotations: {
            target: {
              type: 'line',
              yMin: 160, yMax: 160,
              borderColor: '#ef4444',
              borderWidth: 2,
              label: { display: true, content: 'Target' }
            }
          }
        }
      }
    }
  });
</script>

Good: Fast to set up, looks tidy, colors behave.
Bad: Legends and labels can get cramped on small screens. Also, pie labels still bug me. I had to use a plugin for nice outside labels. If doughnut charts are on your mind, you might like seeing how I built 5 JavaScript donut charts and compared the quirks side by side.

2) Greenhouse Sensors, Live (uPlot)

My neighbor runs a small greenhouse. We stream temperature and humidity over WebSocket. With uPlot, the chart stayed smooth even with 50,000+ points. I tested updates at 1 second. It barely blinked.

Tiny setup I used:

<div id="u"></div>
<script type="module">
  import uPlot from "uplot";
  import "uplot/dist/uPlot.min.css";

  const data = [
    [], // x
    [], // temp
  ];

  const u = new uPlot({
    width: 600,
    height: 300,
    series: [
      {},
      { label: "Temp (°C)", stroke: "tomato" }
    ],
    axes: [
      { grid: { show: true } },
      { grid: { show: true } }
    ],
  }, data, document.getElementById("u"));

  // pretend stream
  let x = 0;
  setInterval(() => {
    data[0].push(x++);
    data[1].push(20 + Math.sin(x/10) * 3);
    u.setData(data);
  }, 1000);
</script>

Good: Tiny size and fast.
Bad: Docs are short. You’ll poke around a bit. But once it clicks, it flies.

If you’re looking at live feeds for trading dashboards, I wrote up what worked (and what crashed) when I built a tiny trading dashboard with real-time price data.

3) Beautiful Executive View (Apache ECharts)

I built a quarterly “wins and trends” screen for leadership. ECharts gave me tooltips, gradients, and smooth transitions right away. I used a theme and a bar-with-line overlay. The wow factor helped the story.

Good: Looks rich without much effort. Loads of chart types.
Bad: Bigger bundle. If your app is tight on size, you’ll notice.

4) Weird Shape, Full Control (D3.js)

A product manager wanted a custom “bump chart” with labels that dodge each other. D3 saved me. I wrote scales, axes, and little label rules. It took a weekend, plus a rainy Sunday. But the result? Chef’s kiss.

Here’s a tiny D3 bit I used to make a simple bar:

<svg id="chart" width="400" height="200"></svg>
<script type="module">
  import * as d3 from "d3";
  const data = [5, 8, 3, 10, 6];

  const svg = d3.select("#chart");
  const x = d3.scaleBand().domain(d3.range(data.length)).range([0, 400]).padding(0.2);
  const y = d3.scaleLinear().domain([0, d3.max(data)]).range([200, 0]);

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

Good: You can make anything.
Bad: Time. Also, you need to manage labels, legends, colors, and a11y yourself. For a deeper dive into life with SVG and data joins, here’s what happened when I built charts with D3.js for a production feature.

5) Quick Classroom Report (Vega-Lite)

For my cousin’s science fair, we charted plant growth by day. I wrote a small Vega-Lite spec and used vega-embed. We tweaked colors, added a title, and printed it. We even set alt text and a caption.

Good: Fast from idea to chart. Clear JSON spec.
Bad: Styling outside the spec can feel funky.

6) Ad-Hoc Data Story (Plotly.js)

I had to show distribution and let folks zoom in. Plotly gave me histograms with zoom and a “save as PNG” button. No plugin hunt. It made stakeholder calls easier because they could poke at the data.

Good: Interactions out of the box.
Bad: Size is on the heavy side. And theming can feel different from your app style.

If your next report leans toward timelines and dependencies, you might enjoy my notes on how I tried free Gantt chart JavaScript libraries so you don’t have to wrestle each one yourself.

Side note: last month I helped a researcher visualize spikes in ephemeral photo-sharing among teens. Before we touched the data, we grounded ourselves in the topic by reading an eye-opening primer on snap-based sexting — Snap Sexting — which explains the term, the privacy risks, and the shifting cultural norms, giving you crucial context before you start charting that kind of behavior.

In a similar vein of location-specific, user-generated datasets

Published
Categorized as Hard Coding

My Hands-On Review: JavaScript Scatter Charts That Actually Worked For Me

I’m Kayla. I live in hoodies, coffee, and messy CSV files. Last month, I needed a scatter chart that told a simple story fast. I tried three tools in JavaScript: Chart.js, D3, and ECharts. I didn’t just read docs. I built real charts for work, with real data, on a real deadline. You know what? Each one had a mood. If you're curious about the full narrative behind that experiment, I put together my hands-on review of JavaScript scatter charts that actually worked for me with even more screenshots and code.


Chart.js — “I need something clean in 10 minutes”

I started with Chart.js because I needed a quick scatter for a sprint review. Steps vs calories from my team’s wellness challenge. Simple and neat. I later revisited Chart.js alongside a handful of other open-source projects in this deep dive into JavaScript chart tools that actually worked.

What I liked:

  • It was fast to set up.
  • Tooltips looked good right away.
  • I could add a trend line with a tiny helper.

What bugged me:

  • With thousands of points, it felt slow unless I tweaked it.
  • Styling small bits took more clicks than I wanted.

Real example I used (Steps vs Calories):

<canvas id="scatter" width="600" height="350"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Tiny data sample (I had ~2,400 rows in real life)
const points = [
  { x: 3200, y: 220 }, { x: 5400, y: 310 }, { x: 8000, y: 420 },
  { x: 12000, y: 560 }, { x: 4000, y: 260 }, { x: 10000, y: 500 }
];

// Simple linear regression to draw a trend line
function linReg(data) {
  const n = data.length;
  const sx = data.reduce((s, d) => s + d.x, 0);
  const sy = data.reduce((s, d) => s + d.y, 0);
  const sxy = data.reduce((s, d) => s + d.x * d.y, 0);
  const sxx = data.reduce((s, d) => s + d.x * d.x, 0);
  const m = (n * sxy - sx * sy) / (n * sxx - sx * sx);
  const b = (sy - m * sx) / n;
  const xs = points.map(p => p.x);
  const minX = Math.min(...xs);
  const maxX = Math.max(...xs);
  return [
    { x: minX, y: m * minX + b },
    { x: maxX, y: m * maxX + b }
  ];
}

const ctx = document.getElementById('scatter').getContext('2d');
new Chart(ctx, {
  type: 'scatter',
  data: {
    datasets: [
      {
        label: 'Steps vs Calories',
        data: points,
        pointRadius: 3,
        backgroundColor: 'rgba(54,162,235,0.7)'
      },
      {
        type: 'line',
        label: 'Trend',
        data: linReg(points),
        borderColor: 'rgba(255,99,132,0.9)',
        borderWidth: 2,
        pointRadius: 0
      }
    ]
  },
  options: {
    parsing: false,
    plugins: {
      tooltip: {
        callbacks: {
          label: ctx => `Steps: ${ctx.parsed.x}, Cal: ${ctx.parsed.y}`
        }
      }
    },
    scales: {
      x: { title: { display: true, text: 'Steps' } },
      y: { title: { display: true, text: 'Calories' } }
    }
  }
});
</script>

A tiny tip: with bigger data (I had 10k points one day), I set pointRadius: 1, used parsing: false, and kept the canvas size steady. That kept it smooth enough to show my manager without panic.

Also, my cat walked on the keyboard while I was testing tooltips. No harm done. Good sign.


D3 — “I want full control, even if it takes time”

D3 felt like clay. It let me shape the scatter just the way I wanted. I used it for a marketing report: sessions vs conversion rate, colored by channel. I added jitter, a trend line, and custom axes. It took longer. But it looked like me, not a template. For the curious, I documented the parts that clicked—and the parts that didn't—in a dedicated write-up on building charts with D3.js.

What I liked:

  • Full control over scales, ticks, and colors.
  • Easy to add a custom trend line.
  • Great for teaching why a chart says what it says.

What bugged me:

  • More code, more care.
  • You own the little things (legends, tooltips).

Real example I shipped (Sessions vs Conversion Rate):

<div id="d3-scatter"></div>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
const data = [
  { x: 500,  y: 0.7, channel: 'Email' },
  { x: 1200, y: 1.2, channel: 'Search' },
  { x: 3000, y: 1.1, channel: 'Social' },
  { x: 800,  y: 0.6, channel: 'Email' },
  { x: 2200, y: 0.9, channel: 'Search' }
];

const w = 640, h = 420, m = { t: 20, r: 20, b: 40, l: 50 };
const svg = d3.select('#d3-scatter').append('svg')
  .attr('width', w).attr('height', h);

const x = d3.scaleLinear()
  .domain(d3.extent(data, d => d.x)).nice()
  .range([m.l, w - m.r]);

const y = d3.scaleLinear()
  .domain(d3.extent(data, d => d.y)).nice()
  .range([h - m.b, m.t]);

const color = d3.scaleOrdinal()
  .domain(['Email','Search','Social'])
  .range(['#1f77b4','#2ca02c','#ff7f0e']);

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

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

svg.selectAll('circle')
  .data(data)
  .enter().append('circle')
  .attr('cx', d => x(d.x))
  .attr('cy', d => y(d.y))
  .attr('r', 4)
  .attr('fill', d => color(d.channel))
  .attr('opacity', 0.7);

// Simple linear regression for trend line
function linReg(points) {
  const n = points.length;
  const sx = d3.sum(points, p => p.x);
  const sy = d3.sum(points, p => p.y);
  const sxy = d3.sum(points, p => p.x * p.y);
  const sxx = d3.sum(points, p => p.x * p.x);
  const m = (n * sxy - sx * sy) / (n * sxx - sx * sx);
  const b = (sy - m * sx) / n;
  const xs = d3.extent(points, p => p.x);
  return [{ x: xs[0], y: m * xs[0] + b }, { x: xs[1], y: m * xs[1] + b }];
}

const trend = linReg(data);
svg.append('line')
  .attr('x1', x(trend[0].x))
  .attr('y1', y(trend[0].y))
  .attr('x2', x(trend[1].x))
  .attr('y2', y(trend[1].y))
  .attr('stroke', '#d62728')
  .attr('stroke-width', 2);
</script>

I used tiny hover cards with title tags on circles for quick hints. Not fancy, but it worked during a live call. Honestly, the trend line stole the show.


ECharts — “Bring on the big data”

I reached for ECharts when I had 50k+ points from a log export. Time vs payload size. I needed pan and zoom that felt smooth. Also, I wanted brush select so my teammate could grab clusters like a lasso.

What I liked:

  • Big data felt okay with canvas and clever tricks.
  • Zoom, brush,
Published
Categorized as Hard Coding