React data visualization library for streaming, networks, and AI-assisted development
See the codeA React data visualization library designed for AI-assisted development and streaming data.
Start with a small chart component. Add network graphs, streaming data and coordinated views when the task needs them. Structured schemas, diagnostics and rendering evidence help coding assistants implement, check and repair chart configurations.
Work through a complete task: check category totals, maintain a live chart, or correct a published chart. The source task packets identify their build and evidence scope; source availability does not establish installed or deployed parity.
3.12.1 extends network perspectives to the Atlas readers and improves projected
layout sizing, annotation anchors, and static SVG precision. Placement components
retain their identity across package entries, and semioticVite() removes unused
worker assets from Vite 7 builds while preserving workers that are used.
See the changelog for the full list and SVG compatibility notes.
Semiotic is a data visualization library for React that combines broad chart coverage with AI tooling. It supports network graphs, streaming data, statistical distributions and coordinated views, with schemas, examples and diagnostics for implementing and checking each task. Check the project's existing dependencies first: extending its current charting library may be the best fit for a small change.
Semiotic provides a workflow for generating, checking and repairing chart configurations. The July 27, 2026 evaluation reports model- and task-specific results; it does not guarantee first-try correctness.
semiotic/ai — a single import with the schema-backed chart capability catalog (XY, ordinal, network, realtime, geo, value, and portable recipes), optimized for LLM code generation. See ai/surface-manifest.json for the generated current inventory. Named imports now tree-shake across published chunks. Prefer family subpaths (semiotic/xy, semiotic/geo, semiotic/value, …) in production code to keep the intended chart family explicit; the cold-consumer table below reports initial-load and on-demand costs.ai/schema.json — machine-readable prop schemas for every componentnpx -y -p semiotic semiotic-mcp — an MCP server for tool-based chart rendering in any MCP clientnpx -p semiotic semiotic-ai --doctor — validate component + props JSON from the command line with typo suggestions and anti-pattern detectiondiagnoseConfig(component, props) — programmatic anti-pattern detector with actionable fixes, spanning validation, encoding, accessibility, and misleading-design (deception) checksauditData(component, props, data?) — chart-aware numeric preflight for inputs that pass schema validation but break the math: non-finite values, zero-span domains, invalid log inputs, negative size geometry, unsafe normalized totals, and scale-dominating outliers. Returns bounded row evidence and flows into diagnoseConfig, Chart Clinic, CLI doctor, and opt-in ChartContainer notificationsAGENTS.md — concise repository workflow shared by modern coding agents;
CLAUDE.md imports it and Copilot receives a short compatibility bridgeai/reference.md — complete on-demand product reference, kept out of
always-loaded coding-agent contextllms.txt — machine-readable documentation following the emerging standardEvery chart includes a built-in error boundary, dev-mode validation
warnings with typo suggestions, and accessibility features (canvas
aria-label, keyboard-navigable legends, keyboard live-region announcements, SVG
<title>/<desc>) so AI-generated code fails gracefully with
actionable diagnostics instead of a blank screen.
The European Accessibility Act has applied to covered products and services since 28 June 2025. A chart library cannot certify an application's legal compliance: scope, content, surrounding controls, testing, and national enforcement remain the application owner's responsibility. Semiotic supplies testable infrastructure for that work: keyboard interaction, accessible data tables, layered descriptions, structured navigation, reduced-motion and forced-colors paths, and WCAG-derived contrast tests for shipped theme presets. See the Accessibility docs and run the application's own assistive-technology and user testing.
Network visualization. Force-directed graphs, Sankey diagrams, chord diagrams, tree layouts, treemaps, circle packing, and orbit diagrams — all as React components with the same prop API as LineChart.
Streaming data. Realtime charts render on canvas at 60fps with a ref-based push API. Rapid network edge pushes coalesce into one layout per animation frame, while read/mutation methods preserve synchronous read-after-write semantics. Built-in decay, pulse, and staleness encoding for monitoring dashboards.
Coordinated views. LinkedCharts provides hover cross-highlighting,
brush cross-filtering, coordinate-based linked crosshairs, and selection
synchronization across any combination of chart types through shared selection state.
Geographic visualization. Choropleth maps, proportional symbol maps, flow maps with animated particles, and distance cartograms — all canvas-rendered with d3-geo projections, zoom/pan, tile basemaps, and drag-rotate globe spinning.
Statistical summaries. Box plots, violin plots, swarm plots, histograms, LOESS smoothing, forecast with confidence envelopes, and anomaly detection. Marginal distribution graphics on scatterplot axes with a single prop.
First-class annotations. Annotations are data-bound objects, not post-hoc artwork. Labels, callouts, thresholds, enclosures, statistical overlays, and React widgets move with the chart and render through browser, SSR, and export paths. Opt into placement, hierarchy, density, progressive disclosure, audience-aware amount, provenance, and editorial lifecycle when the chart needs to communicate more than its encoding alone.
| Layer | For | Example |
|---|---|---|
| Charts | Common chart forms with chart-level props | <LineChart data={d} xAccessor="x" yAccessor="y" /> |
| Frames | Full control over rendering, interaction, and layout | <StreamXYFrame chartType="line" lineStyle={...} /> |
Chart HOCs register only the mark plugins they need. Direct StreamXYFrame
loads the remaining built-ins on first client paint. Call
registerBuiltInXYPlugins() from semiotic/xy or semiotic/realtime/core
before the first render when SSR or the first frame must include marks.
Every Chart component accepts a frameProps prop to access the underlying
Frame API without leaving the simpler interface.
Charts serialize to JSON and back: toConfig, fromConfig, toURL,
copyConfig, configToJSX. Have Vega-Lite specs? fromVegaLite(spec)
translates them to Semiotic configs — works with configToJSX() for
full round-trip from notebooks and AI-generated specs.
Need an external pitfall review? The experimental unstable_toDataPitfallsChain() builds a
dependency-free chain input for datapitfalls,
combining the Semiotic config, JSX, reader grounding, diagnostics,
accessibility audit, and optional rendered SVG/image evidence:
import { unstable_toDataPitfallsChain } from "semiotic/experimental"
import { detectPitfalls } from "datapitfalls"
const input = unstable_toDataPitfallsChain("LineChart", props, {
narrative: "Monthly sales are accelerating.",
rendered: { svg, evidence },
})
const report = await detectPitfalls(input, { apiKey: process.env.ANTHROPIC_API_KEY })
The return path stays dependency-free too. Use whole-chart findings as
ChartContainer notifications, and only turn findings into annotations after
your app can anchor them to marks or semantic positions:
import { ChartContainer } from "semiotic"
import { LineChart } from "semiotic/xy"
import {
unstable_toDataPitfallsAnnotations,
unstable_toDataPitfallsNotifications,
} from "semiotic/experimental"
const notifications = unstable_toDataPitfallsNotifications(report)
const annotations = unstable_toDataPitfallsAnnotations(report, {
anchorFor: (finding) =>
finding.ruleId === "truncated-axis" ? { x: 9, y: 9000 } : null,
})
<ChartContainer notifications={notifications}>
<LineChart {...props} annotations={annotations} />
</ChartContainer>
Need a standard bar or line chart for a dashboard you'll never need to customize beyond colors and labels? Recharts has a larger ecosystem and more community examples. Need GPU-accelerated rendering for millions of data points? Apache ECharts handles that scale.
Semiotic is for projects that outgrow those libraries — when you need network graphs alongside time series, streaming data alongside static snapshots, or coordinated views across chart types.
npm install semiotic
Requires React 18.1+ or React 19.
Hover one chart and highlight the same data in another through a shared selection:
import { LinkedCharts, Scatterplot, BarChart } from "semiotic"
<LinkedCharts>
<Scatterplot
data={data} xAccessor="age" yAccessor="income" colorBy="region"
linkedHover={{ name: "hl", fields: ["region"] }}
selection={{ name: "hl" }}
/>
<BarChart
data={summary} categoryAccessor="region" valueAccessor="total"
selection={{ name: "hl" }}
/>
</LinkedCharts>
Live data fades old points, flashes new ones, flags stale feeds:
import { RealtimeLineChart } from "semiotic"
const chartRef = useRef()
chartRef.current.push({ time: Date.now(), value: cpuLoad })
<RealtimeLineChart
ref={chartRef}
timeAccessor="time"
valueAccessor="value"
decay={{ type: "exponential", halfLife: 100 }}
staleness={{ threshold: 5000, showBadge: true }}
/>
Force-directed graphs and Sankey diagrams — same API as LineChart:
import { ForceDirectedGraph, SankeyDiagram } from "semiotic"
<ForceDirectedGraph
nodes={people} edges={friendships}
colorBy="team" nodeSize={8} showLabels
/>
<SankeyDiagram
edges={budgetFlows}
sourceAccessor="from" targetAccessor="to" valueAccessor="amount"
/>
Choropleth maps, flow maps, and distance cartograms with canvas rendering, zoom/pan, tile basemaps, and animated particles:
import { ChoroplethMap, FlowMap, DistanceCartogram } from "semiotic/geo"
<ChoroplethMap
areas={geoJsonFeatures} valueAccessor="gdp"
colorScheme="viridis" projection="equalEarth" zoomable tooltip
/>
<FlowMap
nodes={airports} flows={routes} valueAccessor="passengers"
showParticles particleStyle={{ color: "source", speedMultiplier: 1.5 }}
/>
<DistanceCartogram
points={cities} center="rome" costAccessor="travelDays"
showRings costLabel="days" lines={routes}
/>
Live service topology with threshold alerting and click-to-inspect:
import { StreamNetworkFrame, ChartContainer, DetailsPanel, LinkedCharts } from "semiotic"
const chartRef = useRef()
chartRef.current.push({ source: "API", target: "Orders", value: 15 })
<LinkedCharts>
<ChartContainer title="System Monitor" status="live"
detailsPanel={
<DetailsPanel position="right" trigger="click">
{(datum) => <div>{datum.id}: {datum.value} req/s</div>}
</DetailsPanel>
}>
<StreamNetworkFrame ref={chartRef} chartType="sankey"
showParticles particleStyle={{ proportionalSpeed: true }}
thresholds={{ metric: n => n.value, warning: 100, critical: 250 }}
/>
</ChartContainer>
</LinkedCharts>
Line, bar, scatter, and area charts share the same accessor-driven API:
import { LineChart, BarChart } from "semiotic"
<LineChart
data={salesData}
xAccessor="month" yAccessor="revenue"
curve="monotoneX" showPoints
/>
<BarChart
data={categoryData}
categoryAccessor="department" valueAccessor="sales"
orientation="horizontal" colorBy="region"
/>
| Category | Components |
|---|---|
| XY | LineChart AreaChart DifferenceChart StackedAreaChart Scatterplot ConnectedScatterplot BubbleChart Heatmap QuadrantChart MultiAxisLineChart MinimapChart CandlestickChart ScatterplotMatrix |
| Categorical | BarChart StackedBarChart GroupedBarChart LikertChart SwimlaneChart FunnelChart SwarmPlot BoxPlot Histogram ViolinPlot RidgelinePlot DotPlot PieChart DonutChart GaugeChart |
| Network | ForceDirectedGraph ChordDiagram SankeyDiagram ProcessSankey TreeDiagram Treemap CirclePack OrbitDiagram |
| Geo | ChoroplethMap ProportionalSymbolMap FlowMap DistanceCartogram |
| Realtime | RealtimeLineChart RealtimeHistogram RealtimeSwarmChart RealtimeWaterfallChart RealtimeHeatmap |
| Coordination | LinkedCharts |
| Layout | ChartGrid ContextLayout CategoryColorProvider |
| Frames | StreamXYFrame StreamOrdinalFrame StreamNetworkFrame StreamGeoFrame |
Paste a Vega-Lite spec, get a Semiotic chart:
import { fromVegaLite } from "semiotic/data"
import { configToJSX, fromConfig } from "semiotic"
const config = fromVegaLite({
mark: "bar",
data: { values: [{ a: "A", b: 28 }, { a: "B", b: 55 }] },
encoding: {
x: { field: "a", type: "nominal" },
y: { field: "b", type: "quantitative" },
},
})
// Render directly
const { componentName, props } = fromConfig(config)
// → componentName: "BarChart", props: { data, categoryAccessor: "a", valueAccessor: "b" }
// Or generate JSX code
configToJSX(config)
// → <BarChart data={[...]} categoryAccessor="a" valueAccessor="b" />
Supports bar, line, area, point, rect, arc, tick marks with encoding translation for color, size, aggregation, and binning.
Capture and replay the path an AI-assisted chart session took:
import {
createLocalStorageConversationArcSink,
enableConversationArc,
getConversationArcStore,
loadConversationArc,
registerConversationArcSink,
} from "semiotic/ai"
const sink = createLocalStorageConversationArcSink({ key: "my-app:arc" })
registerConversationArcSink(sink)
enableConversationArc({ sessionId: "session-abc" })
getConversationArcStore().record({ type: "chart-rendered", component: "LineChart" })
loadConversationArc(sink.load(), { enabled: false })
Semiotic ships 41 stable JavaScript entry points (40 subpaths plus the root). Don't import from "semiotic" unless you need everything — use the smallest sub-path that matches your charts or tooling.
Custom network layouts can opt into a virtual zoom/pan viewport through semiotic/network/zoom. It exports ZoomableNetworkCustomChart and projected-size/LOD hooks; ordinary chart imports exclude its gesture and LOD runtime.
The numbers below are first-party artifact cost: the gzip size of Semiotic's own code for each sub-path. They exclude React and other runtime dependencies, and each row counts every chunk the entry can reach, including code loaded only on demand, so they are not a prediction of a cold application bundle. Do not add artifact rows to estimate an app: dependency resolution and cross-import deduplication happen in the consumer bundler and are measured separately below.
| Entry Point | gzip | What's inside |
|---|---|---|
semiotic/atlas | 302 KB | Motif Braid, Dependency Forest, and Flow Circuit readers |
semiotic/atlas/core | 13 KB | Network Atlas preparation, projections, and evidence queries |
semiotic/access | 29 KB | Chart Access Contract factory and first-wave baseline contracts |
semiotic/evidence | 45 KB | Chart Evidence Envelope, deterministic hashing, and publication gate |
semiotic/artifact | 123 KB | Renderer-independent contracts, claims, time, policy, grounding, and transfer audits |
semiotic/artifact/react | 4 KB | Accessible progressive disclosure for artifact contracts and policy evaluations |
semiotic/line | 144 KB | LineChart only — one-chart micro boundary |
semiotic/xy | 183 KB | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
semiotic/ordinal | 136 KB | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
semiotic/network | 178 KB | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
semiotic/network/zoom | 183 KB | Optional virtual network viewport, camera controls and consumer-owned LOD |
semiotic/network/perspective | 2 KB | Isometric pictograms and an accessible perspective toggle (the prop ships in ./network) |
semiotic/network/perspective/core | 3 KB | Projection sizing, resolution, and SVG placement helpers |
semiotic/vite | 1 KB | Build plugin for unused Semiotic worker assets |
semiotic/geo | 112 KB | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
semiotic/realtime | 211 KB | RealtimeLineChart, RealtimeHistogram, + 4 streaming charts |
semiotic/realtime/core | 209 KB | Streaming chart types, HOCs, and buffer helpers |
semiotic/realtime/react | 2 KB | Stream status and synced push hooks |
semiotic/server | 264 KB | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
semiotic/server/node | 264 KB | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
semiotic/server/edge | 274 KB | renderChart, renderChartWithEvidence, renderToStaticSVG, renderDashboard |
semiotic/utils | 107 KB | ThemeProvider, numeric/accessibility audits, serialization — no chart components |
semiotic/utils/core | 99 KB | Pure theme helpers, numeric/accessibility audits, and serialization |
semiotic/utils/react | 8 KB | ThemeProvider, useTheme, useReducedMotion, useHighContrast, useStreamStatus |
semiotic/recipes | 115 KB | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
semiotic/recipes/core | 113 KB | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
semiotic/recipes/react | 2 KB | Glyph and React layout-selection helpers |
semiotic/themes | 13 KB | Theme presets only (tufte, carbon, etc.) |
semiotic/themes/core | 13 KB | Theme presets and token helpers |
semiotic/themes/react | 8 KB | ThemeProvider/useTheme and hooks |
semiotic/data | 5 KB | bin, rollup, groupBy, pivot, fromVegaLite |
semiotic/value | 6 KB | BigNumber — focal-value KPI / scorecard (SingleValueFrame POC) |
semiotic/physics | 171 KB | GaltonBoardChart, EventDropChart, UnitPileChart, CollisionSwarmChart, PacketFlowChart, PhysicsCustomChart |
semiotic/physics/matter | 1 KB | Matter.js migration helpers + optional peer guard (no chart components) |
semiotic/physics/rapier | 1 KB | Rapier peer guard + adapter decision metadata (no chart components) |
semiotic/ai | 642 KB | All schema-backed charts + validation — optimized for LLM code generation |
semiotic/ai/core | 141 KB | suggestCharts, auditData, describeChart, repairChartConfig, tool adapters — no chart components |
semiotic/controls | 18 KB | DirectManipulationControl, CircularBrush, LinearBrush, MobileStandardControls, auditVisualizationControls — no frame renderer |
semiotic/rough | 3 KB | Optional deterministic Rough.js paint backend — exact Semiotic geometry remains authoritative |
semiotic/text | 2 KB | Optional Pretext annotation hook — peer package excluded |
semiotic | 421 KB | Full chart API and shared utilities |
The table above is first-party artifact cost, not an application bundle. The generated table
below measures a different thing: a fresh consumer bundles one retained named import from a packed
semiotic tarball through the public export path, with code splitting as an application build does.
Initial load is what a page downloads before the chart renders; on demand is code fetched
only when a feature needs it. It includes Semiotic and its resolved runtime dependencies,
but externalizes React/React DOM and optional adapter peers that the host application owns. Each row
starts cold, so use it to compare one public import choice—not to add together an application's rows.
The checked machine-readable baseline is benchmarks/setup/cold-consumer-imports.json; refresh it
after a production build with npm run docs:cold-consumer.
Method: fresh npm pack --ignore-scripts tarball → temporary consumer → minified/tree-shaken Rolldown (Vite's bundler) ESM build with code splitting and a production NODE_ENV → gzip -9 per file. Initial load counts the entry and its statically imported chunks; on demand counts chunks fetched only when a feature needs them (brushing, marginal graphics, animated transitions, a bare frame's built-in layouts). React/React DOM and optional adapter peers are external; Semiotic and its resolved runtime dependencies are included.
| Public named import | Runtime | gzip initial load | gzip on demand |
|---|---|---|---|
import { semioticVite } from "semiotic/vite" | node | 0.6 KiB | — |
import { getNetworkPerspectiveSize } from "semiotic/network/perspective/core" | browser | 1.1 KiB | — |
import { MotifBraidChart } from "semiotic/atlas" | browser | 102.5 KiB | 29.3 KiB |
import { prepareNetworkAtlas } from "semiotic/atlas/core" | browser | 5.9 KiB | — |
import { LineChart } from "semiotic" | browser | 118.2 KiB | 43.8 KiB |
import { LineChart } from "semiotic/xy" | browser | 117.8 KiB | 31.7 KiB |
import { LineChart } from "semiotic/line" | browser | 118.0 KiB | 31.8 KiB |
import { BarChart } from "semiotic/ordinal" | browser | 101.4 KiB | 14.7 KiB |
import { SankeyDiagram } from "semiotic/network" | browser | 126.0 KiB | 6.6 KiB |
import { ZoomableNetworkCustomChart } from "semiotic/network/zoom" | browser | 102.4 KiB | 29.3 KiB |
import { isometricGlyphs } from "semiotic/network/perspective" | browser | 1.4 KiB | — |
import { RealtimeLineChart } from "semiotic/realtime" | browser | 115.2 KiB | 41.5 KiB |
import { RingBuffer } from "semiotic/realtime/core" | browser | 0.7 KiB | 13.6 KiB |
import { useStreamStatus } from "semiotic/realtime/react" | browser | 0.6 KiB | — |
import { GaltonBoardChart } from "semiotic/physics" | browser | 86.0 KiB | 0.3 KiB |
import { MATTER_PHYSICS_CAPABILITIES } from "semiotic/physics/matter" | browser | 0.1 KiB | — |
import { RAPIER_PHYSICS_CAPABILITIES } from "semiotic/physics/rapier" | browser | 0.2 KiB | — |
import { renderChart } from "semiotic/server" | node | 284.7 KiB | — |
import { generateFrameSVGs } from "semiotic/server/edge" | node | 119.5 KiB | — |
import { renderToImage } from "semiotic/server/node" | node | 285.1 KiB | 0.6 KiB |
import { suggestCharts } from "semiotic/ai" | browser | 39.4 KiB | 13.6 KiB |
import { suggestCharts } from "semiotic/ai/core" | browser | 38.9 KiB | — |
import { buildArtifactContract } from "semiotic/artifact" | browser | 4.4 KiB | — |
import { ArtifactInspector } from "semiotic/artifact/react" | browser | 3.7 KiB | — |
import { createChartAccessContract } from "semiotic/access" | browser | 26.1 KiB | — |
import { toEvidenceEnvelope } from "semiotic/evidence" | browser | 41.8 KiB | — |
import { bin } from "semiotic/data" | browser | 0.9 KiB | — |
import { ChoroplethMap } from "semiotic/geo" | browser | 104.4 KiB | 1.7 KiB |
import { usePretextAnnotations } from "semiotic/text" | browser | 1.5 KiB | — |
import { createRoughRenderMode } from "semiotic/rough" | browser | 3.2 KiB | — |
import { resolveThemePreset } from "semiotic/themes" | browser | 2.5 KiB | — |
import { resolveThemePreset } from "semiotic/themes/core" | browser | 2.5 KiB | — |
import { ThemeProvider } from "semiotic/themes/react" | browser | 5.4 KiB | — |
import { validateProps } from "semiotic/utils" | browser | 9.6 KiB | — |
import { smartTickFormat } from "semiotic/utils/core" | browser | 1.1 KiB | — |
import { useReducedMotion } from "semiotic/utils/react" | browser | 0.3 KiB | — |
import { waffleLayout } from "semiotic/recipes" | browser | 1.6 KiB | — |
import { waffleLayout } from "semiotic/recipes/core" | browser | 1.6 KiB | — |
import { Glyph } from "semiotic/recipes/react" | browser | 0.8 KiB | — |
import { BigNumber } from "semiotic/value" | browser | 5.8 KiB | — |
import { DirectManipulationControl } from "semiotic/controls" | browser | 1.5 KiB | — |
Line-boundary interpretation: the retained named import from semiotic/line emits 352.4 KiB raw versus 352.4 KiB from semiotic/xy; gzip differs by 0.2 KiB (0.1%). Tree-shaking converges both paths on the same LineChart implementation graph. Treat semiotic/line as a narrower API/direct-ESM artifact boundary, not an application-bundle saving. Do not add another per-chart entry until its packed named import beats the family path by both 10 KiB gzip and 7%.
d3 packaging model: Semiotic externalizes the twelve d3 modules it imports
and declares them as normal runtime dependencies. Consumers do not need to
install d3 packages manually; their bundler resolves, deduplicates, and
tree-shakes that dependency graph. A packed webpack comparison favored this
model in three of four representative chart families, and a Next 16 webpack
route was 22.7 KiB gzip smaller than the fully bundled alternative. The one
bundled win, Sankey, was only 0.4 KiB gzip. This choice retains a 22-package,
1.9 MB unpacked d3 install closure in exchange for smaller common application
graphs and an ordinary dependency contract. The checked policy is
npm run check:d3-packaging; the reproducible evidence is in
benchmarks/setup/d3-packaging.json and can be regenerated with
npm run measure:d3-packaging -- --toolchain-root <dir> after installing
webpack and Next in that isolated toolchain directory.
// Import from the sub-path, not from "semiotic"
import { LineChart } from "semiotic/xy"
import { BarChart } from "semiotic/ordinal"
import { SankeyDiagram } from "semiotic/network"
import { ChoroplethMap } from "semiotic/geo"
Tree-shaking & multi-subpath imports: Family entries (semiotic/xy, semiotic/network, semiotic/ai, …) are built as one ESM graph with shared chunks. Stream frames, renderers, and other common code ship once and are imported by every entry that needs them — so combining semiotic/ai + semiotic/xy + semiotic/network does not mean paying for three full copies of the runtime. The package is marked "sideEffects": false, so modern bundlers keep only the named exports you retain (e.g. LineChart + suggestCharts). Prefer family subpaths for clarity; import AI helpers from semiotic/ai or the lighter semiotic/ai/core when you do not need the chart catalog.
When to use "semiotic": Fine when you want one import for mixed families. Shared chunks prevent duplicated runtime code across family subpaths; the cold-consumer table above is the better guide for a single named import.
CommonJS compatibility note: require("semiotic/xy") loads the shared
CommonJS client bundle (about 2.1 MB before compression) so React contexts stay
singletons across family imports. Prefer ESM imports in browser builds when
bundle size matters; splitting that CJS client without a context-identity
contract would be unsafe.
Built with strict: true. Full type definitions ship with the package.
Generics for type-safe accessors:
interface Sale { month: number; revenue: number }
<LineChart<Sale>
data={sales}
xAccessor="month" // TS validates this is keyof Sale
yAccessor="revenue"
/>
All chart components render SVG automatically in server environments — no
special imports or configuration needed. Non-streaming chart HOCs can be
imported and rendered directly from a Next.js Server Component: Semiotic's
"use client" directive defines the package boundary, so the importing page
does not need its own wrapper or directive. Props crossing that boundary must
remain serializable; add an app-owned client wrapper only when you introduce
hooks, callback props, browser state, or a push-driven streaming chart.
// app/dashboard/page.tsx — a Next.js Server Component
import { LineChart } from "semiotic/xy"
// Server: renders <svg> with path/circle/rect elements
// Client: renders <canvas> with SVG overlay for axes
export default async function DashboardPage() {
const data = await fetchMetrics()
return <LineChart data={data} xAccessor="date" yAccessor="value" />
}
For standalone SVG/PNG/GIF generation (email, OG images, PDF, Slack), use the server entry point:
import { renderChart, renderToImage, renderToAnimatedGif } from "semiotic/server"
// SVG — sync, no dependencies
const svg = renderChart("LineChart", {
data, xAccessor: "date", yAccessor: "value",
theme: "tufte", title: "Revenue Trend",
})
// PNG — async, requires sharp
const png = await renderToImage("BarChart", { data, ... }, { format: "png", scale: 2 })
// Animated GIF — async, requires sharp + gifenc
const gif = await renderToAnimatedGif("line", data, { ... }, { fps: 12 })
mcp-name: io.github.nteract/semiotic
Semiotic ships with an MCP server that lets AI coding assistants render charts, diagnose configuration problems, discover schemas, read packaged AI guidance, and get chart recommendations via tool calls.
Add to your MCP client config (e.g. claude_desktop_config.json for Claude Desktop):
{
"mcpServers": {
"semiotic": {
"command": "npx",
"args": ["-y", "-p", "semiotic", "semiotic-mcp"]
}
}
}
No API keys or authentication required. The server runs locally via stdio. HTTP mode is also available for inspectors, web clients, and ChatGPT Apps SDK experiments: npx -y -p semiotic semiotic-mcp --http --port 3001. It binds to 127.0.0.1 by default; intentionally expose another interface with --host 0.0.0.0 or MCP_HOST=0.0.0.0. Since 3.7.2, HTTP mode is stateless: each request gets a fresh read-only MCP server + transport, so it can autoscale on serverless hosts without sticky sessions.
For ChatGPT developer mode, expose the HTTP endpoint over HTTPS with a tunnel and create a connector that points at https://<your-tunnel>/mcp. The experimental Apps SDK surface is renderInteractiveChart, which returns a text/html;profile=mcp-app widget template plus a hidden SVG payload rendered by Semiotic on the MCP server.
For a hosted deployment, see deploy/cloud-run. The wrapper runs the published semiotic-mcp
binary, exposes /mcp plus health endpoints, and supports MCP_ALLOWED_HOSTS for production
host-header allowlisting. For ChatGPT Apps domain verification, set
OPENAI_APPS_CHALLENGE_TOKEN so HTTP mode serves the raw token from
/.well-known/openai-apps-challenge.
| Tool | Description |
|---|---|
renderChart | Render a Semiotic chart to static SVG. Supports the components returned by getSchema that are marked [renderable]. Pass { component: "LineChart", props: { data: [...], xAccessor: "x", yAccessor: "y" } }. Returns SVG string plus a "Render evidence" JSON block (mark counts by scene type, resolved axis domains, empty flag, annotation count, accessible name) so agents can verify the chart drew data marks, or validation errors with fix suggestions. |
renderInteractiveChart | Render a static-data chart as a ChatGPT Apps widget. Uses the same Semiotic server render path as renderChart, then hydrates an iframe UI with fit, zoom, data, hover, and render-evidence controls. |
getSchema | Return the prop schema for a specific component. Pass { component: "LineChart" } to get its props, or omit component to list the complete schema-backed catalog. Components marked [renderable] are available through renderChart; realtime charts require a browser/live environment. |
suggestChart | Sample-row recommender. Pass { data: [{...}, ...] } with 1–5 sample objects plus optional broad intent/capability filters. |
suggestCharts | Capability-based recommender for bounded row data. Returns ranked chart suggestions with scores, reasons, caveats, import paths, and ready-to-use props. |
suggestStreamCharts | Recommend streaming charts from a schema (shape, key, throughput). Returns { suggestions, excluded, stretchSuggestions }. |
suggestStreamDashboard | Compose a multi-panel dashboard from stream schemas and an audience layout policy. |
suggestDashboard | Build a multi-panel dashboard suggestion that covers distinct analytical intents. |
suggestStretchCharts | Recommend audience-literacy stretch picks from an AudienceProfile. |
repairChartConfig | Check whether a requested chart fits a dataset and return ranked alternatives when it does not. |
interrogateChart | Return a statistical summary and chart-aware context for answering natural-language questions with optional annotations. |
diagnoseConfig | Check a chart configuration for common problems — empty data, bad dimensions, missing accessors, wrong data shape, and more. Returns a human-readable diagnostic report with actionable fixes. |
reportIssue | Generate a pre-filled GitHub issue URL for bug reports or feature requests. Pass { title: "...", body: "...", labels: ["bug"] }. Returns a URL the user can open to submit. |
applyTheme | List named theme presets or return ThemeProvider/CSS/token usage for a preset such as { name: "tufte" }. |
auditArtifact | Evaluate an explicit artifact contract under a named policy without inferring missing source, review, or timing facts. |
recommendRepresentation | Compare chart and non-chart outcomes against supplied data, claims, evidence, time state, and policy requirements. |
repairArtifact | Propose contract repairs and optionally fill missing identity fields; existing mismatches require a revision and claim reassessment. |
explainRefusal | Explain which explicit policy obligations prevent publication or action and identify safe follow-up work. |
Artifact tools are opt-in diagnostics, not publication authorization. A
conditional result (including CLI exit code 0) retains open work. Ordinary
renderChart calls do not enforce artifact policy; governed hosts must enforce
their own release boundary. Manual-check resolution and revision-bound release
approval remain separate host workflow work.
| Resource | Description |
|---|---|
semiotic://artifact-contract-schema | Standalone JSON Schema for portable purpose, claim, evidence, timing, review, and transfer metadata. |
semiotic://schema | Full machine-readable component schema JSON. |
semiotic://schema-index | Compact discovery index with categories, renderability, import paths, and each component's schema resource URI. |
semiotic://schema/{component} | One component's schema, metadata, accessibility guidance, and behavior contracts (for example semiotic://schema/LineChart). |
semiotic://components | Backward-compatible component index showing renderable/browser-only status and MCP categories. |
semiotic://surface-manifest | Generated inventory of the current AI schema, exports, renderability, tools, resources, and prompts. |
semiotic://behavior-contracts | Agent-visible semantic rules for color precedence, required prop combinations, push refs, and renderability. |
semiotic://system-prompt | Compact AI instructions with import rules, chart props, SSR guidance, and pitfalls. |
semiotic://examples | Copy-paste chart examples by data shape. |
ui://semiotic/chart-widget.html | ChatGPT Apps / MCP Apps widget template used by renderInteractiveChart. |
| Prompt | Description |
|---|---|
build-semiotic-chart | Reusable workflow for choosing a chart, reading schema, diagnosing props, and rendering a preview. |
debug-semiotic-chart | Reusable workflow for debugging invalid props, rendering failures, and issue reports. |
Tool: getSchema
Args: { "component": "LineChart" }
→ Returns: { "name": "LineChart", "description": "...", "parameters": { "properties": { "data": ..., "xAccessor": ..., ... } } }
Tool: suggestChart
Args: {
"data": [
{ "month": "Jan", "revenue": 120, "region": "East" },
{ "month": "Feb", "revenue": 180, "region": "West" }
]
}
→ Returns:
1. BarChart (high confidence) — categorical field (region) with values (revenue)
2. StackedBarChart (medium confidence) — two categorical fields (month, region)
3. DonutChart (medium confidence) — 2 categories — proportional composition
Tool: renderChart
Args: {
"component": "BarChart",
"props": {
"data": [
{ "category": "Q1", "revenue": 120 },
{ "category": "Q2", "revenue": 180 },
{ "category": "Q3", "revenue": 150 }
],
"categoryAccessor": "category",
"valueAccessor": "revenue"
}
}
→ Returns: <svg>...</svg>
Tool: renderInteractiveChart
Args: {
"component": "BarChart",
"props": {
"title": "Revenue by Quarter",
"data": [
{ "quarter": "Q1", "revenue": 120 },
{ "quarter": "Q2", "revenue": 180 }
],
"categoryAccessor": "quarter",
"valueAccessor": "revenue"
}
}
→ Returns: structured chart summary for the model + hidden SVG/widget metadata for ChatGPT.
Tool: diagnoseConfig
Args: { "component": "LineChart", "props": { "data": [] } }
→ Returns: ✗ [EMPTY_DATA] data is an empty array — Fix: provide at least one data point
Tool: reportIssue
Args: {
"title": "Bug: BarChart tooltip shows undefined for custom accessor",
"body": "When using valueAccessor='amount', tooltip displays 'undefined'.\n\ndiagnoseConfig output: ✓ no issues detected.",
"labels": ["bug"]
}
→ Returns: Open this URL to submit the issue: https://github.com/nteract/semiotic/issues/new?...
For quick validation without an MCP client:
npx -p semiotic semiotic-ai --list # list components with import paths and renderability
npx -p semiotic semiotic-ai --list --json # machine-readable component index
npx -p semiotic semiotic-ai --schema GaugeChart
npx -p semiotic semiotic-ai --suggest '{"data":[{"category":"A","value":10}],"intent":"comparison"}'
npx -p semiotic semiotic-ai --doctor # validate component + props JSON
npx -p semiotic semiotic-ai --schema # dump all chart schemas
npx -p semiotic semiotic-ai --compact # compact schema (fewer tokens)
--doctor uses the full diagnoseConfig checks when dist is available and falls back to schema-only validation in clean source checkouts.
Semiotic is indexed by AI-coding-agent documentation tools so your assistant (Claude Code, Cursor, Cline, Copilot, etc.) can pull current docs and tools without copy-paste:
context7.json)The Official MCP Registry is the canonical MCP directory record; it is distinct from acceptance into any assistant vendor's curated connector directory. Secondary-directory freshness and release ownership are tracked in MCP_DISTRIBUTION.md.
Agent-facing API surface:
AGENTS.md is the concise repository development contract and CLAUDE.md imports it for Claude Code. These stay repository-local rather than shipping irrelevant contributor instructions to package consumers.ai/reference.md, ai/schema.json, ai/surface-manifest.json, ai/behaviorContracts.cjs, and agent-skill/semiotic-charts/SKILL.md are bundled in the npm tarball (see package.json#files). The reference is the on-demand product guide printed by npx -p semiotic semiotic-ai; the schema, manifest, contracts, and portable skill provide structured generation and validation guidance.semiotic.nteract.io/llms.txt + /llms-full.txt — deployed at the docs site per the llms.txt standard. Agents fetch the navigation map (llms.txt) or the full inlined docs (llms-full.txt) over HTTP; they're not part of the npm package itself.See CONTRIBUTING.md. Our community follows the nteract Code of Conduct.
Development of this library owes a lot to Susie Lu, Jason Reid, James Womack, Matt Herman, Shelby Sturgis, and Tristan Reid.
The Sankey layout engine is based on sankey-plus
by Tom Shanley, which improved on his earlier
d3-sankey-circular with better cycle detection, hierarchical arc stacking,
and dynamic extent adjustment.
Semiotic icon based on an icon by Andre Schauer.
Overall
Maintained
Code review
npm
Latest release
Releases this year
268 followers · starred Jan 2018
503 followers · starred Sep 2017
554 followers · starred Jul 2018
198 followers · starred Jul 2026
React data visualization library for streaming, networks, and AI-assisted development
See the codeA React data visualization library designed for AI-assisted development and streaming data.
Start with a small chart component. Add network graphs, streaming data and coordinated views when the task needs them. Structured schemas, diagnostics and rendering evidence help coding assistants implement, check and repair chart configurations.
Work through a complete task: check category totals, maintain a live chart, or correct a published chart. The source task packets identify their build and evidence scope; source availability does not establish installed or deployed parity.
3.12.1 extends network perspectives to the Atlas readers and improves projected
layout sizing, annotation anchors, and static SVG precision. Placement components
retain their identity across package entries, and semioticVite() removes unused
worker assets from Vite 7 builds while preserving workers that are used.
See the changelog for the full list and SVG compatibility notes.
Semiotic is a data visualization library for React that combines broad chart coverage with AI tooling. It supports network graphs, streaming data, statistical distributions and coordinated views, with schemas, examples and diagnostics for implementing and checking each task. Check the project's existing dependencies first: extending its current charting library may be the best fit for a small change.
Semiotic provides a workflow for generating, checking and repairing chart configurations. The July 27, 2026 evaluation reports model- and task-specific results; it does not guarantee first-try correctness.
semiotic/ai — a single import with the schema-backed chart capability catalog (XY, ordinal, network, realtime, geo, value, and portable recipes), optimized for LLM code generation. See ai/surface-manifest.json for the generated current inventory. Named imports now tree-shake across published chunks. Prefer family subpaths (semiotic/xy, semiotic/geo, semiotic/value, …) in production code to keep the intended chart family explicit; the cold-consumer table below reports initial-load and on-demand costs.ai/schema.json — machine-readable prop schemas for every componentnpx -y -p semiotic semiotic-mcp — an MCP server for tool-based chart rendering in any MCP clientnpx -p semiotic semiotic-ai --doctor — validate component + props JSON from the command line with typo suggestions and anti-pattern detectiondiagnoseConfig(component, props) — programmatic anti-pattern detector with actionable fixes, spanning validation, encoding, accessibility, and misleading-design (deception) checksauditData(component, props, data?) — chart-aware numeric preflight for inputs that pass schema validation but break the math: non-finite values, zero-span domains, invalid log inputs, negative size geometry, unsafe normalized totals, and scale-dominating outliers. Returns bounded row evidence and flows into diagnoseConfig, Chart Clinic, CLI doctor, and opt-in ChartContainer notificationsAGENTS.md — concise repository workflow shared by modern coding agents;
CLAUDE.md imports it and Copilot receives a short compatibility bridgeai/reference.md — complete on-demand product reference, kept out of
always-loaded coding-agent contextllms.txt — machine-readable documentation following the emerging standardEvery chart includes a built-in error boundary, dev-mode validation
warnings with typo suggestions, and accessibility features (canvas
aria-label, keyboard-navigable legends, keyboard live-region announcements, SVG
<title>/<desc>) so AI-generated code fails gracefully with
actionable diagnostics instead of a blank screen.
The European Accessibility Act has applied to covered products and services since 28 June 2025. A chart library cannot certify an application's legal compliance: scope, content, surrounding controls, testing, and national enforcement remain the application owner's responsibility. Semiotic supplies testable infrastructure for that work: keyboard interaction, accessible data tables, layered descriptions, structured navigation, reduced-motion and forced-colors paths, and WCAG-derived contrast tests for shipped theme presets. See the Accessibility docs and run the application's own assistive-technology and user testing.
Network visualization. Force-directed graphs, Sankey diagrams, chord diagrams, tree layouts, treemaps, circle packing, and orbit diagrams — all as React components with the same prop API as LineChart.
Streaming data. Realtime charts render on canvas at 60fps with a ref-based push API. Rapid network edge pushes coalesce into one layout per animation frame, while read/mutation methods preserve synchronous read-after-write semantics. Built-in decay, pulse, and staleness encoding for monitoring dashboards.
Coordinated views. LinkedCharts provides hover cross-highlighting,
brush cross-filtering, coordinate-based linked crosshairs, and selection
synchronization across any combination of chart types through shared selection state.
Geographic visualization. Choropleth maps, proportional symbol maps, flow maps with animated particles, and distance cartograms — all canvas-rendered with d3-geo projections, zoom/pan, tile basemaps, and drag-rotate globe spinning.
Statistical summaries. Box plots, violin plots, swarm plots, histograms, LOESS smoothing, forecast with confidence envelopes, and anomaly detection. Marginal distribution graphics on scatterplot axes with a single prop.
First-class annotations. Annotations are data-bound objects, not post-hoc artwork. Labels, callouts, thresholds, enclosures, statistical overlays, and React widgets move with the chart and render through browser, SSR, and export paths. Opt into placement, hierarchy, density, progressive disclosure, audience-aware amount, provenance, and editorial lifecycle when the chart needs to communicate more than its encoding alone.
| Layer | For | Example |
|---|---|---|
| Charts | Common chart forms with chart-level props | <LineChart data={d} xAccessor="x" yAccessor="y" /> |
| Frames | Full control over rendering, interaction, and layout | <StreamXYFrame chartType="line" lineStyle={...} /> |
Chart HOCs register only the mark plugins they need. Direct StreamXYFrame
loads the remaining built-ins on first client paint. Call
registerBuiltInXYPlugins() from semiotic/xy or semiotic/realtime/core
before the first render when SSR or the first frame must include marks.
Every Chart component accepts a frameProps prop to access the underlying
Frame API without leaving the simpler interface.
Charts serialize to JSON and back: toConfig, fromConfig, toURL,
copyConfig, configToJSX. Have Vega-Lite specs? fromVegaLite(spec)
translates them to Semiotic configs — works with configToJSX() for
full round-trip from notebooks and AI-generated specs.
Need an external pitfall review? The experimental unstable_toDataPitfallsChain() builds a
dependency-free chain input for datapitfalls,
combining the Semiotic config, JSX, reader grounding, diagnostics,
accessibility audit, and optional rendered SVG/image evidence:
import { unstable_toDataPitfallsChain } from "semiotic/experimental"
import { detectPitfalls } from "datapitfalls"
const input = unstable_toDataPitfallsChain("LineChart", props, {
narrative: "Monthly sales are accelerating.",
rendered: { svg, evidence },
})
const report = await detectPitfalls(input, { apiKey: process.env.ANTHROPIC_API_KEY })
The return path stays dependency-free too. Use whole-chart findings as
ChartContainer notifications, and only turn findings into annotations after
your app can anchor them to marks or semantic positions:
import { ChartContainer } from "semiotic"
import { LineChart } from "semiotic/xy"
import {
unstable_toDataPitfallsAnnotations,
unstable_toDataPitfallsNotifications,
} from "semiotic/experimental"
const notifications = unstable_toDataPitfallsNotifications(report)
const annotations = unstable_toDataPitfallsAnnotations(report, {
anchorFor: (finding) =>
finding.ruleId === "truncated-axis" ? { x: 9, y: 9000 } : null,
})
<ChartContainer notifications={notifications}>
<LineChart {...props} annotations={annotations} />
</ChartContainer>
Need a standard bar or line chart for a dashboard you'll never need to customize beyond colors and labels? Recharts has a larger ecosystem and more community examples. Need GPU-accelerated rendering for millions of data points? Apache ECharts handles that scale.
Semiotic is for projects that outgrow those libraries — when you need network graphs alongside time series, streaming data alongside static snapshots, or coordinated views across chart types.
npm install semiotic
Requires React 18.1+ or React 19.
Hover one chart and highlight the same data in another through a shared selection:
import { LinkedCharts, Scatterplot, BarChart } from "semiotic"
<LinkedCharts>
<Scatterplot
data={data} xAccessor="age" yAccessor="income" colorBy="region"
linkedHover={{ name: "hl", fields: ["region"] }}
selection={{ name: "hl" }}
/>
<BarChart
data={summary} categoryAccessor="region" valueAccessor="total"
selection={{ name: "hl" }}
/>
</LinkedCharts>
Live data fades old points, flashes new ones, flags stale feeds:
import { RealtimeLineChart } from "semiotic"
const chartRef = useRef()
chartRef.current.push({ time: Date.now(), value: cpuLoad })
<RealtimeLineChart
ref={chartRef}
timeAccessor="time"
valueAccessor="value"
decay={{ type: "exponential", halfLife: 100 }}
staleness={{ threshold: 5000, showBadge: true }}
/>
Force-directed graphs and Sankey diagrams — same API as LineChart:
import { ForceDirectedGraph, SankeyDiagram } from "semiotic"
<ForceDirectedGraph
nodes={people} edges={friendships}
colorBy="team" nodeSize={8} showLabels
/>
<SankeyDiagram
edges={budgetFlows}
sourceAccessor="from" targetAccessor="to" valueAccessor="amount"
/>
Choropleth maps, flow maps, and distance cartograms with canvas rendering, zoom/pan, tile basemaps, and animated particles:
import { ChoroplethMap, FlowMap, DistanceCartogram } from "semiotic/geo"
<ChoroplethMap
areas={geoJsonFeatures} valueAccessor="gdp"
colorScheme="viridis" projection="equalEarth" zoomable tooltip
/>
<FlowMap
nodes={airports} flows={routes} valueAccessor="passengers"
showParticles particleStyle={{ color: "source", speedMultiplier: 1.5 }}
/>
<DistanceCartogram
points={cities} center="rome" costAccessor="travelDays"
showRings costLabel="days" lines={routes}
/>
Live service topology with threshold alerting and click-to-inspect:
import { StreamNetworkFrame, ChartContainer, DetailsPanel, LinkedCharts } from "semiotic"
const chartRef = useRef()
chartRef.current.push({ source: "API", target: "Orders", value: 15 })
<LinkedCharts>
<ChartContainer title="System Monitor" status="live"
detailsPanel={
<DetailsPanel position="right" trigger="click">
{(datum) => <div>{datum.id}: {datum.value} req/s</div>}
</DetailsPanel>
}>
<StreamNetworkFrame ref={chartRef} chartType="sankey"
showParticles particleStyle={{ proportionalSpeed: true }}
thresholds={{ metric: n => n.value, warning: 100, critical: 250 }}
/>
</ChartContainer>
</LinkedCharts>
Line, bar, scatter, and area charts share the same accessor-driven API:
import { LineChart, BarChart } from "semiotic"
<LineChart
data={salesData}
xAccessor="month" yAccessor="revenue"
curve="monotoneX" showPoints
/>
<BarChart
data={categoryData}
categoryAccessor="department" valueAccessor="sales"
orientation="horizontal" colorBy="region"
/>
| Category | Components |
|---|---|
| XY | LineChart AreaChart DifferenceChart StackedAreaChart Scatterplot ConnectedScatterplot BubbleChart Heatmap QuadrantChart MultiAxisLineChart MinimapChart CandlestickChart ScatterplotMatrix |
| Categorical | BarChart StackedBarChart GroupedBarChart LikertChart SwimlaneChart FunnelChart SwarmPlot BoxPlot Histogram ViolinPlot RidgelinePlot DotPlot PieChart DonutChart GaugeChart |
| Network | ForceDirectedGraph ChordDiagram SankeyDiagram ProcessSankey TreeDiagram Treemap CirclePack OrbitDiagram |
| Geo | ChoroplethMap ProportionalSymbolMap FlowMap DistanceCartogram |
| Realtime | RealtimeLineChart RealtimeHistogram RealtimeSwarmChart RealtimeWaterfallChart RealtimeHeatmap |
| Coordination | LinkedCharts |
| Layout | ChartGrid ContextLayout CategoryColorProvider |
| Frames | StreamXYFrame StreamOrdinalFrame StreamNetworkFrame StreamGeoFrame |
Paste a Vega-Lite spec, get a Semiotic chart:
import { fromVegaLite } from "semiotic/data"
import { configToJSX, fromConfig } from "semiotic"
const config = fromVegaLite({
mark: "bar",
data: { values: [{ a: "A", b: 28 }, { a: "B", b: 55 }] },
encoding: {
x: { field: "a", type: "nominal" },
y: { field: "b", type: "quantitative" },
},
})
// Render directly
const { componentName, props } = fromConfig(config)
// → componentName: "BarChart", props: { data, categoryAccessor: "a", valueAccessor: "b" }
// Or generate JSX code
configToJSX(config)
// → <BarChart data={[...]} categoryAccessor="a" valueAccessor="b" />
Supports bar, line, area, point, rect, arc, tick marks with encoding translation for color, size, aggregation, and binning.
Capture and replay the path an AI-assisted chart session took:
import {
createLocalStorageConversationArcSink,
enableConversationArc,
getConversationArcStore,
loadConversationArc,
registerConversationArcSink,
} from "semiotic/ai"
const sink = createLocalStorageConversationArcSink({ key: "my-app:arc" })
registerConversationArcSink(sink)
enableConversationArc({ sessionId: "session-abc" })
getConversationArcStore().record({ type: "chart-rendered", component: "LineChart" })
loadConversationArc(sink.load(), { enabled: false })
Semiotic ships 41 stable JavaScript entry points (40 subpaths plus the root). Don't import from "semiotic" unless you need everything — use the smallest sub-path that matches your charts or tooling.
Custom network layouts can opt into a virtual zoom/pan viewport through semiotic/network/zoom. It exports ZoomableNetworkCustomChart and projected-size/LOD hooks; ordinary chart imports exclude its gesture and LOD runtime.
The numbers below are first-party artifact cost: the gzip size of Semiotic's own code for each sub-path. They exclude React and other runtime dependencies, and each row counts every chunk the entry can reach, including code loaded only on demand, so they are not a prediction of a cold application bundle. Do not add artifact rows to estimate an app: dependency resolution and cross-import deduplication happen in the consumer bundler and are measured separately below.
| Entry Point | gzip | What's inside |
|---|---|---|
semiotic/atlas | 302 KB | Motif Braid, Dependency Forest, and Flow Circuit readers |
semiotic/atlas/core | 13 KB | Network Atlas preparation, projections, and evidence queries |
semiotic/access | 29 KB | Chart Access Contract factory and first-wave baseline contracts |
semiotic/evidence | 45 KB | Chart Evidence Envelope, deterministic hashing, and publication gate |
semiotic/artifact | 123 KB | Renderer-independent contracts, claims, time, policy, grounding, and transfer audits |
semiotic/artifact/react | 4 KB | Accessible progressive disclosure for artifact contracts and policy evaluations |
semiotic/line | 144 KB | LineChart only — one-chart micro boundary |
semiotic/xy | 183 KB | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
semiotic/ordinal | 136 KB | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
semiotic/network | 178 KB | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
semiotic/network/zoom | 183 KB | Optional virtual network viewport, camera controls and consumer-owned LOD |
semiotic/network/perspective | 2 KB | Isometric pictograms and an accessible perspective toggle (the prop ships in ./network) |
semiotic/network/perspective/core | 3 KB | Projection sizing, resolution, and SVG placement helpers |
semiotic/vite | 1 KB | Build plugin for unused Semiotic worker assets |
semiotic/geo | 112 KB | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
semiotic/realtime | 211 KB | RealtimeLineChart, RealtimeHistogram, + 4 streaming charts |
semiotic/realtime/core | 209 KB | Streaming chart types, HOCs, and buffer helpers |
semiotic/realtime/react | 2 KB | Stream status and synced push hooks |
semiotic/server | 264 KB | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
semiotic/server/node | 264 KB | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
semiotic/server/edge | 274 KB | renderChart, renderChartWithEvidence, renderToStaticSVG, renderDashboard |
semiotic/utils | 107 KB | ThemeProvider, numeric/accessibility audits, serialization — no chart components |
semiotic/utils/core | 99 KB | Pure theme helpers, numeric/accessibility audits, and serialization |
semiotic/utils/react | 8 KB | ThemeProvider, useTheme, useReducedMotion, useHighContrast, useStreamStatus |
semiotic/recipes | 115 KB | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
semiotic/recipes/core | 113 KB | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
semiotic/recipes/react | 2 KB | Glyph and React layout-selection helpers |
semiotic/themes | 13 KB | Theme presets only (tufte, carbon, etc.) |
semiotic/themes/core | 13 KB | Theme presets and token helpers |
semiotic/themes/react | 8 KB | ThemeProvider/useTheme and hooks |
semiotic/data | 5 KB | bin, rollup, groupBy, pivot, fromVegaLite |
semiotic/value | 6 KB | BigNumber — focal-value KPI / scorecard (SingleValueFrame POC) |
semiotic/physics | 171 KB | GaltonBoardChart, EventDropChart, UnitPileChart, CollisionSwarmChart, PacketFlowChart, PhysicsCustomChart |
semiotic/physics/matter | 1 KB | Matter.js migration helpers + optional peer guard (no chart components) |
semiotic/physics/rapier | 1 KB | Rapier peer guard + adapter decision metadata (no chart components) |
semiotic/ai | 642 KB | All schema-backed charts + validation — optimized for LLM code generation |
semiotic/ai/core | 141 KB | suggestCharts, auditData, describeChart, repairChartConfig, tool adapters — no chart components |
semiotic/controls | 18 KB | DirectManipulationControl, CircularBrush, LinearBrush, MobileStandardControls, auditVisualizationControls — no frame renderer |
semiotic/rough | 3 KB | Optional deterministic Rough.js paint backend — exact Semiotic geometry remains authoritative |
semiotic/text | 2 KB | Optional Pretext annotation hook — peer package excluded |
semiotic | 421 KB | Full chart API and shared utilities |
The table above is first-party artifact cost, not an application bundle. The generated table
below measures a different thing: a fresh consumer bundles one retained named import from a packed
semiotic tarball through the public export path, with code splitting as an application build does.
Initial load is what a page downloads before the chart renders; on demand is code fetched
only when a feature needs it. It includes Semiotic and its resolved runtime dependencies,
but externalizes React/React DOM and optional adapter peers that the host application owns. Each row
starts cold, so use it to compare one public import choice—not to add together an application's rows.
The checked machine-readable baseline is benchmarks/setup/cold-consumer-imports.json; refresh it
after a production build with npm run docs:cold-consumer.
Method: fresh npm pack --ignore-scripts tarball → temporary consumer → minified/tree-shaken Rolldown (Vite's bundler) ESM build with code splitting and a production NODE_ENV → gzip -9 per file. Initial load counts the entry and its statically imported chunks; on demand counts chunks fetched only when a feature needs them (brushing, marginal graphics, animated transitions, a bare frame's built-in layouts). React/React DOM and optional adapter peers are external; Semiotic and its resolved runtime dependencies are included.
| Public named import | Runtime | gzip initial load | gzip on demand |
|---|---|---|---|
import { semioticVite } from "semiotic/vite" | node | 0.6 KiB | — |
import { getNetworkPerspectiveSize } from "semiotic/network/perspective/core" | browser | 1.1 KiB | — |
import { MotifBraidChart } from "semiotic/atlas" | browser | 102.5 KiB | 29.3 KiB |
import { prepareNetworkAtlas } from "semiotic/atlas/core" | browser | 5.9 KiB | — |
import { LineChart } from "semiotic" | browser | 118.2 KiB | 43.8 KiB |
import { LineChart } from "semiotic/xy" | browser | 117.8 KiB | 31.7 KiB |
import { LineChart } from "semiotic/line" | browser | 118.0 KiB | 31.8 KiB |
import { BarChart } from "semiotic/ordinal" | browser | 101.4 KiB | 14.7 KiB |
import { SankeyDiagram } from "semiotic/network" | browser | 126.0 KiB | 6.6 KiB |
import { ZoomableNetworkCustomChart } from "semiotic/network/zoom" | browser | 102.4 KiB | 29.3 KiB |
import { isometricGlyphs } from "semiotic/network/perspective" | browser | 1.4 KiB | — |
import { RealtimeLineChart } from "semiotic/realtime" | browser | 115.2 KiB | 41.5 KiB |
import { RingBuffer } from "semiotic/realtime/core" | browser | 0.7 KiB | 13.6 KiB |
import { useStreamStatus } from "semiotic/realtime/react" | browser | 0.6 KiB | — |
import { GaltonBoardChart } from "semiotic/physics" | browser | 86.0 KiB | 0.3 KiB |
import { MATTER_PHYSICS_CAPABILITIES } from "semiotic/physics/matter" | browser | 0.1 KiB | — |
import { RAPIER_PHYSICS_CAPABILITIES } from "semiotic/physics/rapier" | browser | 0.2 KiB | — |
import { renderChart } from "semiotic/server" | node | 284.7 KiB | — |
import { generateFrameSVGs } from "semiotic/server/edge" | node | 119.5 KiB | — |
import { renderToImage } from "semiotic/server/node" | node | 285.1 KiB | 0.6 KiB |
import { suggestCharts } from "semiotic/ai" | browser | 39.4 KiB | 13.6 KiB |
import { suggestCharts } from "semiotic/ai/core" | browser | 38.9 KiB | — |
import { buildArtifactContract } from "semiotic/artifact" | browser | 4.4 KiB | — |
import { ArtifactInspector } from "semiotic/artifact/react" | browser | 3.7 KiB | — |
import { createChartAccessContract } from "semiotic/access" | browser | 26.1 KiB | — |
import { toEvidenceEnvelope } from "semiotic/evidence" | browser | 41.8 KiB | — |
import { bin } from "semiotic/data" | browser | 0.9 KiB | — |
import { ChoroplethMap } from "semiotic/geo" | browser | 104.4 KiB | 1.7 KiB |
import { usePretextAnnotations } from "semiotic/text" | browser | 1.5 KiB | — |
import { createRoughRenderMode } from "semiotic/rough" | browser | 3.2 KiB | — |
import { resolveThemePreset } from "semiotic/themes" | browser | 2.5 KiB | — |
import { resolveThemePreset } from "semiotic/themes/core" | browser | 2.5 KiB | — |
import { ThemeProvider } from "semiotic/themes/react" | browser | 5.4 KiB | — |
import { validateProps } from "semiotic/utils" | browser | 9.6 KiB | — |
import { smartTickFormat } from "semiotic/utils/core" | browser | 1.1 KiB | — |
import { useReducedMotion } from "semiotic/utils/react" | browser | 0.3 KiB | — |
import { waffleLayout } from "semiotic/recipes" | browser | 1.6 KiB | — |
import { waffleLayout } from "semiotic/recipes/core" | browser | 1.6 KiB | — |
import { Glyph } from "semiotic/recipes/react" | browser | 0.8 KiB | — |
import { BigNumber } from "semiotic/value" | browser | 5.8 KiB | — |
import { DirectManipulationControl } from "semiotic/controls" | browser | 1.5 KiB | — |
Line-boundary interpretation: the retained named import from semiotic/line emits 352.4 KiB raw versus 352.4 KiB from semiotic/xy; gzip differs by 0.2 KiB (0.1%). Tree-shaking converges both paths on the same LineChart implementation graph. Treat semiotic/line as a narrower API/direct-ESM artifact boundary, not an application-bundle saving. Do not add another per-chart entry until its packed named import beats the family path by both 10 KiB gzip and 7%.
d3 packaging model: Semiotic externalizes the twelve d3 modules it imports
and declares them as normal runtime dependencies. Consumers do not need to
install d3 packages manually; their bundler resolves, deduplicates, and
tree-shakes that dependency graph. A packed webpack comparison favored this
model in three of four representative chart families, and a Next 16 webpack
route was 22.7 KiB gzip smaller than the fully bundled alternative. The one
bundled win, Sankey, was only 0.4 KiB gzip. This choice retains a 22-package,
1.9 MB unpacked d3 install closure in exchange for smaller common application
graphs and an ordinary dependency contract. The checked policy is
npm run check:d3-packaging; the reproducible evidence is in
benchmarks/setup/d3-packaging.json and can be regenerated with
npm run measure:d3-packaging -- --toolchain-root <dir> after installing
webpack and Next in that isolated toolchain directory.
// Import from the sub-path, not from "semiotic"
import { LineChart } from "semiotic/xy"
import { BarChart } from "semiotic/ordinal"
import { SankeyDiagram } from "semiotic/network"
import { ChoroplethMap } from "semiotic/geo"
Tree-shaking & multi-subpath imports: Family entries (semiotic/xy, semiotic/network, semiotic/ai, …) are built as one ESM graph with shared chunks. Stream frames, renderers, and other common code ship once and are imported by every entry that needs them — so combining semiotic/ai + semiotic/xy + semiotic/network does not mean paying for three full copies of the runtime. The package is marked "sideEffects": false, so modern bundlers keep only the named exports you retain (e.g. LineChart + suggestCharts). Prefer family subpaths for clarity; import AI helpers from semiotic/ai or the lighter semiotic/ai/core when you do not need the chart catalog.
When to use "semiotic": Fine when you want one import for mixed families. Shared chunks prevent duplicated runtime code across family subpaths; the cold-consumer table above is the better guide for a single named import.
CommonJS compatibility note: require("semiotic/xy") loads the shared
CommonJS client bundle (about 2.1 MB before compression) so React contexts stay
singletons across family imports. Prefer ESM imports in browser builds when
bundle size matters; splitting that CJS client without a context-identity
contract would be unsafe.
Built with strict: true. Full type definitions ship with the package.
Generics for type-safe accessors:
interface Sale { month: number; revenue: number }
<LineChart<Sale>
data={sales}
xAccessor="month" // TS validates this is keyof Sale
yAccessor="revenue"
/>
All chart components render SVG automatically in server environments — no
special imports or configuration needed. Non-streaming chart HOCs can be
imported and rendered directly from a Next.js Server Component: Semiotic's
"use client" directive defines the package boundary, so the importing page
does not need its own wrapper or directive. Props crossing that boundary must
remain serializable; add an app-owned client wrapper only when you introduce
hooks, callback props, browser state, or a push-driven streaming chart.
// app/dashboard/page.tsx — a Next.js Server Component
import { LineChart } from "semiotic/xy"
// Server: renders <svg> with path/circle/rect elements
// Client: renders <canvas> with SVG overlay for axes
export default async function DashboardPage() {
const data = await fetchMetrics()
return <LineChart data={data} xAccessor="date" yAccessor="value" />
}
For standalone SVG/PNG/GIF generation (email, OG images, PDF, Slack), use the server entry point:
import { renderChart, renderToImage, renderToAnimatedGif } from "semiotic/server"
// SVG — sync, no dependencies
const svg = renderChart("LineChart", {
data, xAccessor: "date", yAccessor: "value",
theme: "tufte", title: "Revenue Trend",
})
// PNG — async, requires sharp
const png = await renderToImage("BarChart", { data, ... }, { format: "png", scale: 2 })
// Animated GIF — async, requires sharp + gifenc
const gif = await renderToAnimatedGif("line", data, { ... }, { fps: 12 })
mcp-name: io.github.nteract/semiotic
Semiotic ships with an MCP server that lets AI coding assistants render charts, diagnose configuration problems, discover schemas, read packaged AI guidance, and get chart recommendations via tool calls.
Add to your MCP client config (e.g. claude_desktop_config.json for Claude Desktop):
{
"mcpServers": {
"semiotic": {
"command": "npx",
"args": ["-y", "-p", "semiotic", "semiotic-mcp"]
}
}
}
No API keys or authentication required. The server runs locally via stdio. HTTP mode is also available for inspectors, web clients, and ChatGPT Apps SDK experiments: npx -y -p semiotic semiotic-mcp --http --port 3001. It binds to 127.0.0.1 by default; intentionally expose another interface with --host 0.0.0.0 or MCP_HOST=0.0.0.0. Since 3.7.2, HTTP mode is stateless: each request gets a fresh read-only MCP server + transport, so it can autoscale on serverless hosts without sticky sessions.
For ChatGPT developer mode, expose the HTTP endpoint over HTTPS with a tunnel and create a connector that points at https://<your-tunnel>/mcp. The experimental Apps SDK surface is renderInteractiveChart, which returns a text/html;profile=mcp-app widget template plus a hidden SVG payload rendered by Semiotic on the MCP server.
For a hosted deployment, see deploy/cloud-run. The wrapper runs the published semiotic-mcp
binary, exposes /mcp plus health endpoints, and supports MCP_ALLOWED_HOSTS for production
host-header allowlisting. For ChatGPT Apps domain verification, set
OPENAI_APPS_CHALLENGE_TOKEN so HTTP mode serves the raw token from
/.well-known/openai-apps-challenge.
| Tool | Description |
|---|---|
renderChart | Render a Semiotic chart to static SVG. Supports the components returned by getSchema that are marked [renderable]. Pass { component: "LineChart", props: { data: [...], xAccessor: "x", yAccessor: "y" } }. Returns SVG string plus a "Render evidence" JSON block (mark counts by scene type, resolved axis domains, empty flag, annotation count, accessible name) so agents can verify the chart drew data marks, or validation errors with fix suggestions. |
renderInteractiveChart | Render a static-data chart as a ChatGPT Apps widget. Uses the same Semiotic server render path as renderChart, then hydrates an iframe UI with fit, zoom, data, hover, and render-evidence controls. |
getSchema | Return the prop schema for a specific component. Pass { component: "LineChart" } to get its props, or omit component to list the complete schema-backed catalog. Components marked [renderable] are available through renderChart; realtime charts require a browser/live environment. |
suggestChart | Sample-row recommender. Pass { data: [{...}, ...] } with 1–5 sample objects plus optional broad intent/capability filters. |
suggestCharts | Capability-based recommender for bounded row data. Returns ranked chart suggestions with scores, reasons, caveats, import paths, and ready-to-use props. |
suggestStreamCharts | Recommend streaming charts from a schema (shape, key, throughput). Returns { suggestions, excluded, stretchSuggestions }. |
suggestStreamDashboard | Compose a multi-panel dashboard from stream schemas and an audience layout policy. |
suggestDashboard | Build a multi-panel dashboard suggestion that covers distinct analytical intents. |
suggestStretchCharts | Recommend audience-literacy stretch picks from an AudienceProfile. |
repairChartConfig | Check whether a requested chart fits a dataset and return ranked alternatives when it does not. |
interrogateChart | Return a statistical summary and chart-aware context for answering natural-language questions with optional annotations. |
diagnoseConfig | Check a chart configuration for common problems — empty data, bad dimensions, missing accessors, wrong data shape, and more. Returns a human-readable diagnostic report with actionable fixes. |
reportIssue | Generate a pre-filled GitHub issue URL for bug reports or feature requests. Pass { title: "...", body: "...", labels: ["bug"] }. Returns a URL the user can open to submit. |
applyTheme | List named theme presets or return ThemeProvider/CSS/token usage for a preset such as { name: "tufte" }. |
auditArtifact | Evaluate an explicit artifact contract under a named policy without inferring missing source, review, or timing facts. |
recommendRepresentation | Compare chart and non-chart outcomes against supplied data, claims, evidence, time state, and policy requirements. |
repairArtifact | Propose contract repairs and optionally fill missing identity fields; existing mismatches require a revision and claim reassessment. |
explainRefusal | Explain which explicit policy obligations prevent publication or action and identify safe follow-up work. |
Artifact tools are opt-in diagnostics, not publication authorization. A
conditional result (including CLI exit code 0) retains open work. Ordinary
renderChart calls do not enforce artifact policy; governed hosts must enforce
their own release boundary. Manual-check resolution and revision-bound release
approval remain separate host workflow work.
| Resource | Description |
|---|---|
semiotic://artifact-contract-schema | Standalone JSON Schema for portable purpose, claim, evidence, timing, review, and transfer metadata. |
semiotic://schema | Full machine-readable component schema JSON. |
semiotic://schema-index | Compact discovery index with categories, renderability, import paths, and each component's schema resource URI. |
semiotic://schema/{component} | One component's schema, metadata, accessibility guidance, and behavior contracts (for example semiotic://schema/LineChart). |
semiotic://components | Backward-compatible component index showing renderable/browser-only status and MCP categories. |
semiotic://surface-manifest | Generated inventory of the current AI schema, exports, renderability, tools, resources, and prompts. |
semiotic://behavior-contracts | Agent-visible semantic rules for color precedence, required prop combinations, push refs, and renderability. |
semiotic://system-prompt | Compact AI instructions with import rules, chart props, SSR guidance, and pitfalls. |
semiotic://examples | Copy-paste chart examples by data shape. |
ui://semiotic/chart-widget.html | ChatGPT Apps / MCP Apps widget template used by renderInteractiveChart. |
| Prompt | Description |
|---|---|
build-semiotic-chart | Reusable workflow for choosing a chart, reading schema, diagnosing props, and rendering a preview. |
debug-semiotic-chart | Reusable workflow for debugging invalid props, rendering failures, and issue reports. |
Tool: getSchema
Args: { "component": "LineChart" }
→ Returns: { "name": "LineChart", "description": "...", "parameters": { "properties": { "data": ..., "xAccessor": ..., ... } } }
Tool: suggestChart
Args: {
"data": [
{ "month": "Jan", "revenue": 120, "region": "East" },
{ "month": "Feb", "revenue": 180, "region": "West" }
]
}
→ Returns:
1. BarChart (high confidence) — categorical field (region) with values (revenue)
2. StackedBarChart (medium confidence) — two categorical fields (month, region)
3. DonutChart (medium confidence) — 2 categories — proportional composition
Tool: renderChart
Args: {
"component": "BarChart",
"props": {
"data": [
{ "category": "Q1", "revenue": 120 },
{ "category": "Q2", "revenue": 180 },
{ "category": "Q3", "revenue": 150 }
],
"categoryAccessor": "category",
"valueAccessor": "revenue"
}
}
→ Returns: <svg>...</svg>
Tool: renderInteractiveChart
Args: {
"component": "BarChart",
"props": {
"title": "Revenue by Quarter",
"data": [
{ "quarter": "Q1", "revenue": 120 },
{ "quarter": "Q2", "revenue": 180 }
],
"categoryAccessor": "quarter",
"valueAccessor": "revenue"
}
}
→ Returns: structured chart summary for the model + hidden SVG/widget metadata for ChatGPT.
Tool: diagnoseConfig
Args: { "component": "LineChart", "props": { "data": [] } }
→ Returns: ✗ [EMPTY_DATA] data is an empty array — Fix: provide at least one data point
Tool: reportIssue
Args: {
"title": "Bug: BarChart tooltip shows undefined for custom accessor",
"body": "When using valueAccessor='amount', tooltip displays 'undefined'.\n\ndiagnoseConfig output: ✓ no issues detected.",
"labels": ["bug"]
}
→ Returns: Open this URL to submit the issue: https://github.com/nteract/semiotic/issues/new?...
For quick validation without an MCP client:
npx -p semiotic semiotic-ai --list # list components with import paths and renderability
npx -p semiotic semiotic-ai --list --json # machine-readable component index
npx -p semiotic semiotic-ai --schema GaugeChart
npx -p semiotic semiotic-ai --suggest '{"data":[{"category":"A","value":10}],"intent":"comparison"}'
npx -p semiotic semiotic-ai --doctor # validate component + props JSON
npx -p semiotic semiotic-ai --schema # dump all chart schemas
npx -p semiotic semiotic-ai --compact # compact schema (fewer tokens)
--doctor uses the full diagnoseConfig checks when dist is available and falls back to schema-only validation in clean source checkouts.
Semiotic is indexed by AI-coding-agent documentation tools so your assistant (Claude Code, Cursor, Cline, Copilot, etc.) can pull current docs and tools without copy-paste:
context7.json)The Official MCP Registry is the canonical MCP directory record; it is distinct from acceptance into any assistant vendor's curated connector directory. Secondary-directory freshness and release ownership are tracked in MCP_DISTRIBUTION.md.
Agent-facing API surface:
AGENTS.md is the concise repository development contract and CLAUDE.md imports it for Claude Code. These stay repository-local rather than shipping irrelevant contributor instructions to package consumers.ai/reference.md, ai/schema.json, ai/surface-manifest.json, ai/behaviorContracts.cjs, and agent-skill/semiotic-charts/SKILL.md are bundled in the npm tarball (see package.json#files). The reference is the on-demand product guide printed by npx -p semiotic semiotic-ai; the schema, manifest, contracts, and portable skill provide structured generation and validation guidance.semiotic.nteract.io/llms.txt + /llms-full.txt — deployed at the docs site per the llms.txt standard. Agents fetch the navigation map (llms.txt) or the full inlined docs (llms-full.txt) over HTTP; they're not part of the npm package itself.See CONTRIBUTING.md. Our community follows the nteract Code of Conduct.
Development of this library owes a lot to Susie Lu, Jason Reid, James Womack, Matt Herman, Shelby Sturgis, and Tristan Reid.
The Sankey layout engine is based on sankey-plus
by Tom Shanley, which improved on his earlier
d3-sankey-circular with better cycle detection, hierarchical arc stacking,
and dynamic extent adjustment.
Semiotic icon based on an icon by Andre Schauer.
Overall
Maintained
Code review
npm
Latest release
Releases this year
268 followers · starred Jan 2018
503 followers · starred Sep 2017
554 followers · starred Jul 2018
198 followers · starred Jul 2026