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

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 labelled
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 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.


Quick start

Three lines to a chart.

Drop in the script, point it at an element, hand it some JSON.

index.html

        
multi-series & options

        
npm / bundler

        
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 colour, an array of colours, 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 colour 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 colours below in one go — anything you set explicitly still wins. Particle colours are never themed.
axisColorrgba(255,255,255,.2)Colour of the axis lines.
gridColorrgba(255,255,255,.1)Colour of the grid lines behind the plot.
textColorrgba(255,255,255,.6)Colour of the tick labels and axis titles.
crosshairColorrgba(255,255,255,.22)Colour 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 centre 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.