A chart that an AI can pick correctly, that a screen-reader user can actually receive, and that carries its own provenance is worth more than one that merely looks right. Those three properties aren't pixels — they're metadata. And metadata is portable in a way a rendering engine never is. The IDID portability spec writes three of them down as plain JSON Schemas, so the ideas can travel into tools that have never heard of Semiotic.
Why this matters
Most chart "interoperability" stops at appearance: a converter reads one format's marks and emits another's. That's a parser, and a parser only ever reproduces what the source already had. The more interesting question is what the source didn't have. A Vega-Lite spec doesn't know which intents its chart serves, who's going to read it, or where a callout came from. Neither does a Mermaid diagram or a notebook plot. Those gaps are exactly where a non-expert reader and an LLM both get misled — and exactly the metadata that makes a chart legible to an agent and receivable by a real audience.
So the framing shifts. An adapter shouldn't just reproduce a format; it should be anexport mechanism for the ideas the format is missing. For that to work, the ideas have to exist as something library-neutral — not a TypeScript interface buried in one renderer, but a schema any tool can implement. That's what the spec is: three versioned JSON Schemas, each implementable without reading a line of Semiotic source.
Three primitives
Chart capability answers what is this chart good at? — declaratively, with a familiarity/accuracy/precision rubric and per-intent scores, so a heuristic or an LLM can rank a chart against a dataset and a goal without executing a chart library. Audience profile answerswho is reading, and what is the org trying to grow? — per-chart familiarity, adoption targets, and a reception modality (a screen reader can't receive an eight-slice pie no matter how familiar it is). Annotation provenance & lifecycleanswerswhere did this note come from, how much do we trust it, and how does it age? — author, basis, confidence, and a two-axis lifecycle that keeps a fresh-but-disputed note distinct from a stale-but-accepted one.
Public domain fields are labelled with x-idid-status so a reader can tell what's real today from what the spec reserves for later. Open string unions (an annotation'ssource) stay open with a recognized-values list; genuinely closed unions (freshness, status, anchor) use a strict enum. The schemas are the stable artifact; they ship in the npm package under /spec/v0.1and live in the repository at the same path.
Bidirectional Vega-Lite — the proof
Vega-Lite is the closest thing the ecosystem has to a neutral chart interchange format. Semiotic already reads it (fromVegaLite); the spec adds the inverse (toVegaLiteResult), so the tested supported single-view subset can round-trip through the dominant format. Unsupported semantics refuse with structured diagnostics. Here's a chart built from a Vega-Lite spec — and it converts straight back to one:
This chart was built from a Vega-Lite bar spec via fromVegaLite(). RuntoVegaLiteResult() on the result and you get the spec back for the tested supported single-view subset; unsupported semantics return a typed refusal.
import { fromVegaLite } from "semiotic/data" import { unstable_toVegaLiteResult as toVegaLiteResult } from "semiotic/experimental" const config = fromVegaLite(vegaSpec) // Vega-Lite -> Semiotic ChartConfig const result = toVegaLiteResult(config) // ChartConfig -> Vega-Lite if (result.status === "refused") throw new Error(result.diagnostics[0].message) const back = result.spec // supported subsetCarrying the metadata on the spec
The tested supported round trip preserves its mark, data, and encoding subset — but plain Vega-Lite has no place for capability, audience, or provenance. The binding rides them under usermeta.idid, a key every Vega-Lite renderer ignores, so the spec and its meaning travel together. Attach a capability, an audience, and a provenanced annotation, and the metadata appears alongside the chart:
import { unstable_toVegaLiteResult as toVegaLiteResult, unstable_attachIDID as attachIDID, unstable_attachIDIDAnnotations as attachIDIDAnnotations, } from "semiotic/experimental" const result = toVegaLiteResult(config) if (result.status === "refused") throw new Error(result.diagnostics[0].message) let spec = result.spec spec = attachIDID(spec, { capability, audience }) // ride under usermeta.idid spec = attachIDIDAnnotations(spec, [provenancedNote]) // + a note with its evidenceThe enriched spec's metadata block, computed live:
{ "usermeta": { "idid": { "specVersion": "0.1", "capability": { "component": "BarChart", "family": "ordinal", "importPath": "semiotic/ordinal", "rubric": { "familiarity": 5, "accuracy": 5, "precision": 4 }, "intentScores": { "compare-categories": 5, "rank": 4 } }, "audience": { "name": "Quarterly exec review", "targets": { "PieChart": { "direction": "decrease", "weight": 2, "reason": "exact comparison, not slices" } }, "receptionModality": "visual" }, "annotations": [ { "type": "y-threshold", "value": 100, "label": "Plan target", "provenance": { "authorKind": "watcher", "source": "ai", "basis": "statistical-test", "confidence": 0.72, "createdAt": "2026-06-20T14:00:00Z" }, "lifecycle": { "ttlHint": "P7D", "status": "proposed", "anchor": "semantic" } } ] } } }A plain Vega-Lite renderer draws the chart and ignores all of it. An IDID-aware host reads it back — and acts on it. Because the audience profile travels on the spec, any tool can feed it straight into a suggestion engine: the same audience that shipped with the chart now calibrates what else to recommend. The metadata is actionable, not decorative.
When to reach for it
Reach for the spec when you're building an adapter — Vega-Lite, Mermaid, a dbt test, a notebook export — and you want it to carry more than appearance. Reach for it when chart metadata has to cross a tool boundary: a Python service that emits capability descriptors a React frontend consumes, a BI portal that stamps annotations with provenance another system reads. Validate against the published JSON Schemas with any compliant validator — no library dependency required, which is the whole point.
Don't reach for it as a chart-rendering format; it isn't one. It describes metadataabout charts, not their geometry — the geometry stays in Vega-Lite, in aChartConfig, or in your renderer of choice. And don't expect a 100% format translation: an adapter that can't faithfully represent a source construct should refuse with a reason, not emit a plausible-but-wrong chart. A 70%-faithful adapter that announces its 30% gap is an asset; a 95%-faithful one that hides its 5% is a liability.
Where this goes
The same shape recurs anywhere chart metadata outlives the tool that made it. A model emits a chart spec that a downstream agent has to interpret faithfully. A design system needs charts whose audience calibration survives being handed to a different team. A research figure travels into a paper stripped of its context, and a provenanced caveat is what survives the trip. In each case the durable value isn't the picture — it's the metadata riding alongside it. The leading sign this is working won't be downloads; it'll be someone implementing these schemas in a stack that isn't Semiotic at all.