npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spark-charts

v0.1.4

Published

A lightweight canvas charting library for time-series: crosshair, area fills, threshold lines, pan/zoom, smart date axis, add/remove series.

Readme

spark-charts

A small, dependency-free canvas charting library for time-series data, written in TypeScript. Finance-style dashboards out of the box: crosshair tracing, area fills, dashed threshold lines, pan/zoom, add/remove series, axis labels on either side, and a date X-axis that stays sensible from minutes to decades.

Features

  • Canvas rendering with a layered design: a main layer (grid, axes, series, thresholds) that only redraws on data/viewport/size changes, and a cheap overlay layer for the crosshair that redraws on every pointer move. A requestAnimationFrame scheduler coalesces invalidations to one redraw per frame.
  • Crosshair / trace — vertical + horizontal lines, a snapped marker dot per series, a date readout pill on the time axis, and live values pushed into the legend. Each series' colored pill stays pinned at its last value while you hover (so the final readout never disappears), and the live value under the crosshair rides along beside its marker — crosshair.valueLabel picks 'marker' (default, floats next to the marker), 'axis' (a neutral pill docked on the value axis), or 'none' (legend only). Set crosshair.pinLastValue: false for the older behaviour where the colored pill itself follows the crosshair.
  • Series typesLineSeries (with optional gradient area fill) and HistogramSeries (sign-coloured bars / sticks). Dense data is decimated to a min/max envelope per pixel column, so a 20-year daily series stays cheap to draw without losing spikes.
  • Threshold lines — solid/dashed/dotted horizontal lines with coloured name + value pills anchored to the axis (e.g. high p90 0.92%).
  • Add / remove series — programmatically (addLineSeries, removeSeries) or via the built-in interactive legend (click a chip to toggle, Clear to toggle all).
  • Pan / zoom — drag to pan, mouse-wheel to zoom around the cursor, double-click to reset. The value axis auto-scales to what's visible.
  • Axis on the left or right (valueAxis.side) with a pluggable value formatter (percent, fixed decimals, or your own).
  • Smart time axis — picks a nice calendar step (… 1m, 1h, 1d, 1mo, 3mo, 1y …) for the visible range and labels it hierarchically: months on a multi-month view, but the year at a year boundary; day numbers on a multi-day view, but the month at a month boundary.
  • HiDPI aware and responsive (redraws on container resize).

No runtime dependencies; ships as ESM with type declarations.

Install

npm install spark-charts

Quick start

import { Chart, percentFormat } from 'spark-charts';

const chart = new Chart(document.getElementById('app')!, {
  title: '15-Day Rolling Returns',
  legend: { visible: true, showClearButton: false },
  valueAxis: { side: 'right', format: percentFormat(2) },
});

const spy = chart.addLineSeries({ id: 'SPY', color: '#5b7cfa' });
spy.setData([
  { time: Date.UTC(2025, 0, 1) / 1000, value: 1.2 },
  { time: Date.UTC(2025, 0, 2) / 1000, value: 0.7 },
  // time is epoch seconds (UTC by default), matching the UNIX-timestamp convention
]);

Area fill

chart.addAreaSeries({
  id: 'drawdown',
  color: '#6ea8e6',
  area: { topColor: 'rgba(110,168,230,0.30)', bottomColor: 'rgba(110,168,230,0.02)' },
});

Histogram / sticks

chart.addHistogramSeries({
  id: 'highs', base: 0, positiveColor: '#3fa860', negativeColor: '#e0524e', barWidth: 0.24,
}).setData(points);

Threshold (dashed) lines

chart.addThresholdLine({
  value: 0.92, label: 'high p90', color: '#3fa860',
  lineStyle: 'dashed', labelValue: '0.92%',
});

Add / remove at runtime

const s = chart.addLineSeries({ id: 'TLT', color: '#e0913c' });
s.setData(data);
chart.removeSeries('TLT'); // or chart.removeSeries(s)

API

new Chart(container, options?) / createChart(container, options?)

| Method | Purpose | | --- | --- | | addLineSeries(opts) / addAreaSeries(opts) | Add a line (optionally area-filled). | | addHistogramSeries(opts) | Add sign-coloured bars. | | removeSeries(id \| series) | Remove a series. | | addThresholdLine(opts) → id / removeThresholdLine(id) | Manage threshold lines. | | setData(points) / update(point) | On a series: replace / append data. | | setVisibleRange(from, to) / fitContent() | Control the time window. | | getVisibleRange() | { from, to } in epoch seconds, or null. | | destroy() | Detach listeners and remove canvases. |

Options are deep-merged over DEFAULT_OPTIONS; pass only what you want to change. Key groups: layout, grid, valueAxis (side, autoScale, scaleMargins, format, minValue/maxValue), timeAxis (timezone, minTickSpacing, format, crosshairFormat), crosshair, legend, title.

Built-in value formatters: percentFormat(decimals), fixedFormat(decimals), autoNumberFormat.

Design notes

  • Time is always the X axis, stored as epoch seconds. The tick generator aligns to real calendar boundaries (months/years vary in length) and defaults to UTC so daily data doesn't drift across DST; set timeAxis.timezone: 'local' if you need local time.
  • Auto-fit is active until the user pans/zooms or you call setVisibleRange; after that the window is preserved (and clamped into the data bounds) across data updates. fitContent() resumes auto-fit.
  • The library renders into a container you size with CSS.

Development

npm install   # dev tooling only (esbuild, typescript, eslint)
npm run dev   # bundle + serve the demo at http://127.0.0.1:5173

| Script | Description | | --- | --- | | npm run build | Bundle the demo to examples/app.js. | | npm run build:lib | Bundle the library to dist/index.js + emit .d.ts. | | npm run typecheck | Strict tsc over src + examples (no emit). | | npm run lint | ESLint (type-checked rules). | | npm run check | typecheck + lint — the gate that must stay green. |

Strictness

The codebase type-checks with no any and lints with zero problems (errors or warnings). npm run check is the gate.

  • TypeScript (tsconfig.json): strict plus noUnusedLocals, noUnusedParameters, noImplicitReturns, noImplicitOverride, noFallthroughCasesInSwitch, noUncheckedSideEffectImports, erasableSyntaxOnly, verbatimModuleSyntax, isolatedModules.
  • ESLint (eslint.config.js, flat config): @eslint/js recommended + typescript-eslint recommendedTypeChecked, plus stricter custom rules (no-explicit-any, the no-unsafe-* family, consistent-type-imports, switch-exhaustiveness-check, no-confusing-void-expression, …) with explicit-function-return-type raised to error. Node tooling scripts (*.mjs) are linted as plain ESM without type-aware rules.

License

MIT