Skip to main content

Tavotto — a visual editor for matplotlib and AI-generated scientific figures. Edit the figure, keep the code.

English · 简体中文

Release PyPI CI License Python

Download · Get started · What you can edit · Publication checks

Your plots are finished. Turning them into Figure 1 is not. Tavotto™ opens the figures matplotlib already produced and lets you click the title, the legend, a curve — and change it, right there.

The Tavotto window: a tree of the elements inside the figure on the left, three panels arranged as (a)(b)(c) on a 150 × 112.5 mm page in the middle, and the properties of the selected title on the right — font, 9 pt, its text, and the source file fig1_kinetics.py.

The title of panel (a) is selected. Its font and size are on the right — and so is the script that drew it, fig1_kinetics.py, still untouched.

Stop re-running a script to move a legend

The last stretch before submission usually goes: change a line, re-run, look, change it again. Twenty times, for things you can see but not easily say — the legend three millimetres to the left, the tick labels one point smaller, panel (b) aligned to panel (a).

The other way out is to drag the PDFs into Illustrator, finish them by hand, and accept that the figure and the code that made it have parted company.

Tavotto is the third option. Open the figure, change what you can see, export. The script stays where it was, and every change can be undone.

Edit inside the figure

Double-click a panel and Tavotto runs your script once, keeping the matplotlib Figure in memory. From then on you are editing that figure directly: pick the title, an axis label, a tick, a curve, the legend, an arrow your script drew — from the tree or by clicking it on the canvas — and change its size, colour, weight, dash pattern, or just drag it somewhere else.

Dragging and dialling are instant. The figure follows the cursor frame by frame; matplotlib runs once, when you let go, to make the change official. Hit-testing follows the drawn geometry rather than bounding boxes, so clicking a curve selects the curve and not the empty rectangle around it.

Build the page

The same window in layout mode: the asset library on the left showing the three source PDFs with their physical sizes, the composed page in the middle with one panel selected, and its position, size in millimetres and scale on the right.

Panels land on a page measured in millimetres, at the size their script drew them. Drag them, snap them to each other, align and distribute a selection, group what belongs together, or bind panels into a row, column or grid that reflows when a size changes. Panel labels (a)(b)(c) come from one command. Annotations — text, arrows, shapes, at any angle — sit on top, with presets for the usual research furniture: reversible-reaction arrows, scale bars, zoom boxes.

Your script is still the source

Editing a figure never touches your .py file. Every change is stored as an override beside the document and replayed onto a fresh run of your script whenever the figure is opened again — which is also why undo, version history and re-rendering at export quality all work on the same footing. (The one exception is the optional assistant below, which you have to ask for by name.)

If you do want a change baked into the figure file on disk, "write back to the original file" is an explicit action: it re-runs your script from scratch to prove the result matches what you were looking at, and it can be locked off per project.

Export for publication

PDF export embeds each original vector panel as it was drawn, so the text stays real, selectable, searchable text. PNG is rasterised from that same PDF, so the two can never disagree. Two deliberate exceptions: a panel with opacity below 1, or with a flip applied, is embedded as a bitmap at your export DPI — PDF vector content supports neither.

Before anything is written, Tavotto checks the figure against a publication profile and tells you what a reviewer would have told you three weeks later:

The preflight list in the export dialog: two blocking findings about text below the profile's 8.5 pt and 8 pt floors, warnings about a frame line width and a legend font size, suggestions about bold legend text, axis-label format and lines drawn without markers, and one item marked not verifiable.

Font sizes are judged at their final physical size — a panel placed at 60% is checked against fontsize × 0.6, not against what the script asked for. Findings come in four levels. Blocking stops the export until you confirm in writing. Warning is always shown. Not verifiable is what genuinely cannot be checked — text inside an imported bitmap — and needs a human. Suggestion never decides anything for you. All of it, your confirmation included, goes into an optional proof report written next to the exported files.

Finish figures an AI wrote

