ChartSpec 0.1Portable schema
Function-free ChartSpec 0.1 keeps charts portable and serializable.
urn:graflume:schema:0.1 · function-free JSONGraflume · Alpha
Build your first chart with the exact verified snapshot. Learn the data, ChartSpec, Scene, and lifecycle contracts.
Quick start
Build your first chart with the exact verified snapshot.
exact commit · SRI · CDN
row[] / TypedArray columns
create() · resize() · destroy()
<script defer
src="https://cdn.jsdelivr.net/gh/statground/graflume@4affa24255ec2145ef9983a7f0aea586d5189926/cdn/graflume.global.js"
integrity="sha384-5RM5TBHV62iDnvZKAHQFcMiZsT2ASkHua9qBVG6rwNuPIa3qABTKT3us0S26nITS"
crossorigin="anonymous"></script>
<div id="chart" style="height: 360px"></div>
<script>
const rows = [
{ month: 'Jan', sales: 42 },
{ month: 'Feb', sales: 51 },
{ month: 'Mar', sales: 49 }
];
const chart = Graflume.line('#chart', rows, {
x: { field: 'month', type: 'ordinal' },
y: { field: 'sales', type: 'quantitative' },
theme: 'graflume-light',
mark: { point: true }
});
</script>
npm · Not published · UNLICENSED
Manual
Learn the data, ChartSpec, Scene, and lifecycle contracts.
Choose a chart type and theme, then render again to see the same row data expressed by another specification.
Select a chart mark and inspect the status area to see which row its safe data event identifies.
Compare the table with the generated ChartSpec, export a PNG, and destroy the instance when leaving the page.
ChartSpec 0.1Function-free ChartSpec 0.1 keeps charts portable and serializable.
urn:graflume:schema:0.1 · function-free JSONDataTableRow data and zero-copy TypedArray columns share one data contract.
row objects · zero-copy TypedArray columnsstandard · large · ultraStandard, large, and ultra profiles bound rendering work.
bounded line · point · bar rendering · responsive resizevalidate()Functions, unsafe keys, raw HTML, and runtime evaluation are rejected.
functions · unsafe keys · raw HTML · runtime evaluationChartSpec · current runtime
Current executable foundations are ordered by the embedded Graflume catalog. Every statement below describes an enforced schema or runtime boundary.
transform-dataflow
supported
Ordered function-free transforms, top-level and layer pipelines, deterministic row lineage, parameters, seeded operations, aggregation counts, named sources, reusable branches and a shared transform DAG are supported.
transform[]compile().dataLineageScene.metadata.dataLineageCurrent boundary: The portable transform registry is closed and function-free: arbitrary executable transforms, host side effects and opaque callbacks are rejected.
encoding-scale-registry
supported
A closed portable encoding map, conditional mark channels, geographic and trading channels, 17 positional/categorical/probability/temporal scale types, four color scale types, and shared or independent multi-view domains, axes, legends and colorbars are supported.
encodingcreatePositionScale()createColorScale()Scale.map()Scale.invert()Scale.ticks()Current boundary: Channels and scales use a closed registered vocabulary; arbitrary executable accessors and unregistered mark/channel contracts fail validation.
analytic-interaction
supported
A closed immutable v1 store, union/intersection semantics, point/interval/rectangle/axis/lasso state, Canvas pointer/touch and keyboard-authored geometry, continuous and categorical pixel/domain navigation, linked multi-view state, and lineage-preserving selection-driven filtering are supported.
interaction.selectioninteraction.domainNavigationgetAnalyticSelection()applyAnalyticSelection()domainToPixel()pixelToDomain()zoomDomainBy()panDomainBy()Current boundary: Spatial analytic selection remains a separate renderer boundary; selection state and filtering stay inside the closed portable interaction contract.
composition-resolve
supported
Legacy flat layers and the closed layer/facet/repeat/hconcat/vconcat/concat/inset Canvas composition grammar are supported with deterministic bounded layout, scoped semantic and lineage identities, named axes, shared or independent multi-view domains/axes/legends/colorbars, linked view state, per-layer transforms, z-order and clipping.
layerfacetrepeathconcatvconcatconcatinsetresolveCurrent boundary: Facet virtualization, renderer mixing and Spatial composition remain separate boundaries; composition stays deterministic and bounded.
semantic-index-accessibility
supported
All Canvas families compile a bounded lineage-aware semantic index and expose a virtualized native explorer with full roving traversal, focus-ring, tooltip and selection synchronization. Spatial GPU views expose projected roving traversal and active-descendant synchronization, while a bounded stable-key focus store links Canvas and Spatial views through a portable authored contract.
getSemanticIndex()toAccessibleRows()getAccessibilityState()accessibility.tableaccessibility.navigationCurrent boundary: Accessibility explorers and focus stores are bounded, authored semantic projections rather than an unbounded DOM mirror of every rendered primitive.
label-layout-authoring
supported
All 41 Canvas families support function-free reusable markLabels with stable-key portable positions, deterministic automatic collision placement, connectors, editing handles, pointer drag, keyboard selection and nudging, grid/mark/plot snapping, bounded undo/redo, runtime import/export events and accessible instructions/live status. Annotation adds a closed data-coordinate point/rule/band/label/callout registry with drag, resize and history.
markLabelsgetMarkLabelState()getMarkLabelPositions()setMarkLabelPositions()undoMarkLabelEdit()redoMarkLabelEdit()marklabelchangegetAnnotations()setAnnotations()addAnnotation()removeAnnotation()annotationchangeCurrent boundary: Authoring uses closed markLabels and data-coordinate annotation primitives; raw HTML, arbitrary event code, freehand drawing and image authoring are excluded.
incremental-worker-binary
supported
Stable-key append/upsert/replaceLast uses bounded ring storage, explicit reject/drop-oldest/coalesce queues, count/event-time retention and replay. Row-local transforms recompute changed keys with exact global fallback; the frame runtime adds RAF coalescing, cancellation, pause/follow-live and lazy bounded history. Automatic module Workers accept row, typed-column and Arrow-compatible binary envelopes, injected JavaScript/WASM adapters and worker-owned renderers with bounded work and cancellation.
streamingappendData()updateData()upsertData()replaceLastData()getStreamingState()exportStreamingReplay()replayData()createIncrementalDataStore()createTransformWorkerAdapter()installTransformWorker()Current boundary: Queues, histories and worker work remain explicitly bounded; JavaScript/WASM adapters must be installed by the host and cannot introduce opaque unbounded execution.
ChartSpec · axes
Configure axis placement, value formatting, label direction, typography, ticks, grids, and spacing with a function-free ChartSpec.
format.type · dateStyle · timeStyle · timeZonenumber · compact · currency · percent · date · time · datetime
labels.orientation · fontauto · horizontal · vertical-up · vertical-down · angle · maxLength
ticks · grid · offsetcount · values · spacing · padding · dash · opacity
x · y · x2 · y2 · axisIdaxes: {
x: {
position: 'bottom',
format: { type: 'datetime', dateStyle: 'medium', timeStyle: 'short', timeZone: 'Asia/Seoul' },
labels: {
orientation: 'vertical-up', padding: 10,
font: { family: 'Inter', size: 11, weight: 'semibold' }
},
ticks: { count: 6, spacing: 48 }
},
y: { grid: { visible: true, dash: [4, 4], opacity: 0.7 } }
}
ChartSpec · SpatialSpec · storytelling
Identify series, emphasize a datum or range, place a custom callout, and let readers select marks.
legend: { mode: 'categories', field: 'series', position: 'bottom' },
highlights: [
{ id: 'focus', target: { type: 'datum', field: 'city', value: 'Seoul' } },
{ id: 'quarter', target: { type: 'range', x: { from: 'Apr', to: 'Jun' } } }
],
annotations: [{
id: 'note',
target: { type: 'datum', rowIndex: 3 },
text: 'Campaign started', detail: 'User-authored plain text',
placement: 'auto', style: { maxWidth: 220 }
}],
interaction: {
controls: { annotations: true },
playback: {
field: 'period', key: 'period', mode: 'cumulative',
transition: { duration: 600, easing: 'ease-in-out' }
},
selection: {
mode: 'multiple', toggle: true,
clearOnBackground: true, clearOnEscape: true
}
}
Canvas target types: datum · layer · range · normalized plot. Spatial target types: picked datum · layer · world point · world box. Spatial category and continuous legends are display-only; layer legends can toggle a layer.
Auto placement compares perimeter candidates and avoids the plot, legend, and other callouts when possible. Explicit placement is preserved with only necessary safety correction. Callout text wraps safely inside the bubble. Long unbroken tokens are visually shortened within the line budget, while the complete source text remains available to assistive technology. Playback smoothly interpolates matching stable keys for 600 ms with ease-in-out. Entering or exiting marks fade, incompatible shapes crossfade, and reduced-motion jumps directly to the final state.
Spatial callouts that follow the camera are DOM overlays and are not included in the current PNG framebuffer export.
Browser API
Choose a chart type and theme, then render again to see the same row data expressed by another specification.
const chart = Graflume.create('#chart', {
specVersion: '0.1', data,
mark: { type: 'line', point: true },
x: { field: 'period', type: 'ordinal' },
y: { field: 'actual', type: 'quantitative' },
interaction: { hover: true, click: true }
});
chart.on('click', ({ hit }) => console.log(hit?.datum));
chart.setData(nextRows);
chart.appendData(nextBatch); // bounded streaming applies when spec.streaming is configured
chart.resize();
chart.toDataURL('image/png');
chart.destroy();Graflume.compile(spec)DOM-free validation · normalize · Scene
bar() · line() · combo()Chart family · Quick API
setSpec(spec)Choose a chart type and theme, then render again to see the same row data expressed by another specification.
setData(rows)Row data
appendData(rows) · upsertData(rows) · replaceLastData(rows)legacy append · explicit streaming retention / replay
resize()Responsive
on('click')Select a chart mark and inspect the status area to see which row its safe data event identifies.
toDataURL()Compare the table with the generated ChartSpec, export a PNG, and destroy the instance when leaving the page.
destroy()Integration checklist
Browser API
Portable specification fields and browser methods are shown together so that declarative and runtime contracts remain distinguishable.
transform-dataflow
supported
Ordered function-free transforms, top-level and layer pipelines, deterministic row lineage, parameters, seeded operations, aggregation counts, named sources, reusable branches and a shared transform DAG are supported.
transform[]compile().dataLineageScene.metadata.dataLineageCurrent boundary: The portable transform registry is closed and function-free: arbitrary executable transforms, host side effects and opaque callbacks are rejected.
encoding-scale-registry
supported
A closed portable encoding map, conditional mark channels, geographic and trading channels, 17 positional/categorical/probability/temporal scale types, four color scale types, and shared or independent multi-view domains, axes, legends and colorbars are supported.
encodingcreatePositionScale()createColorScale()Scale.map()Scale.invert()Scale.ticks()Current boundary: Channels and scales use a closed registered vocabulary; arbitrary executable accessors and unregistered mark/channel contracts fail validation.
analytic-interaction
supported
A closed immutable v1 store, union/intersection semantics, point/interval/rectangle/axis/lasso state, Canvas pointer/touch and keyboard-authored geometry, continuous and categorical pixel/domain navigation, linked multi-view state, and lineage-preserving selection-driven filtering are supported.
interaction.selectioninteraction.domainNavigationgetAnalyticSelection()applyAnalyticSelection()domainToPixel()pixelToDomain()zoomDomainBy()panDomainBy()Current boundary: Spatial analytic selection remains a separate renderer boundary; selection state and filtering stay inside the closed portable interaction contract.
composition-resolve
supported
Legacy flat layers and the closed layer/facet/repeat/hconcat/vconcat/concat/inset Canvas composition grammar are supported with deterministic bounded layout, scoped semantic and lineage identities, named axes, shared or independent multi-view domains/axes/legends/colorbars, linked view state, per-layer transforms, z-order and clipping.
layerfacetrepeathconcatvconcatconcatinsetresolveCurrent boundary: Facet virtualization, renderer mixing and Spatial composition remain separate boundaries; composition stays deterministic and bounded.
semantic-index-accessibility
supported
All Canvas families compile a bounded lineage-aware semantic index and expose a virtualized native explorer with full roving traversal, focus-ring, tooltip and selection synchronization. Spatial GPU views expose projected roving traversal and active-descendant synchronization, while a bounded stable-key focus store links Canvas and Spatial views through a portable authored contract.
getSemanticIndex()toAccessibleRows()getAccessibilityState()accessibility.tableaccessibility.navigationCurrent boundary: Accessibility explorers and focus stores are bounded, authored semantic projections rather than an unbounded DOM mirror of every rendered primitive.
label-layout-authoring
supported
All 41 Canvas families support function-free reusable markLabels with stable-key portable positions, deterministic automatic collision placement, connectors, editing handles, pointer drag, keyboard selection and nudging, grid/mark/plot snapping, bounded undo/redo, runtime import/export events and accessible instructions/live status. Annotation adds a closed data-coordinate point/rule/band/label/callout registry with drag, resize and history.
markLabelsgetMarkLabelState()getMarkLabelPositions()setMarkLabelPositions()undoMarkLabelEdit()redoMarkLabelEdit()marklabelchangegetAnnotations()setAnnotations()addAnnotation()removeAnnotation()annotationchangeCurrent boundary: Authoring uses closed markLabels and data-coordinate annotation primitives; raw HTML, arbitrary event code, freehand drawing and image authoring are excluded.
incremental-worker-binary
supported
Stable-key append/upsert/replaceLast uses bounded ring storage, explicit reject/drop-oldest/coalesce queues, count/event-time retention and replay. Row-local transforms recompute changed keys with exact global fallback; the frame runtime adds RAF coalescing, cancellation, pause/follow-live and lazy bounded history. Automatic module Workers accept row, typed-column and Arrow-compatible binary envelopes, injected JavaScript/WASM adapters and worker-owned renderers with bounded work and cancellation.
streamingappendData()updateData()upsertData()replaceLastData()getStreamingState()exportStreamingReplay()replayData()createIncrementalDataStore()createTransformWorkerAdapter()installTransformWorker()Current boundary: Queues, histories and worker work remain explicitly bounded; JavaScript/WASM adapters must be installed by the host and cannot introduce opaque unbounded execution.
ChartSpec · AxisSpec
Configure axis placement, value formatting, label direction, typography, ticks, grids, and spacing with a function-free ChartSpec.
axes: {
y: {
position: 'left',
format: { type: 'currency', currency: 'KRW', notation: 'compact' }
},
y2: {
position: 'right', offset: 8, grid: false,
format: { type: 'percent', fractionDigits: 1 }
}
},
layers: [
{ mark: { type: 'bar' }, x: { field: 'period', axisId: 'x' }, y: { field: 'revenue', axisId: 'y' } },
{ mark: { type: 'line', point: true }, x: { field: 'period', axisId: 'x' }, y: { field: 'marginRate', axisId: 'y2' } }
]
Runtime · legend · annotation · selection
Identify series, emphasize a datum or range, place a custom callout, and let readers select marks.
chart.on('selectionchange', ({ state }) => {
console.log(state.items);
});
const noteId = chart.addAnnotation({
target: { type: 'datum', rowIndex: 3 },
text: 'Reviewed by the analyst',
placement: 'auto'
});
chart.updateAnnotation(noteId, { placement: 'auto' });
chart.toggleAnnotations();
const legendItem = chart.getLegendState().items.find(item => item.toggleable);
if (legendItem) chart.setLegendItemVisible(legendItem.id, false);
chart.clearSelection();