Bonnard

Bonnard draws a scatter chart to plot one measure against another, one point per row, and a bubble chart when a third measure sets each point’s size.

The examples use Acme, a demo retailer that reports in pounds. Each variant is a few words in the question. A follow-up changes a chart already drawn.

How an agent draws it

The chat above ran three tools, in this order:

  1. explore_schema finds the tables and their columns: here weekly_store_sales, with week_start, store, footfall and orders.
  2. sql_query runs a read-only query, so the agent can check the numbers first.
  3. render_chart takes the SQL and the chart options. It runs the query and returns the chart the chat draws.

The render_chart call behind the chart above:

render_chartjson

JSON

{
  "charts": [
    {
      "sql": "SELECT store, ROUND(AVG(footfall)) AS footfall, SUM(orders) * 1.0 / SUM(footfall) AS conversion FROM weekly_store_sales WHERE week_start >= DATE '2026-06-29' GROUP BY store",
      "chartType": "scatter",
      "title": "Footfall against conversion by store, Q3 2026",
      "encode": { "x": "footfall", "y": "conversion" },
      "fields": [
        { "name": "footfall", "label": "Weekly footfall", "format": "number" },
        { "name": "conversion", "label": "Conversion", "format": "percent" }
      ]
    }
  ]
}

A call takes up to 10 charts, shown as a carousel. Each example below shows its one entry. A connected agent reads the full option reference with explore_docs, topic charts/scatter.

Bubbles

Prompt

Footfall against conversion for each store last quarter, with bubbles sized by revenue.

render_chartjson

JSON

{
  "sql": "SELECT store, ROUND(AVG(footfall)) AS footfall, SUM(orders) * 1.0 / SUM(footfall) AS conversion, SUM(revenue) / 1000 AS revenue FROM weekly_store_sales WHERE week_start >= DATE '2026-06-29' GROUP BY store",
  "chartType": "scatter",
  "title": "Footfall against conversion, sized by revenue, Q3 2026",
  "encode": { "x": "footfall", "y": "conversion", "size": "revenue" },
  "fields": [
    { "name": "footfall", "label": "Weekly footfall", "format": "number" },
    { "name": "conversion", "label": "Conversion", "format": "percent" },
    { "name": "revenue", "label": "Revenue (£k)", "format": "number" }
  ]
}

A third measure sets each point’s size, scaled to the largest value on the chart. The chart names that measure under its title.

Colour by group

Prompt

Footfall against conversion for each store last quarter, coloured by region.

render_chartjson

JSON

{
  "sql": "SELECT store, region, ROUND(AVG(footfall)) AS footfall, SUM(orders) * 1.0 / SUM(footfall) AS conversion FROM weekly_store_sales WHERE week_start >= DATE '2026-06-29' GROUP BY store, region",
  "chartType": "scatter",
  "title": "Footfall against conversion by region, Q3 2026",
  "encode": { "x": "footfall", "y": "conversion", "series": "region" },
  "fields": [
    { "name": "footfall", "label": "Weekly footfall", "format": "number" },
    { "name": "conversion", "label": "Conversion", "format": "percent" }
  ]
}

A text column splits the points into groups, one colour each, and the legend lists the largest group first. The store, the other text column, names each point in its tooltip.

Quadrants

Prompt

Footfall against conversion for each store last quarter, split at the median footfall and our 4% conversion target.

render_chartjson

JSON

{
  "sql": "SELECT store, ROUND(AVG(footfall)) AS footfall, SUM(orders) * 1.0 / SUM(footfall) AS conversion FROM weekly_store_sales WHERE week_start >= DATE '2026-06-29' GROUP BY store",
  "chartType": "scatter",
  "title": "Stores against median footfall and the conversion target",
  "encode": { "x": "footfall", "y": "conversion" },
  "fields": [
    { "name": "footfall", "label": "Weekly footfall", "format": "number" },
    { "name": "conversion", "label": "Conversion", "format": "percent" }
  ],
  "reference": [
    { "y": 0.04, "label": "Target 4%" },
    { "x": "median", "label": "Median footfall" }
  ]
}

Only a scatter takes a vertical line. With one vertical and one horizontal line the points fall into quadrants, each line at a number, the average or the median.

Axis ranges

Prompt

Footfall against conversion for each store last quarter, with footfall from zero and the conversion axis fitted to the data.

render_chartjson

JSON

{
  "sql": "SELECT store, ROUND(AVG(footfall)) AS footfall, SUM(orders) * 1.0 / SUM(footfall) AS conversion FROM weekly_store_sales WHERE week_start >= DATE '2026-06-29' GROUP BY store",
  "chartType": "scatter",
  "title": "Footfall against conversion by store, Q3 2026",
  "encode": { "x": "footfall", "y": "conversion" },
  "fields": [
    { "name": "footfall", "label": "Weekly footfall", "format": "number" },
    { "name": "conversion", "label": "Conversion", "format": "percent" }
  ],
  "axes": { "x": { "min": 0 }, "y": { "min": "auto" } }
}

By default the x-axis fits the data and the y-axis starts at zero. Each end of either axis takes a number, or a fit to the data.

Options and limits

What a scatter chart takes
Ask forWhat Bonnard draws
”as a scatter plot” or “footfall against conversion”One point per row, from two numeric columns
”sized by revenue”A bubble chart, a third measure setting each point’s size
”coloured by region”One colour per group, with a legend
”France in amber”A named colour per group, from eight colours and grey
”make the UK stand out”Up to three groups in colour, the rest in grey
”the UK as triangles”A point shape per group
”split at the median”Vertical and horizontal lines that make quadrants
”a target line” or “the average”Reference lines and bands, up to six
”start footfall at zero”A fixed range on either axis, or one fitted to the data
”legend at the top”The legend at the top or bottom, or hidden
  • One row is one point, and the chart sums nothing: the query does any grouping.
  • Both axes take numeric columns only, and the x-axis is always a number line.
  • The colour split takes a text column and ignores a numeric one. The size takes a numeric column.
  • A chart query returns up to 5,000 rows by default, so up to 5,000 points.
  • Points turn more translucent past 150 and again past 400, so overlaps read as density.
  • A fixed axis end wins over a reference line, which is then not drawn.
  • A scatter takes no trend line or moving average: those are for line, bar and area charts.
  • Labels and axis titles run to 60 characters.
  • A scatter chart draws no data labels. No chart draws a log scale, custom hex colours or zoom.