Workflow: a Python script — written by you, Claude or Codex — runs matplotlib, which produces a vector PDF; Tavotto handles visual editing, layout and the publication preflight; the result is a PDF or PNG. The script stays the source throughout.

Coding agents write good first-pass matplotlib. What is left over is visual — the legend two lines too tall, the panel that wants to be a little smaller, the label overlapping a data point. Describing that in a prompt is slower than doing it.

Hand a figure over with one command, from a terminal or from an agent:

tavotto open figures/Fig1_kinetics.pdf   # the output file
tavotto open figures/fig1_kinetics.py    # or the script — the output name is resolved for you
tavotto open figures/                    # or the whole figure library

It opens that figure's library as a project, registers anything missing, and hands the figure to the desktop app — into the window that is already open, if there is one. Without the desktop app it falls back to browser mode.

Codex users can install the plugin, which teaches Codex the shape a Tavotto-editable figure has to have (script beside its output, vector PDF, an output name that resolves statically) and puts the editor inside Codex itself:

codex plugin marketplace add Tavotto/Tavotto && codex plugin add tavotto@tavotto

It ships a skill, a local MCP server with six tools — open a figure, apply overrides, run the preflight, export true-vector PDF/SVG or PNG at an explicit DPI, verify a replay, close a session, all usable in hosts with no interface at all — and an embedded canvas built from the same frontend code the desktop app runs, so dragging, snapping and undo have no second implementation. See codex-plugin/README.md, including which parts are not yet verified inside a real Codex Desktop.

A path suggested by the model is never treated as permission. On a zero-config first open, a capable Codex host shows the canonical local directory for you to approve; that approval lasts only for the current Tavotto MCP connection.

Everything runs on your machine

Rendering, composition and export are local processes. Tavotto does not upload your figures, scripts, project files or data anywhere, and unpublished results do not leave the building. It makes exactly two requests on its own:

  • A once-a-day check of GitHub Releases for a new version. Turn it off under Settings → Check for updates (or set TAVOTTO_NO_UPDATE_CHECK=1).
  • Anonymous usage statistics — off until you say yes. You are asked once, on first run. If you opt in, Tavotto sends broad feature events (app started, figure opened, edit committed, export succeeded) plus version, OS family and architecture, tagged with a random UUID generated on your machine. Never your figures, scripts, filenames, paths, data, figure text or assistant prompts — the event schema cannot represent them. Turn it off under Settings → Privacy, diagnostics & About (or set TAVOTTO_NO_TELEMETRY=1).

The two switches are independent; neither covers the other. Details in the privacy policy and the event contract.

Get started

Desktop

Download from the latest release: a .dmg for macOS (Apple Silicon) or an .exe for Windows. Install it, double-click, done — Tavotto opens in its own window and updates itself from then on.

You do not need to install Python. Both installers carry a private Python runtime with the usual scientific stack already in it — numpy, matplotlib, pandas, scipy, seaborn, Pillow — pinned to the same versions on both platforms, so the same script draws the same figure. Rendering works the moment the installer finishes: offline, without Homebrew, Conda or Xcode, and without touching a Python you already have.

That runtime is also why the installers are large: 195 MB to download on macOS, 89 MB on Windows, around half a gigabyte once installed. Paid once, and offline.

macOS builds are Apple Silicon (arm64) only. Intel Macs are neither built nor tested — use the PyPI install below. There is no Linux installer; Linux runs from PyPI (browser mode, beta). The single source of truth for what is supported, beta, and unsupported — per platform and per Python version — is docs/support-matrix.json; release pages, the website and in-app copy must match it (a test enforces the facts it can check).

PyPI

Same command on all three platforms:

pipx install "tavotto[worker]"
tavotto

Your browser opens at http://127.0.0.1:5089. --figures <dir> opens a figure directory straight away, --port changes the port, --no-browser skips opening a browser.

Try it in 30 seconds

pipx install "tavotto[worker]"
git clone --depth 1 https://github.com/Tavotto/Tavotto.git
tavotto --figures Tavotto/examples/figures

