Publication-quality chart images from data. No browser, no Puppeteer, no native compilation.
Generate beautiful PNG charts directly from JSON data β perfect for bots, dashboards, alerts, and automated reports. Runs anywhere Node.js runs.
Most chart libraries need a browser (Puppeteer, Playwright) or native dependencies (canvas, cairo). That means 400MB+ installs, painful Docker builds, and slow cold starts.
chart-image uses Vega-Lite + Sharp with prebuilt binaries:
| chart-image | Puppeteer + Chart.js | QuickChart.io | |
|---|---|---|---|
| Install size | ~15MB | ~400MB+ | 0 (API) |
| Native deps | None | Chromium | N/A |
| Cold start | <500ms | 2-5s | Network latency |
| Offline | β | β | β |
| Fly.io/Docker | Just works | Pain | Depends on uptime |
clawhub install chart-imagegit clone https://github.com/Cluka-399/chart-image.git skills/chart-image
cd skills/chart-image/scripts && npm installnode scripts/chart.mjs \
--type line \
--data '[{"x":"Mon","y":10},{"x":"Tue","y":25},{"x":"Wed","y":18}]' \
--title "Weekly Trend" \
--dark \
--output chart.pngThat's it. One command, one PNG.
Track trends over time. The default chart type.
node scripts/chart.mjs --type line \
--data '[{"x":"Mon","y":142},{"x":"Tue","y":148},{"x":"Wed","y":145},{"x":"Thu","y":155},{"x":"Fri","y":162}]' \
--title "AAPL Weekly Price" --y-title "Price (USD)" \
--dark --show-values --output chart.pngCompare categories side by side.
node scripts/chart.mjs --type bar \
--data '[{"x":"React","y":45},{"x":"Vue","y":28},{"x":"Svelte","y":15},{"x":"Angular","y":12}]' \
--title "Framework Usage %" --output chart.pngLike line charts, but with filled regions to emphasize volume.
node scripts/chart.mjs --type area \
--data '[{"x":"Jan","y":100},{"x":"Feb","y":250},{"x":"Mar","y":180},{"x":"Apr","y":420},{"x":"May","y":380},{"x":"Jun","y":520}]' \
--title "Monthly Signups" --dark --output chart.pngShow proportions at a glance. Use --type pie for a solid circle or --type donut for the ring style.
node scripts/chart.mjs --type donut \
--data '[{"x":"Desktop","y":58},{"x":"Mobile","y":35},{"x":"Tablet","y":7}]' \
--title "Traffic by Device" --dark --output chart.pngCompare multiple trends on one chart using --series-field.
node scripts/chart.mjs --type line \
--data '[{"x":"Q1","y":30,"series":"2024"},{"x":"Q2","y":45,"series":"2024"},{"x":"Q3","y":52,"series":"2024"},{"x":"Q4","y":61,"series":"2024"},{"x":"Q1","y":40,"series":"2025"},{"x":"Q2","y":58,"series":"2025"},{"x":"Q3","y":72,"series":"2025"}]' \
--title "Revenue Growth" --y-title "Revenue ($M)" \
--series-field series --dark --legend top --output chart.pngAdd thresholds, targets, or buy prices with --hline.
node scripts/chart.mjs --type line \
--data '[{"x":"Jan 1","y":0.00072},{"x":"Jan 5","y":0.00085},{"x":"Jan 10","y":0.00091},{"x":"Jan 15","y":0.00078},{"x":"Jan 20","y":0.00062},{"x":"Jan 25","y":0.00071}]' \
--title "Token Price" --y-title "Price (USD)" \
--dark --show-values --hline "0.0008,#e63946,Buy Price" --output chart.pngColor bars/points based on a threshold β great for KPI dashboards.
node scripts/chart.mjs --type bar \
--data '[{"month":"Jan","score":72},{"month":"Feb","score":45},{"month":"Mar","score":38},{"month":"Apr","score":61},{"month":"May","score":29},{"month":"Jun","score":55},{"month":"Jul","score":82},{"month":"Aug","score":47},{"month":"Sep","score":68},{"month":"Oct","score":34},{"month":"Nov","score":76},{"month":"Dec","score":91}]' \
--x-field month --y-field score --x-sort none \
--conditional-color "50,#e63946,#2a9d8f" --hline "50,#888,Target" \
--title "Monthly Performance Score" --subtitle "Target: 50" --darkFlip axes for leaderboards, rankings, or long category names.
node scripts/chart.mjs --type bar \
--data '[{"lang":"Python","stars":95},{"lang":"JavaScript","stars":82},{"lang":"TypeScript","stars":78},{"lang":"Rust","stars":71},{"lang":"Go","stars":63},{"lang":"Java","stars":58},{"lang":"C++","stars":45},{"lang":"Swift","stars":38}]' \
--x-field lang --y-field stars --horizontal --sort desc \
--conditional-color "60,#e63946,#2a9d8f" --bar-labels \
--title "GitHub Stars by Language" --darkpointβ Scatter plothistogramβ Binned quantitative distribution chartcandlestickβ OHLC financial charts (--open-field,--high-field,--low-field,--close-field)heatmapβ Grid visualization (--color-value-field,--color-scheme viridis)- Stacked bars β
--type bar --stacked --color-field category - Volume overlay β Dual Y-axis with
--volume-field - Sparkline β Tiny inline charts with
--sparkline(80Γ20, no axes)
Don't want to write JSON? Use the shorthand format:
node scripts/chart.mjs --type bar \
--data "Mon:10,Tue:25,Wed:18,Thu:30,Fri:22,Sat:35,Sun:28" \
--title "Weekly Activity" --dark --output chart.pngFormat: label:value,label:value,...
Use --dark for dark backgrounds (great for Discord, Slack, dark dashboards):
Omit --dark for light mode (reports, emails, light UIs):
Tip for bots: Auto-switch based on time of day β --dark between 20:00β07:00.
Use --font-family with any CSS font-family stack Vega can resolve.
Need more control over headings? --title-size, --subtitle-size, --title-color, and --subtitle-color let you tune chart title typography without editing the Vega spec. Need softer dashboard gridlines? --grid-dash 4,2 adds custom dashed grid patterns without touching the Vega spec either.
Safest generic families:
sans-serifserifmonospacesystem-ui
Recommended stacks:
# Brand / product sans
--font-family "Inter, Helvetica, Arial, sans-serif"
# Editorial / report serif
--font-family "Georgia, Times New Roman, serif"
# Terminal / dashboard monospace
--font-family "JetBrains Mono, SFMono-Regular, Consolas, monospace"Example:
node scripts/chart.mjs --type bar \
--data '[{"month":"Jan","value":12},{"month":"Feb","value":18},{"month":"Mar","value":15},{"month":"Apr","value":22}]' \
--x-field month --y-field value --x-sort none \
--title "Georgia / serif example" \
--subtitle "--font-family \"Georgia, serif\"" \
--font-family "Georgia, serif" --dark --show-valuesNote: specific fonts like Inter, Arial, Georgia, or JetBrains Mono only work if they exist on the host. If not, Vega falls back to the next font in the stack.
Built-in options for monitoring and alerting use cases:
node scripts/chart.mjs --type line --data '[...]' \
--title "Iran Strike Odds (48h)" \
--show-change --focus-change --show-values --dark \
--output alert.png| Flag | Effect |
|---|---|
--show-change |
Annotates % change from first to last value |
--focus-change |
Zooms Y-axis to 2Γ data range for drama |
--focus-recent N |
Shows only the last N data points |
--show-values |
Labels min/max peaks on the chart |
Read from stdin:
curl -s api.example.com/metrics | node scripts/chart.mjs --type line --dark --output metrics.png
echo '[{"x":"A","y":1},{"x":"B","y":2}]' | node scripts/chart.mjs --output out.png| Option | Description | Default |
|---|---|---|
--type |
line, bar, area, point, histogram, pie, donut, candlestick, heatmap |
line |
--data |
JSON array or shorthand key:val,... |
stdin |
--output |
Output file path | chart.png |
--title |
Chart title | β |
--subtitle |
Subtitle below title | β |
--title-align |
Title alignment: start, middle, end |
start |
--width |
Width in px | 600 |
--height |
Height in px | 300 |
--output-size |
Platform preset: twitter, discord, slack, linkedin, bluesky, youtube, instagram, portrait, story, thumbnail, wide, square |
β |
--dark |
Dark theme | false |
--svg |
Output SVG instead of PNG | false |
| Option | Description | Default |
|---|---|---|
--x-field |
X axis field name | x |
--y-field |
Y axis field name | y |
--x-title / --y-title |
Axis labels | field name |
--x-type |
ordinal, temporal, quantitative |
ordinal |
--y-domain |
Y range as "min,max" |
auto |
--y-format |
percent, dollar, compact, decimal4, integer, scientific |
auto |
| Option | Description | Default |
|---|---|---|
--color |
Primary color | #e63946 |
--color-scheme |
Vega scheme (e.g. viridis, category10) |
β |
--no-grid |
Remove gridlines | false |
--legend |
top, bottom, left, right, none |
β |
--hline |
Reference line: "value,color,label" (repeatable) |
β |
| Option | Description |
|---|---|
--series-field |
Field to split into multiple lines |
--stacked |
Stack bars/areas |
--color-field |
Field for color encoding |
--series-order CSV |
Explicit series/category order for multi-series legends and stacked-bar legend/stack order |
| Option | Description |
|---|---|
--show-change |
Show % change annotation |
--focus-change |
Zoom Y to highlight change |
--focus-recent N |
Show last N points only |
--show-values |
Label min/max peaks |
--last-value |
Label the final data point value |
--annotations |
JSON array of event markers: [{"x":"14:00","label":"News"}] |
--y-format dollar # β $1,234.56
--y-format percent # β 45.2%
--y-format compact # β 1.2K, 3.4M
--y-format decimal4 # β 0.0004
--y-format integer # β 1,234Or pass any d3-format string: --y-format ',.3f'
This skill was built specifically for headless server environments where you can't (or don't want to) install a browser:
- Fly.io β Works out of the box on
flyctl deploy. No special Dockerfile needed. - Docker β No
apt-get installfor Cairo/Pango/etc. Justnpm install. - VPS β Runs on any machine with Node.js 18+. No GPU, no display server.
- CI/CD β Generate charts in GitHub Actions, GitLab CI, etc.
The secret: Vega-Lite renders to SVG natively, then Sharp (with prebuilt libvips binaries) converts to PNG. No browser in the loop.
MIT
Built by @Cluka-399 Β· Published on ClawHub Β· GitHub










