Spec-driven Vega-Lite renderer: Vega-Lite JSON in, backend-neutral scenegraph and SVG out.
let spec : String =
#|{
#| "data": {"values": [{"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43}]},
#| "mark": "bar",
#| "encoding": {
#| "x": {"field": "a", "type": "nominal"},
#| "y": {"field": "b", "type": "quantitative"}
#| }
#|}
fn main {
let svg = @moon_vegalite.render_svg(spec) catch {
e => {
println("error: " + e.message()) // names the JSON path of an unsupported feature
return
}
}
println(svg)
}git clone https://github.com/sayoi7799/moon_vegalite
cd moon_vegalite/examples/hello
moon run cmd > bar.svg # open bar.svg in a browsercd moon_vegalite
moon run cmd/vl2svg --target native -- examples/bar_basic.vl.json bar.svg # SVG file (stdout without the second argument)
moon run cmd/vl2svg --target native -- --scene examples/bar_basic.vl.json # scenegraph JSON, with the bounds of every itemlet svg = @moon_vegalite.render_svg(spec_json) // SVG document
let c = @moon_vegalite.compile(spec_json) // scenegraph + view metadata
let scene = @moon_vegalite.scene_json(c) // the scenegraph as JSON// moon.mod
import {
"sayoi7799/moon_vegalite@0.2.0",
}// moon.work, next to your moon.mod
members = [
".",
"../moon_vegalite", // the checkout
]// your package's moon.pkg
import {
"sayoi7799/moon_vegalite",
}let svg = @moon_vegalite.render_svg_sketch(
spec_json,
options=@moon_vegalite.SketchOptions::new(seed=42, roughness=1.5, fill_style=CrossHatch),
)vl2svg --sketch --seed 42 --roughness 1.5 spec.vl.json chart.svg # also --decimals 2 for a smaller file| Scenegraph shape | Drawn by rough as |
|---|---|
| rect (bars, cells, legend swatches) and the frame of a plot | rectangle |
| rule (axis lines, ticks, grid lines, rule marks) | line |
| line, area, symbol (points, circles, squares, ...) | path, with the transform of the symbol kept |
| text | not touched |
| gradient fills (continuous colour legends), the page background | not touched |
moon build --target js --release
node scripts/build_site.mjs && node scripts/serve_site.mjs # http://localhost:8080/
| Chart | moon_vegalite | official vega | hand-drawn |
|---|---|---|---|
| stacked_bar_v | |||
| point_color | |||
| layer_bar_line | |||
| layer_histogram | |||
| layer_color_legend_left | |||
| layer_circle_independent_color |
| Area | Supported |
|---|---|
| Data | inline data.values. data.url and datasets are not read, data.format.parse is rejected: embed the values |
| Marks | bar, line, point, circle, square, area, rule, tick, rect, text, arc |
| Mark properties | color, filled, opacity, interpolate (linear, step, step-before, step-after, monotone); size for rule and line (the stroke width); rule: strokeDash; text: align, baseline, dx, dy, fontSize, text, x, y ("width" / "height" or pixels); arc: innerRadius, outerRadius, padAngle, cornerRadius, stroke, strokeWidth |
| Position channels | x, y (a field, or a numeric datum), x2, y2 (on rule, bar, rect: ranged bars and rects, rules across the plot), theta (on arc) |
| Other channels | color (nominal fields with the category scheme, ordinal fields with a blues ramp sampled per value, quantitative fields with a blues ramp and a gradient legend, constant values), size, opacity (also constant), shape (nominal), text (with format); aggregate: count without a field works on size, colour, opacity and shape |
| Field types | quantitative, ordinal, nominal, temporal |
| Aggregates | count, sum, mean, average, min, max, median, distinct, … (the vega aggregate ops) |
| Transforms | filter (expression or predicate), calculate (expression subset), aggregate, bin, timeUnit, fold; field-level bin, timeUnit, aggregate |
| Time units | year, quarter, month, week, date, day, hours, minutes, seconds, milliseconds and combinations (UTC; "local" time is UTC) |
| Scales | linear, log, sqrt, pow, time, utc, band, point, ordinal with domain, domainMin, domainMax, range, zero, nice, reverse, clamp, base, exponent (position channels); the angle scale of arc takes range |
| Stacking | default stacking for bar and area (stacked areas are imputed, as vega-lite does); stack: zero / center / normalize / null; arc stacks theta around the circle (zero, normalize) |
| Sorting | sort on discrete x / y: ascending, descending, a value list, -x / -y, {op, field, order} |
| Axes | grid, ticks, labels with flush / overlap removal, title; the properties domain, grid, labels, ticks, format, labelAngle, labelFlush, labelOverlap, tickMinStep, titlePadding, offset, position and the colour / width / size family; axis: null |
| Legends | symbol legends (size, shape, opacity, colour, merged when the channels share a field), gradient legend for quantitative colour (the default blues ramp); legend.orient: left or right; legend: null |
| Layers | layer with nested layers and inherited data / transform / encoding; position scales shared by default (domains merged, a band scale wins over a point scale) or independent with resolve.scale.x / y (each with its own axis, the second on the opposite side); colour scales and legends shared, or independent with resolve.scale.color; one size scale with independent legends (resolve.legend.size); layers that bin the same field identically share one bin and one binned scale; axis and legend titles merge as in vega-lite |
| Arcs | mark: arc with theta and color: pie and donut charts, in a 200 × 200 view without a frame |
| Layout | one view or layer; width / height as numbers or {step}; padding; background; a single-string title |
| Hand-drawn style | render_svg_sketch / vl2svg --sketch: rects, rules, lines, areas, symbols, arcs and plot frames drawn by moon_rough, text unchanged (see above) |
| Config | config.scale.zero only; every other config key is rejected with its path |
| Accepted, no effect on the picture | description, name, usermeta |
| Layer | Reference | Where |
|---|---|---|
| number formatting, ticks, nice, time formats, scales | d3 / vega-format / vega-scale as bundled in vega 5.33.1 | fixtures/{numfmt,ticks,formatcases,timecases,scalecases} |
| expressions, data transforms | vega-expression, vega-transforms | fixtures/{exprcases,transformcases} |
| scenegraph bounds, SVG | vega-scenegraph | fixtures/scenecases |
| end to end: 80 official Vega-Lite examples (15 of them layer_*, 2 of them arc_*), edge cases, MoonFrame specs | vega-lite 5.23.0 compile + vega scenegraph and SVG | fixtures/vlexamples |
cd scripts
npm ci
npm run fixtures # downloads the example specs of the vega-lite tag, regenerates ../fixtures
npm run gallery # regenerates ../gallery (needs: moon build --target native cmd/vl2svg)| Scenario | native | js | wasm-gc | Node 24 + vega-lite 5.23.0 + vega 5.33.1 |
|---|---|---|---|---|
| 10,000 rows, mean per category, bars: to SVG | 21.5 ± 0.4 ms | 9.5 ± 0.2 ms | 8.6 ± 0.7 ms | 5.9 ± 0.8 ms |
| 10,000 rows, mean per category, bars: to scenegraph | 21.4 ± 0.6 ms | 9.6 ± 0.4 ms | 7.9 ± 0.2 ms | 4.5 ± 0.3 ms |
| 100,000-point scatter plot: to SVG | 1.68 ± 0.03 s | 0.83 ± 0.03 s | 0.68 ± 0.04 s | 0.88 ± 0.04 s |
| 100,000-point scatter plot: to scenegraph | 0.66 ± 0.02 s | 0.32 ± 0.03 s | 0.28 ± 0.03 s | 0.17 ± 0.01 s |
moon bench --release --target js -p sayoi7799/moon_vegalite/benchmarks # also wasm-gc and native
moon build --target native cmd/vl2svg # the identical-SVG check runs this binary
cd scripts && npm ci && npm run bench # the vega side, with the identical-SVG check| Package | Content |
|---|---|
| util, stats, format, time, value | JavaScript-compatible numbers (V8 libm), d3 ticks, d3-format, time intervals / formats, dynamic values |
| scale | continuous, band, point, ordinal and sequential scales, colour schemes |
| expr, transform | the expression subset, aggregate / bin / stack / timeunit / fold / filter / formula |
| scene, svg | scenegraph, mark bounds, path generation, SVG renderer |
| spec | strict reader of Vega-Lite JSON (unsupported features raise errors with paths) |
| vl | the compiler: data pipeline, scales, axes, legends, layout |
| sketch | the hand-drawn style: the shapes of the scenegraph drawn by moon_rough |
| web | the entry point of the demo page (js): render, render_sketch |
| benchmarks | moon bench for the performance table (not part of the library) |
| cmd/vl2svg | command line tool (native) |
pub suberror RenderError {
RenderError(String)
}fn render_svg_sketch(spec : String, options? : SketchOptions, scale? : Double) -> String raise RenderErrorfn svg_sketch_of(c : Compiled, options? : SketchOptions, scale? : Double) -> String raise RenderErrorInstall
Download zipSpec-driven Vega-Lite renderer: Vega-Lite JSON in, backend-neutral scenegraph and SVG out.