Runtime

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 PNG

The 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 in results, 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

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.