Charts
A chart is a sentence like mean value by subject sorted, run over a collection's rows as the viewer. For anything a sentence can't express, write a few lines of JavaScript, or return a Plotly figure.
Chart blocks
A chart block reads a collection’s rows (as the viewer) and shapes them in one of two ways:
- A sentence like
mean value by subject sorted. It’s parsed, never executed, so it’s safe and needs no worker. The language is described below. - A few lines of JavaScript for anything a sentence can’t express:
return count(rows, "status"). It runs in an isolated worker with only the rows and some helpers (no page, no session) and is killed after two seconds.
The block detects which one it is: text starting with a measure and without code punctuation is a sentence.
return groupBy(rows, "class").map(([label, list]) => ({ label, value: mean(list, "value") }));Sentence examples:
count grades by subject
mean value from grades by subject sorted
percent by status
# stacked, one series per status
count by month of date and by status
count by subject top 5 # the rest as "Other"
mean value by week of date rolling 4
sum (first_half + second_half) / 2 by month of date
values value against date where subject = Mathematics # a scatter
# as of a past date
mean value by subject as of 2026-06-30| Part | Forms |
|---|---|
| Measures | count percent sum mean median min max distinct values |
| Groupings | by field, by month|week|weekday|year of date, by n every 5; a second grouping after “and” makes series: by class and by subject |
| Conditions | where a = x and b >= 3 and roles has pupil |
| Endings | top n, sorted (smallest first) or sorted desc, cumulative, rolling n, as of 2026-06-30 |
Relations are shown by name. Time axes span first to last month with data, keeping empty months and bins: counts show 0, while an average over nothing is a gap in the line rather than a drop to 0. The language has its own unit tests, which run in Node without a build.
Live tables, shared charts
Editors can make a table Live from its toolbar. Live tables read rows through sync, so everyone’s changes appear in real time: what you want for a kanban board, trip planning or a sign-up sheet with several people editing. Small live tables (up to 2,000 rows) are also cached per user in IndexedDB, so reloads are instant; signing out clears the cache. Non-live tables load once when the page opens.
Charts on a page share data: each collection (or slice) is fetched once per page. Chart settings:
- Only rows where filters the first source with the filter language (
class = "class4a0000000"), so only that slice is fetched. - Live re-renders when the rows change anywhere, via sync. Off by default, since a chart that stays still is easier to read.
Try it
Type or pick a sentence to chart a year of a small shop’s orders (category, country, shipper, date, total, shipped), Northwind-style:
Charts with Plotly
For charts beyond the sentence language (heatmaps, sunbursts, box plots, maps, 3D), a JavaScript chart block can return a Plotly figure, { plotly: { data, layout } }, styled with the page’s theme and palette. Plotly ships in the binary and is only loaded when such a chart scrolls into view. Any page or island can use it via plot() from sluurp/plotly; these three islands use the same orders data: