Charticles
Charts made of light
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
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 horizontaltype: 'bar'
Line
smooth, monotone, never overshootstype: 'line'
Pie
part-to-whole, direct labelledtype: 'pie'
Donut
with a live total in the holetype: 'donut'
Bubble
a third value in the sizetype: 'bubble'
Radar
many measures, one silhouettetype: 'radar'
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
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
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 normalised 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.
Three lines to a chart.
Drop in the script, point it at an element, hand it some JSON.
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.
| Option | Default | What it does |
|---|---|---|
| Core | ||
| type | 'line' | One of line, area, bar, bubble, radar, pie, donut. |
| data | — | Numbers, {label, value} records, {labels, values}, or {labels, series}. Bubble adds a third slot: {x, y, r}. |
| background | 'transparent' | Canvas fill painted behind the particles. |
| padding | auto | Number, [y, x] or a box. Left alone, it is measured from the real axis labels. |
| responsive | true | Re-layout on container resize. |
| Particles | ||
| particleColor | palette | A colour, an array of colours, or fn(index, series). |
| particleSize | 0.8 | Base particle radius in CSS pixels. |
| particleDensity | 15 | Multiplier on the auto-computed particle budget. |
| particleCount | 50000 | Hard ceiling on particles, whatever the density. |
| particleBloom | 0.8 | Additive glow strength, 0–1. |
| particleOpacity | 0.7 | Global particle alpha. Kept under 1 so additive stacking does not clip hues to white. |
| particleJitter | 1 | Idle drift amplitude in pixels — the “alive” wobble. |
| particleSpeed | 0.085 | Spring 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 | ||
| showAxis | true | Axis lines and tick labels. |
| showGrid | true | Grid lines behind the plot. |
| showLegend | true | Legend — rendered only when there is more than one series or slice. |
| showTooltip | true | Hover tooltip and crosshair. |
| showValues | false | Print values next to the marks. |
| legendPosition | per type | top, bottom, left, right. Defaults to bottom where colour keys a series — line, area, bar, bubble, radar — and top for pie and donut. |
| legendAlign | per type | start, center, end. center under the plot, start otherwise. |
| theme | 'dark' | dark or light. Sets the chrome colours below in one go — anything you set explicitly still wins. Particle colours are never themed. |
| axisColor | rgba(255,255,255,.2) | Colour of the axis lines. |
| gridColor | rgba(255,255,255,.1) | Colour of the grid lines behind the plot. |
| textColor | rgba(255,255,255,.6) | Colour of the tick labels and axis titles. |
| crosshairColor | rgba(255,255,255,.22) | Colour of the hover crosshair. |
| fontFamily / fontSize | system sans / 11 | Typeface and size for axis labels. |
| min / max | auto | Pin the value axis instead of deriving it from the data. |
| valueFormat | compact | fn(value) → string for ticks, labels and tooltips. |
| Per type | ||
| curve | 'smooth' | Line interpolation: smooth, linear, step. |
| fillArea | false | Fill under the line with particles. type: 'area' turns this on for you. |
| lineWidth | 3.2 | Thickness of the particle band forming the stroke. |
| stacked | false | Stack bar series instead of grouping them. |
| horizontal | false | Lay bars on their side. |
| minRadius / maxRadius | 5 / 30 | Bubble radius range. Values map between the two by area, so twice the value covers twice the ink. |
| bubble.edgeFade | 0.35 | Feathering of the bubble rim, 0–1. |
| levels | 4 | Radar web rings between the centre and the edge. |
| webShape | 'polygon' | Radar web: polygon follows the spokes, circle draws true rings. |
| radar.fill / fillAmount | true / 0.55 | Fill the enclosed area, and the share of particles it takes. |
| innerRadius | 0 / 0.62 | Donut hole, as a fraction of the outer radius. |
| startAngle | -90 | Where the first slice begins, in degrees. |
| padAngle | 1.2 | Gap 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. |