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. PNG plotting was checked in a live sandbox on that date (changelog). So a cell that draws a chart needs no install step. The interpreter keeps variables between cells, like a notebook. It runs in its own Firecracker microVM, and it supports 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 sandboxes run-code "${id}" analysis.py --out-dir charts # each chart saved as a PNGThe second cell uses sales from the first. Variables stay in the language's
context until you restart it. You do not need to call plt.show() or
savefig. Figures that are 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. That took
about 90 seconds on 23 September 2026. To skip that install, bake R into a
custom image with apt: ["r-base-core"], the package the
interpreter installs.
Other Python libraries. A library the image does not list, such as
seaborn, installs with sbx.exec("pip install seaborn"). Or it 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 they are returned. So a loop of cells never redraws old charts.
- A cell that hangs. The default timeout is five minutes. A timeout is a
result with
status: "timeout", not an exception. 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_sandboxes_interpreter_run. Charts come back as images in the tool
result.
Start
Terminalnpx withruntime sandboxes run -- python3 -c 'import matplotlib; print(matplotlib.__version__)'A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card needed. The first run prints a link to approve in your browser.
Facts on this page were checked on 26 October 2026.