Charticles

Charts made of light particles

A zero-dependency charting library that renders your data as a living cloud of particles. Bar, line, bubble, radar, pie and donut — responsive, accessible, and driven by plain JSON.

a slow swell of growth, twelve months of it

Six types, one engine

Every chart is the same particle field, arranged differently.

Axes, ticks and legends are generated for you from the data. Hover any chart — tooltips and crosshairs come as standard, and every series is keyboard-toggleable from its legend.

Bar

grouped, stacked or horizontal
type: 'bar'

Line

smooth, monotone, never overshoots
type: 'line'

Pie

part-to-whole, direct labeled
type: 'pie'

Donut

with a live total in the hole
type: 'donut'

Bubble

a third value in the size
type: 'bubble'

Radar

many measures, one silhouette
type: 'radar'

Showcase

Built with Particle Charts.

Charts in the wild — from dashboards, decks and side projects.

Want to showcase your project? Shoot me an email at blake@destinedstudio.com


Playground

Turn the knobs.

Every option below is a top-level key on the config object. The code updates as you drag.

Chart type

Particles

Chrome

live config

          

What you get

Small library. Few surprises.

Zero dependencies

One file, one <script> tag, no build step required. ESM source is shipped alongside for bundlers.

Responsive by default

A ResizeObserver re-lays out the field on every container change, and HiDPI is handled for you.

Plain JSON in

Arrays of numbers, {label, value} records, multi-series objects or {x, y} points — all normalized the same way.

Real bloom

Particles composite additively into an offscreen layer, then bloom in two blurred passes — glow that costs one extra draw.

Axes & legends for free

Nice-number ticks, measured padding, label thinning, and a legend that never fights the plot for space.

Accessible & polite

Every chart ships a screen-reader data table, honours prefers-reduced-motion, and pauses when scrolled out of view.


Quick start

Install it, then three lines to a chart.

One file, no dependencies, no build step required. Take it from a CDN for a plain HTML page, or from npm if you have a bundler.

1a — CDN, for a plain page

          

Defines the global ParticleCharts. unpkg serves the same file — https://unpkg.com/particle-charts@1/dist/particle-charts.min.js. Drop the .min for the readable build, and pin an exact version (@1.0.0) in production rather than a range.

1b — npm, for a bundler

          

Ships ES module source plus index.d.ts, so TypeScript works with no @types package. require('particle-charts') resolves to the CommonJS build. Node 18+.

2 — a chart, in full

          

The container needs a height. The chart fills it and re-lays out whenever it changes.

2 — the same, as a module

          

Every chart type has a shorthand: line, area, bar, bubble, radar, pie, donut.

multi-series & options

        
updating live

        

Reference

Options.

Everything is optional except data. Nested groups (particle, axis, legend, line, bar, bubble, radar, pie) can also be set through the flat aliases shown here.

OptionDefaultWhat it does
Core
type'line'One of line, area, bar, bubble, radar, pie, donut.
dataNumbers, {label, value} records, {labels, values}, or {labels, series}. Bubble adds a third slot: {x, y, r}.
background'transparent'Canvas fill painted behind the particles.
paddingautoNumber, [y, x] or a box. Left alone, it is measured from the real axis labels.
responsivetrueRe-layout on container resize.
Particles
particleColorpaletteA color, an array of colors, or fn(index, series).
particleSize0.8Base particle radius in CSS pixels.
particleDensity15Multiplier on the auto-computed particle budget.
particleCount50000Hard ceiling on particles, whatever the density.
particleBloom0.8Additive glow strength, 0–1.
particleOpacity0.7Global particle alpha. Kept under 1 so additive stacking does not clip hues to white.
particleJitter1Idle drift amplitude in pixels — the “alive” wobble.
particleSpeed0.085Spring stiffness pulling each particle to its target.
particleShape'soft'Particles are circles. soft adds a glow sprite once they are big enough for the halo to read; square forces rectangles.
Chrome
showAxistrueAxis lines and tick labels.
showGridtrueGrid lines behind the plot.
showLegendtrueLegend — rendered only when there is more than one series or slice.
showTooltiptrueHover tooltip and crosshair.
showValuesfalsePrint values next to the marks.
legendPositionper typetop, bottom, left, right. Defaults to bottom where color keys a series — line, area, bar, bubble, radar — and top for pie and donut.
legendAlignper typestart, center, end. center under the plot, start otherwise.
theme'dark'dark or light. Sets the chrome colors below in one go — anything you set explicitly still wins. Particle colors are never themed.
axisColorrgba(255,255,255,.2)Color of the axis lines.
gridColorrgba(255,255,255,.1)Color of the grid lines behind the plot.
textColorrgba(255,255,255,.6)Color of the tick labels and axis titles.
crosshairColorrgba(255,255,255,.22)Color of the hover crosshair.
fontFamily / fontSizesystem sans / 11Typeface and size for axis labels.
min / maxautoPin the value axis instead of deriving it from the data.
valueFormatcompactfn(value) → string for ticks, labels and tooltips.
Per type
curve'smooth'Line interpolation: smooth, linear, step.
fillAreafalseFill under the line with particles. type: 'area' turns this on for you.
lineWidth3.2Thickness of the particle band forming the stroke.
stackedfalseStack bar series instead of grouping them.
horizontalfalseLay bars on their side.
minRadius / maxRadius5 / 30Bubble radius range. Values map between the two by area, so twice the value covers twice the ink.
bubble.edgeFade0.35Feathering of the bubble rim, 0–1.
levels4Radar web rings between the center and the edge.
webShape'polygon'Radar web: polygon follows the spokes, circle draws true rings.
radar.fill / fillAmounttrue / 0.55Fill the enclosed area, and the share of particles it takes.
innerRadius0 / 0.62Donut hole, as a fraction of the outer radius.
startAngle-90Where the first slice begins, in degrees.
padAngle1.2Gap between slices, in degrees.
Methods
chart.update(data, opts)Swap the data; particles morph to the new shape.
chart.setOptions(opts)Change the look without touching the data.
chart.resize()Force a re-layout.
chart.toDataURL()PNG snapshot of the current frame.
chart.destroy()Stop the loop, drop the listeners, remove the DOM.