Three panels appear in the asset library. Drag one onto the page, double-click it, click the title in the element tree, change 9 pt to 11, export. examples/figures/ holds two perfectly ordinary matplotlib scripts — Tavotto does not ask you to write them in any particular way.

Advanced installation and Python environments

pip, into the current environment:

pip install "tavotto[worker]"
tavotto

Reuse the environment your figures were made in. Drop the [worker] extra and point Tavotto at your own interpreter, so figures render against exactly the dependencies they were written for:

pipx install tavotto
export TAVOTTO_WORKER_PYTHON=/path/to/your/env/bin/python   # Windows: setx TAVOTTO_WORKER_PYTHON "..."
tavotto

From source (needs node + pnpm to build the interface):

git clone https://github.com/Tavotto/Tavotto.git && cd Tavotto
python -m venv .venv && .venv/bin/pip install -e ".[worker,dev]"
python scripts/build_frontend.py
.venv/bin/tavotto

Which interpreter renders your figures. Tavotto picks in this order: TAVOTTO_WORKER_PYTHON → the one you chose in Settings → the bundled runtime → its own interpreter → a Python or Conda it finds on the machine. Whatever you choose explicitly always wins, and Tavotto only launches the environment you point it at: it never installs anything into it and never modifies an existing Python or Conda. The bundled runtime is likewise never written to — bytecode and the matplotlib font cache go to Tavotto's own data folder, so the installed app stays byte-identical (on macOS, writing into it would break the code signature).

