How to make charts with a code interpreter
Run matplotlib code with sbx.interpreter.run(); every open figure comes back as a PNG in the result, with no file to fetch.
On Runtime the chart libraries are already in the sandbox. The default image included NumPy 1.26.4, pandas 2.1.4 and matplotlib 3.6.3 on 24 September 2026, when PNG plotting was checked in a live sandbox (changelog), so a cell that draws a chart needs no install step. The interpreter keeps variables between cells like a notebook, runs in its own Firecracker microVM, and speaks seven languages: Python, JavaScript, TypeScript, R, Java, Bash and Go. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while it waits for the next cell (pricing).
Draw a chart and save it
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.interpreter.run(`import pandas as pdsales = pd.DataFrame({"month": ["Jan", "Feb", "Mar", "Apr"], "revenue": [120, 135, 160, 152]})`);const cell = await sbx.interpreter.run(`import matplotlib.pyplot as pltfig, ax = plt.subplots(figsize=(6, 3.5))ax.bar(sales["month"], sales["revenue"], color="#3b6ea5")ax.set_title("Revenue by month")`);for (const [i, result] of cell.results.entries()) { const inline = result.data["image/png"]; const ref = result.refs["image/png"]; if (typeof inline === "string") await writeFile(`chart-${i}.png`, Buffer.from(inline, "base64")); else if (ref) await writeFile(`chart-${i}.png`, await sbx.interpreter.result(ref));}Pythonimport base64from withruntime import Sandboxwith Sandbox.create() as sbx: sbx.interpreter.run( "import pandas as pd\n" "sales = pd.DataFrame({'month': ['Jan', 'Feb', 'Mar', 'Apr'], 'revenue': [120, 135, 160, 152]})" ) cell = sbx.interpreter.run( "import matplotlib.pyplot as plt\n" "fig, ax = plt.subplots(figsize=(6, 3.5))\n" "ax.bar(sales['month'], sales['revenue'], color='#3b6ea5')\n" "ax.set_title('Revenue by month')" ) for i, result in enumerate(cell["results"]): if "image/png" in result["data"]: png = base64.b64decode(result["data"]["image/png"]) elif "image/png" in result["refs"]: png = sbx.interpreter.result(result["refs"]["image/png"]) else: continue with open(f"chart-{i}.png", "wb") as file: file.write(png)Terminalruntime sandbox run-code "${id}" analysis.py --out-dir charts # each chart saved as a PNGThe second cell uses sales from the first: variables persist in the
language's context until you restart it. You do not call plt.show() or
savefig; figures still open when the cell ends are returned as PNG images
and then closed, so the next cell starts with none.
What a cell returns
| Field | What it holds |
|---|---|
results[].data["image/png"] |
A chart, base64-encoded PNG |
results[].refs["image/png"] |
A result too large to travel inline; fetch it with interpreter.result(ref) |
results[].data["text/plain"] |
The value of the cell's last expression, as text |
results[].data["application/vnd.runtime.table+json"] |
A pandas or R data frame, as a table |
stdout, stderr |
What the cell printed |
error |
name, value and traceback when the cell raised |
status |
ok, error, interrupted, timeout or lost |
A chart and a table can come back from the same cell, so a chatbot can show the picture and let the user sort the numbers.
Other formats and languages
SVG or a PDF. Save the figure yourself and read the file back:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.interpreter.run(`import matplotlib.pyplot as pltplt.plot([1, 4, 9, 16])plt.savefig("/workspace/line.svg")plt.close()`);const svg = await sbx.files.readText("/workspace/line.svg");console.log(svg.slice(0, 60));R. Base R plots come back as PNG in the same way:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ timeoutSeconds: 900 });const cell = await sbx.interpreter.run("plot(pressure, type = 'b')", { language: "r", timeoutMs: 300_000,});console.log(cell.results.length, cell.status);R installs from Ubuntu's archive the first time a sandbox uses it, which took
about 90 seconds on 23 September 2026. Bake it into a
custom image with apt: ["r-base-core"], the package the interpreter installs, to skip that.
Other Python libraries. A library the image does not list, such as
seaborn, installs with sbx.exec("pip install seaborn"), or goes in an image
recipe's pip list so every sandbox starts with it
(what is installed).
Mistakes and how Runtime handles them
- Looking for the chart in
stdout. Charts arrive inresults, not as printed text. Printing the figure object shows only its description. - Charts that pile up. Each cell's open figures are closed once returned, so a loop of cells never redraws old charts.
- A cell that hangs. The default timeout is 60 seconds. A timeout is a
result with
status: "timeout", not an exception, and the context keeps its variables. - Model code you do not trust. The cell runs in a microVM with its own
kernel. Turn the internet off for it with
sbx.network.off()(how to).
Where this helps
- A data analysis agent that answers with a chart.
- A code interpreter for a chatbot, where users upload a CSV and ask for a plot.
- What is a code interpreter? for the idea in general.
An agent connected to Runtime's MCP server gets the same through
runtime_sandbox_interpreter_run, with charts as images in the tool result.
Start
Terminalnpx withruntime sandbox run --trial -- python3 -c 'import matplotlib; print(matplotlib.__version__)'New accounts get 50 free sandbox hours, no card. The first run prints a link to approve in your browser.
Facts on this page were checked on 25 September 2026.