Share a Vega-Lite chart
Vega-Lite's whole argument is that a chart is a specification, not a picture — which is why the charts it produces have tooltips, panning, brushing, and selections that filter one view from another.
Then it comes time to show someone, and the chart becomes a PNG in a Slack message. A picture of a tool.
Export HTML, not an image
# Altair (Python) — the usual front-end for Vega-Lite
chart.save("chart.html", inline=True)
The vega-lite CLI's vl2vg | vg2svg path is the one to avoid here — it
renders the spec down to a static SVG, which throws away exactly the
behaviour you wanted to share.
The inline=True matters. Without it the file references the Vega
libraries from a CDN, and the chart breaks the day the pinned version
moves or the viewer is on a locked-down network. With it, you get one
self-contained file — which is also the shape that publishes in a single
call.
If you're hand-writing the page, the same rule applies to your
vega-embed script tags: vendor them rather than linking a CDN.
Publish it
Drag chart.html into Comma, or from a
script:
curl -fsS -X POST "https://commareports.com/api/v1/reports" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html chart.html \
--arg title "Retention by cohort" \
'{title: $title, html: $html}')"
Scripts run inside a sandboxed iframe (allow-scripts, no
allow-same-origin), so vega-embed boots and the chart stays a chart:
tooltips on hover, brush to filter, linked selection across views, and the
"..." menu for exporting the spec. See
interactive HTML reports.
Inline your data
This is the one thing that reliably breaks. A spec whose data is a URL
needs that URL to be publicly reachable and to send permissive CORS
headers, because the sandbox has no same-origin access to your network. A
chart pointing at http://localhost:8000/data.csv or an internal S3 path
renders as an empty axis for everyone but you.
Inline the data into the spec for anything other people need to open. That also makes the report a self-contained record of what the numbers were on the day you published it, which is usually what you wanted.
Comment on the mark
The reason to publish an interactive chart rather than an image is that the conversation can happen on the chart. Select the outlier, leave a thread — "this spike is the migration backfill, not real traffic" — and it stays anchored there across revisions, so the next person who sees the spike finds the explanation instead of asking again.
That note is worth more than the chart, and it is the thing a PNG in a channel structurally cannot hold. See commenting on HTML.
Keeping it current
If the chart is generated by a script, PATCH a saved report id instead
of POSTing a new one. Each run appends a revision at the same URL, so
the link in the doc stays live and any two revisions can be diffed. On a
schedule rather than on a push, use a
routine.
Limits
- Entry HTML: 5 MB. Inlining a large dataset is what pushes past this — aggregate before plotting, which usually makes the chart better too. Assets: 25 MB per file, 250 MB and 500 files total.
- Scripts run, sandboxed — no same-origin access, so no fetches into your private network.
- 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.
Related
- Share an Altair chart — the Python path in detail
- Share a Plotly HTML chart · Share a Bokeh plot
- Interactive HTML reports · Share a Folium map
- Share a Jupyter notebook · with Jupyter