The bundled runtime covers the common scientific stack. It is not a promise to cover whatever your scripts import. If a script needs something it does not have (rdkit, astropy, your lab's own library) Tavotto names the missing package and offers to switch to your environment under Settings → Rendering environment; it will not install that package for you, into its runtime or into yours. With no working interpreter at all, layout, annotation and export still work — only editing inside a figure needs one.

Settings → Privacy, diagnostics and About shows which interpreter is in use, where it came from (bundled, configured, system, …) and, for the bundled runtime, the exact pinned version of every package. The same information is in the diagnostics bundle.

The first open of a figure runs your script. Light figures take a second; heavy ones take as long as they normally do. Every edit after that is sub-second.

Where Tavotto fits

matplotlib alone A vector editor Tavotto
Draws the plot ✓ — Uses the plots you already have
Direct visual editing Limited ✓ ✓
Knows what it is editing Objects in your code Generic paths and glyphs Title, legend, ticks, series, colourbar
Multi-panel page in millimetres By hand in code ✓ ✓
Vector text in the exported PDF ✓ ✓ ✓
Edits stay attached to the script ✓ Separate file from here on ✓
Journal rules checked before export — — ✓

A vector editor is the more powerful drawing tool, and always will be. It just does not know that the thing you clicked is a legend.

What you can edit inside a figure

Text Title, axis labels, tick labels, legend entries, annotations — content, size, colour, weight, style, rotation, opacity, visibility. Draggable.
Data series Line width, dash pattern, colour, markers (scatter markers can be swapped wholesale), legend entry order
Arrows Arrows your script drew (FancyArrowPatch): drag the whole arrow or either endpoint, change arrow style, line style, width, head size, colour. Arrows attached to annotate() keep their data anchors — style only.
Axes Tick locators and formatters (how many ticks, where, written how), tick marks, grid, spines individually or all four, limits, scales, aspect. Drag a subplot and what belongs to it travels along — a label you had moved, its colourbar, a twin axis.
Colourbars Orientation, both-ended extend triangles, colour map, range, tick and label styling — rebuilt in place, so undo, write-back and re-export stay consistent
3D axes Viewing angle (elev / azim / roll), projection, axis lines, panes, grid, per-axis tick groups, optional axis arrows
Figure Overall size in millimetres (the layout reflows), background

What Tavotto does not do is invent plot content. It changes the properties of things your script already drew; new curves, new panels and different data still come from the script — which is the point.

Publication profile and preflight

The rules live in one versioned JSON file (src/tavotto/profiles/publication.json) that both the Python engine and the TypeScript frontend read, so there is no second copy to drift.

The default lab-publication-v1 encodes 80 mm single / 150 mm double column; 16:9, 4:3 and 1:1 aspect ratios; 9 pt body text with a hard floor above 8 pt of final effective size (8.5 pt strict); ≥ 300 dpi rasters; Times New Roman with an explicit CJK fallback; 0.5 / 0.75 / 1.0 / 1.5 pt line widths; ticks in, enclosed spines, frameless legends; Title (unit) axis labels; and Scientific colour maps by semantic type.

A journal with its own widths needs an override, not a fork: {"widths_mm": {"double": 178}} inherits the rest, and the override is recorded in the proof report.

Assistant (optional)

The assistant panel can hand a request to the Codex or Claude CLI on your machine to edit the script itself — "move the legend to the top left and make it 7 pt". Your script is snapshotted first; afterwards you see the diff, the figure re-renders, and one click reverts it. This is the one path that touches your source, and you have to ask for it. Everything else works without those tools installed.

Where your files live

Data directories and what goes in them
Documents and autosaves macOS ~/Library/Application Support/Tavotto/ · Linux ~/.local/share/tavotto/ · Windows %LOCALAPPDATA%\Tavotto\
Exports, canvas files and version history Inside your project, in one tavottofile/ folder: exports in tavottofile/export/, named canvases beside them, version history in tavottofile/versions/. Visible, backupable, and synced along with your figures. Files written by older versions stay readable where they are.
Your scripts and figures Read-only, unless you explicitly choose "write back to the original file" — which can be locked off per project

Security and code signing

Free code signing provided by SignPath.io, certificate by SignPath Foundation. Windows release installers are built from this repository by GitHub Actions and are submitted for manual signing before they are described as signed releases. See the code signing policy.

Report security issues through private reporting, not a public issue.

Contributing

Issues and pull requests are welcome — CONTRIBUTING.md covers how to verify a change and which boundaries the codebase keeps deliberately. When reporting a bug, Settings → Privacy, diagnostics and About → Download diagnostics bundle collects everything usually needed, with keys and personal paths redacted.

.venv/bin/python -m pytest        # backend
cd web && pnpm test               # frontend
cd web && pnpm build              # type-check (tsc -b) + bundle

License

AGPL-3.0-only.

Using Tavotto, modifying it and running it inside your lab are all unrestricted, and the figures and PDFs you produce with it are entirely yours — the licence does not reach your work. The obligations apply to distribution: if you give a modified Tavotto to others, or run it as a network service for them, the corresponding source has to be available to those users.

Tavotto™ is a trademark of the Tavotto project.


If Tavotto saves you an afternoon on your next figure, consider starring the repository.

Metadata

Release files for tavotto 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tavotto 0.10.0
File Size Uploaded
tavotto-0.10.0.tar.gz 2.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for tavotto 0.10.0
File Interpreter ABI Platform
tavotto-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 3.5 MB

Release files / tavotto-0.10.0.tar.gz

Download URL tavotto-0.10.0.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
c24d859306603be015b23e5016ec42df20955a87c969640a8f5952bb6b971803
BLAKE2b-256 checksum
How to use checksums
4bbc17971ebf2b5cdac2f0165b1703fdab35d30f957ebcfa65f16ec00910c43f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release files / tavotto-0.10.0-py3-none-any.whl

Download URL tavotto-0.10.0-py3-none-any.whl
Size 847.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
15c5ffec9a454723cacfe03abcd258236f7ecdbaa409590313adcbcec3960f0a
BLAKE2b-256 checksum
How to use checksums
f1cb3f44fb58ae3f5a95fe4e6f2d10186157887c218554e1f33c5ccf6dc2e8a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release history Release notifications | RSS feed

0.17.0

2 release files

0.16.0

2 release files

0.15.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

This release

0.10.0 This release

2 release files

0.9.2

2 release files

0.8.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page