A new charting engine for RichView
Time series that hold up under scrutiny.
Written in Rust and compiled to WebAssembly. It computes calendar-exact ticks, keeps every spike, never bridges a gap the data does not support and says so whenever one pixel stands for many observations. Then it lets you zoom, scrub and re-base without losing your place. One JSON spec drives the static SVG in a report and the live chart on this page.
Zoom · seven decades down to one season
CO₂ at Mauna Loa has been higher every year since 1959
NOAA's seasonally adjusted series climbs from 314.4 ppm in March 1958 to 429.5 ppm in August 2026, and its annual average rose in every year from 1959 to 2025. Zoom in and the monthly means breathe with the seasons, swinging by 5 to 7 ppm within each year.
Design choices
- One accent carries the story: the seasonally adjusted line. The monthly means sit behind it in a quiet ink, so the seasons stay visible without competing.
- The value axis starts where the data does and says so: its bottom edge is drawn as a gridline, not a baseline. Start at zero brings zero in with an eased transition, so you watch the scale change instead of guessing it.
- Tooltips carry NOAA's own provenance for each month: the Scripps record before May 1974, the two months NOAA interpolated, and the Maunakea months after the 2022 eruption.
- Ticks stay calendar-exact at every zoom: decades, then years, then months under a sticky year label.
Try it
- Drag across the plot to zoom; double-click to return.
- Pinch, or ⌘/Ctrl + scroll, to zoom around the pointer. Shift + drag pans.
- Drag or resize the window in the overview strip.
- Focus the chart, then ← → step month by month, + − zoom and Esc resets.
Edit this chartspec, data and style, re-rendered as you type
Compare · level, rebased and year-over-year in one click
Energy prices whipsaw while shelter costs keep climbing
Measured against a year earlier, US energy prices ranged from −19.6% (January 2015) to +41.6% (June 2022). Shelter stayed between +1.5% and +8.2% and was higher than a year before in every month shown. The gap at October 2025 is real: BLS could not collect prices during the federal shutdown and published no all-items index for that month.
Design choices
- The headline index is ink, core inflation a quiet grey, and three components take validated colours. Hover a legend entry to bring one forward.
- All three views are computed by the engine on exact calendar matches: year-over-year compares June with June, never row 12 with row 0. A missing month leaves two gaps a year apart instead of an invented slope.
- Rebased = 100 indexes every series to the month you click and keeps the published level in the tooltip.
- End labels show the latest visible value and step aside on leader lines where the lines converge. BLS carried October 2025 rents forward, which it says also affects the April 2026 rent indexes, so read shelter around those months with care.
Try it
- Switch between Year-over-year, Rebased = 100 and Published level: the lines morph rather than jump.
- In Rebased, click any month (or press Enter on it) to make it the base.
- Click a legend entry to hide a series; the axis refits smoothly.
- Open Table for every visible value without hovering.
Edit this chartspec, data and style, re-rendered as you type
Scale · 35,040 quarter-hours, four series, still fluid
When wind and solar outrun demand, German power prices turn negative
From October 2025 to September 2026 the day-ahead price was below zero in 2,052 of 35,040 quarter-hours, and in 71% of those wind and solar generation exceeded load. On 1 May 2026, a public holiday, it sat at −499.99 €/MWh, a cent above the market floor, for five quarter-hours around midday.
Design choices
- Two panes share one time axis instead of one plot with two unrelated scales: gigawatts and euros per megawatt-hour have no fixed relationship.
- The price pane's right axis is the same quantity in ct/kWh, so its labels sit on the same gridlines. The engine only allows a second axis that is a declared unit conversion.
- Wind and solar are stacked on exactly shared boundaries; the tooltip lists both, their total and load together.
- Up to six quarter-hours per pixel, M4 decimation keeps the first, last, lowest and highest value of every pixel column, which draws exactly what the full series would. Beyond that, each column shows its mean as the line and its full range as a band, and the chart says so: at full-year zoom the −499.99 €/MWh quarter-hour still sets the bottom of the band.
Try it
- Jump from 1D to All: ticks re-tier from hours to days to months as the window eases open.
- ⌘/Ctrl + scroll over a single day, then hover any quarter-hour.
- Hide Load in the legend to let the power pane refit to wind and solar.
- Watch the frame time under the chart while you zoom.
Edit this chartspec, data and style, re-rendered as you type
How it works
One spec, one engine, two outputs
- Spec + CSVA small JSON contract that rejects unknown fields and names the exact path of every mistake.
- Rust engineUTC calendar arithmetic, round value ticks, exact transforms, M4 decimation, label placement.
- SceneLaid-out geometry with stable keys for every tick, line and label.
- SVG or liveSerialised to static SVG for reports and tests, or painted and animated by a dependency-free runtime.
The whole spec for a chart
{
"data": { "url": "data/co2-mauna-loa-monthly.csv" },
"time": { "field": "month" },
"y": { "label": "CO₂ mole fraction", "unit": "ppm" },
"series": [
{ "field": "monthly_mean", "color": "ink-faint" },
{ "field": "seasonally_adjusted", "color": "series-1" }
],
"interaction": { "ranges": ["10Y", "all"], "zero_toggle": true }
}
Agents check their work without a browser:
$ richview-chart validate spec.json
{ "ok": false, "errors": [{ "path": "/series/1/field",
"message": "column 'seasonaly_adjusted' not found",
"hint": "available columns: month, monthly_mean, …" }] }
$ richview-chart render spec.json --width 960 > chart.svg
Rules the engine enforces
- Missing values and gaps longer than the data's own rhythm break the line. Nothing is interpolated.
- Areas always include zero. Lines may start elsewhere, and then the bottom edge is never drawn as a zero line.
- No independent dual axes. A second axis must be a declared conversion of the first, and its labels share the first axis's gridlines.
- Transforms match calendar dates exactly: a year earlier means the same month a year earlier.
- Tooltips show values at the precision the source published, with the source's own notes.
- Synthetic data must be marked
"demo": true, which stamps the chart Demonstration data. - Colours come from the theme in a fixed order. Larger sets retain every series and disclose reused colour slots; facet dense sets for clearer comparisons.