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

anagraph

v0.5.4

Published

Anagraph is a React charting library focused on one primary goal: **fast and smooth plot manipulation**.

Readme

Anagraph

Anagraph is a React charting library focused on one primary goal: fast and smooth plot manipulation.

It is built for interactive workflows where users drag, zoom, and inspect data continuously. The rendering model is performance-first, with worker-based drawing, frame-coalesced redraws, and data downsampling to keep interaction responsive.

Why this project exists

Many charting workflows degrade when datasets grow or when users manipulate the viewport quickly.

Anagraph is designed to stay responsive under those conditions:

  • smooth pan and zoom on mouse, trackpad, and touch
  • worker-backed rendering via OffscreenCanvas when available
  • redraw scheduling with requestAnimationFrame to avoid redundant work
  • line downsampling based on visible pixel width to reduce draw cost
  • integer pixel transforms in draw path for better canvas performance

If your product relies on fast, fluid chart interaction as a core UX requirement, this is what Anagraph optimizes for.

Features

  • Time-based X axis rendering (timestamps in milliseconds)
  • Multiple drawable layers:
    • Line
    • FillArea
    • VerticalFilling
    • BottomStatus
  • Shared bounds model for synchronized multi-chart panning/zooming
  • Pointer, wheel, touch, and double-click zoom interactions
  • Optional imperative chart API via ChartRef
  • Configurable chart appearance via ChartSettings

Installation

npm install anagraph

Peer runtime expectation: React 18+.

Quick start

1) Create a worker entry file

// src/anagraph.worker.ts
import { startWorker } from "anagraph";

startWorker();

2) Wire Anagraph in your React app

import { useMemo } from "react";
import { WorkerCreatorProvider, BoundsManager, Chart, Line, VerticalFilling, BottomStatus, FillArea } from "anagraph";

const dayStart = new Date();
dayStart.setHours(0, 0, 0, 0);

const xBounds: readonly [number, number] = [dayStart.getTime(), dayStart.getTime() + 24 * 60 * 60 * 1000];

export function ExampleChart() {
    const points = useMemo(
        () =>
            Array.from({ length: 10_000 }, (_, i) => {
                const x = xBounds[0] + (i / 10_000) * (xBounds[1] - xBounds[0]);
                const y = 50 + Math.sin(i / 80) * 35;
                return [x, y] as [number, number];
            }),
        [],
    );

    const workerCreator = useMemo(
        () => () =>
            new Worker(new URL("./anagraph.worker.ts", import.meta.url), {
                type: "module",
            }),
        [],
    );

    return (
        <WorkerCreatorProvider workerCreator={workerCreator}>
            <BoundsManager initialXBounds={xBounds} xBoundsLimit={xBounds}>
                <Chart
                    style={{ height: 360, border: "1px solid #d0d0d0" }}
                    settings={{
                        grid: { y: { bounds: [0, 100] } },
                        background: "#ffffff",
                    }}
                >
                    <VerticalFilling
                        intervals={[[xBounds[0], xBounds[0] + 2 * 60 * 60 * 1000]]}
                        color="rgba(255, 180, 0, 0.2)"
                    />
                    <FillArea
                        points={[
                            [xBounds[0], 30],
                            [xBounds[0] + 6 * 60 * 60 * 1000, 30],
                            [xBounds[0] + 6 * 60 * 60 * 1000, 70],
                            [xBounds[0], 70],
                        ]}
                        yBounds={[0, 100]}
                        fillColor="rgba(0, 140, 255, 0.08)"
                    />
                    <Line points={points} color="#1f6feb" lineWidth={2} yBounds={[0, 100]} />
                    <BottomStatus
                        intervals={[[xBounds[0] + 3 * 60 * 60 * 1000, xBounds[0] + 8 * 60 * 60 * 1000]]}
                        color="#14a44d"
                    />
                </Chart>
            </BoundsManager>
        </WorkerCreatorProvider>
    );
}

3) No worker yet?

If you do not want to configure a worker immediately, set disableWorker on Chart:

<Chart disableWorker>{/* layers */}</Chart>

This uses the single-thread fallback path.

Core API

Containers and context

  • WorkerCreatorProvider - supplies a workerCreator function that returns a new Worker
  • BoundsManager - owns interactive X bounds and propagates updates
  • Chart - rendering surface and interaction layer

Drawable layers

  • Line - polyline series (points, color, lineWidth, yBounds)
  • FillArea - polygon fill (points, fillColor, yBounds, optional border)
  • VerticalFilling - vertical interval highlights (intervals, color)
  • BottomStatus - compact status bars below the main grid (intervals, color)

Chart callbacks

  • onChangeBounds(bounds) - called continuously during manipulation
  • onChangeBoundsEnd(bounds) - called when manipulation ends
  • onHover(x, event) - hover on manipulation area
  • onHoverEnd() - hover leaves manipulation area
  • onTouchUp(x, event) - touch/pointer release callback

Imperative control (ChartRef)

  • xToPixelOffset(x)
  • pixelOffsetToX(pixelOffset)
  • setViewBounds(bounds)

Performance notes and best practices

To get the smoothest interaction in production:

  1. Keep line points sorted by X ascending.
  2. Use timestamps (ms) for X values if you want correct time legend behavior.
  3. Reuse arrays where possible instead of recreating huge datasets every render.
  4. Use onChangeBounds for lightweight UI updates and defer expensive work to onChangeBoundsEnd.
  5. Keep Chart.settings stable (memoize if building dynamically).
  6. Prefer worker mode (WorkerCreatorProvider) for heavy datasets.

Synchronized multi-chart interactions

Place multiple Chart components inside the same BoundsManager to sync pan/zoom across all of them.

Development

Install dependencies

npm install

Run Storybook

npm run storybook

Run tests

npm test

Build library

npm run build

Internal architecture (developer notes)

  • Canvas drawing happens in src/lib/worker
  • Main thread sends object-level update messages (add/change/remove) for each drawable layer
  • Worker stores chart state maps and redraws once per animation frame
  • Line rendering uses visualDownsample(...) based on visible X bounds and pixel width
  • If OffscreenCanvas is unavailable (or disableWorker is true), Anagraph uses an in-thread pseudo-worker path for compatibility

Current status

Anagraph is actively optimized around interaction performance. The main product goal remains unchanged: keep chart manipulation fast, stable, and pleasant for end users even as data volume grows.