figscribe
A lean desktop application for scientific figures: build and fine-tune plots interactively, and get clean, standalone Python code (Matplotlib) as the result instead of a proprietary project file.
For scientists and engineers who find Origin unwieldy but don't want to hand-write every detail of a Matplotlib figure just to fine-tune it.
- What you see is what you get: the preview, the exported PDF and the generated script draw the same figure, checked pixel by pixel in the tests.
- Sizes as journals state them: millimetres and points, with layouts for single and double columns.
- Everything is undoable, including changes made by an AI assistant.
- Your data stays yours: an assistant sees file and column names, never the values, and can run entirely on your own computer.
Contents
- Status
- Installation
- Getting started
- Features
- The generated code
- AI assistants
- Keyboard and mouse
- Command line
- How it works
- Scope
- Development
Status
Alpha. The features below are implemented and tested on Linux, Windows and macOS with Python 3.11 and 3.13. Behaviour may still change; project files of a release keep opening in later ones. Still to come: style templates, snapping and alignment guides, signed installers, and more plot types, axis formats and fit models (see the roadmap). What changed in each release is in the changelog.
Installation
Installers (no Python needed)
From version 0.3.0 each release has an installer for each system, with the assistant and MCP parts included:
| System | File | Notes |
|---|---|---|
| macOS (Apple silicon) | figscribe-VERSION-macos-arm64.dmg |
Open it and drag figscribe to Applications. Intel Macs: use pipx, below. |
| Windows 10/11 | figscribe-VERSION-windows-x64-setup.exe |
Installs for you alone; no administrator needed. |
| Linux (x86-64) | figscribe-VERSION-linux-x86_64.AppImage |
chmod +x it and run it. Needs libxcb-cursor0 (Debian, Ubuntu) or xcb-util-cursor (Fedora), which most desktops have. |
The installers are not signed yet, so the system asks you once:
- macOS: "figscribe cannot be opened because the developer cannot be
verified". Right-click figscribe in Applications, choose Open, and confirm.
If macOS says the app is damaged, run
xattr -cr /Applications/figscribe.apponce. - Windows: SmartScreen shows "Windows protected your PC". Choose More info, then Run anyway.
The first start takes several seconds longer: Matplotlib builds its font cache.
figscribe script and figscribe mcp are in the same program: on macOS
/Applications/figscribe.app/Contents/MacOS/figscribe, on Windows
figscribe-cli.exe in the install folder.
With Python
figscribe needs Python 3.11 or newer. The simplest way is
pipx or uv, which give
figscribe an environment of its own and put the figscribe command on the
path:
pipx install figscribe # or: uv tool install figscribe
figscribe
pip install figscribe in a virtual environment works as well. With Qt,
Matplotlib and SciPy the installation takes about 1.5 GB. The oldest versions
of the libraries that are tested are NumPy 2.0, SciPy 1.13, Matplotlib 3.10
and PySide6 6.7.
Optional parts:
| Extra | For |
|---|---|
pipx install "figscribe[chat]" |
the built-in AI assistant (Claude, ChatGPT, Gemini, local models) |
pipx install "figscribe[mcp]" |
driving figscribe from an MCP client such as Claude Desktop |
pipx install "figscribe[chat,mcp]" |
both |
To add an extra later, install again with --force. To update:
pipx upgrade figscribe. To work on figscribe itself, see
Development.
Getting started
Start figscribe. The start page offers four ways in:
- Open a data file (CSV,
.npy,.npz). The import dialog detects delimiter, decimal comma, header and unit rows, shows a preview and suggests a first plot, e.g. U against t with error bars dU. - Pick a layout: single column (85 × 60 mm), double column (175 × 70 mm), data with fit and residuals, two panels side by side, or a 16:9 slide.
- Open an example from the gallery: a Gaussian scan, a noisy sensor with its means over the raw line, an RC discharge with fit and residuals, a resonance, a color map and more, twelve in all. Each is a copy in a folder you choose, with its data, ready to change.
Then click anything in the figure or in the contents list to select it, and change it in the property panel on the right. Insert adds data, plots, fits, notes and panels; File > Export writes the figure or its Python script.
Features
Data
- CSV files with automatic detection of delimiter, decimal comma, header and
unit rows, and of the text encoding (UTF-8, or cp1252 and Latin-1 as older
Windows programs write them); NumPy
.npyand.npzfiles (read without pickles, so a data file cannot run code). - A data file is removed with the plots that draw from it, in one undo step; figscribe asks first and names them.
- Each data file is recorded with its SHA-256. If it changes later, figscribe says so instead of silently plotting different data, shows what changed, and takes the new version on request, or lets you point it to where a moved file went.
Plots
- Line, scatter, error bars, histograms, images and color meshes with color bars (linear or logarithmic), notes of one or several lines with or without an arrow (optionally on a background, so they read over a grid), reference lines and shaded ranges.
- Error bars as a shaded band around the line, for dense data.
- The Allan deviation of a time series (fractional frequency or phase), on log-log axes, for the stability of clocks, lasers and sensors.
- Curve fits: linear, polynomial, Gaussian, Lorentzian, exponential, or your
own expression such as
a*exp(-x/tau)+c. Error bars weight the fit; the result shows each parameter with its 1σ uncertainty, the reduced χ² and the residuals, and can be copied. Residuals go into their own panel below, with a line width and a zero line of their own to style. - Fit only a range of x; hold a parameter at its start value or keep it within bounds; change a fit that is already there.
- Leave points out by right-clicking them, or let the fit dialog find outliers and leave them out once you confirm. The data file stays as it is: the points are drawn apart, in light grey or a color and marker of your choice, and the script leaves out the same rows, with the reason.
- Show a plot's values in other units, e.g. seconds as hours since the
start of the measurement or volts as mV, or by a formula such as
x / 60 - 273.15, and draw it from part of its data only. The axis, its marks and the fits follow in the same step. - Draw fewer points than the file has: the mean or median of every n rows, every n-th row, or the mean over intervals of x for unevenly sampled data. An averaged point gets its standard error as error bar (propagated when the plot has errors of its own), fits use the reduced points, and the script does the same arithmetic. Rows left out are removed before averaging, or replaced by the mean of their neighbours.
Layout and style
- Several figures in one project, as tabs above the figure: add one with the look of the others, duplicate, rename, reorder.
- Several panels in a grid: add one to the right, left, above or below, or as an inset. Shared x and y axes, a second y axis on the right.
- Exact figure size in mm, font sizes in pt, linear, log and symlog axes,
axis ranges, major ticks every n with minor ticks between them, tick
marks inward or outward and on all four sides, grid, LaTeX in labels
(
$\Delta U$), units in labels asU (V),U / VorU [V]. - Panel letters, (a), (b), … in the style a journal asks for.
- Axis labels lined up across panels, so that in a column of panels the y labels stand on one line, and a distance for each axis' label.
- Line widths of the frame, the tick marks and the grid, tick lengths, and a dashed or dotted grid.
- Values shown with an SI prefix: frequencies in Hz drawn as
0 … 225with the labelf (kHz), while the data stay in Hz.
Working with the figure
- Click to select, edit in the property panel; every change is one undo step. Click an axis, its numbers or its label, to set its range, ticks and label in plain terms: major ticks every 50, 4 minor ticks between them; with values shown in kHz you type the range in kHz too. Hovering over a field explains it.
- Find a setting by typing (Ctrl+F), or any menu action or setting with the command palette (Ctrl+K). The settings used most come first, the rest are under More. A setting that does nothing in the current state is greyed out, and its tooltip says why.
- Above the settings a path such as Figure › Panel A › X axis shows where you are, each part a link back; the axes are rows in the outline, under their panel.
- Colors come from the figure's color cycle, the colors used lately, or Same as… another plot; a plot's context menu brings it to the front or sends it to the back.
- Numbers can be typed with a decimal comma (
1,5), a minus copied from a PDF (−3) and spaces between thousands (10 000); lists of them are then separated by semicolons (0; 0,5; 1). - Drag the legend and notes into place. Zoom with the mouse wheel and pan by dragging to look closer; Use as axis range keeps that view.
- Double-click an axis label, a title, a legend entry or a note to edit it where it is drawn. Drag an inset's corner to resize it, or the gap between two panels to share the room between them differently. Right-click a plot to style it like another, bring it to the front, fit or hide it.
- The Python code drawer under the figure shows the generated script, live, as you edit.
- Help > About figscribe names the versions of figscribe, Python, Matplotlib and Qt, for a bug report.
Export
- PDF, SVG, EPS, PNG and TIFF (LZW compressed, as journals ask) at the exact size, PNG, TIFF and SVG also with a transparent background; File > Export All Figures… writes every figure of the project at once.
- A standalone Python script (see below).
- The project itself as a readable JSON file (
.figscribe).
The generated code
File > Export Python Script… (or figscribe script PROJECT.figscribe -o plot.py) writes a script that needs only NumPy, Matplotlib and, for fits and
for fewer points drawn, SciPy. It is written the way a person would write it:
no figscribe helpers, named variables, comments where they help. From the
example project's script (shortened):
scan_path = HERE / "scan.csv"
# The figure was made from exactly this file; delete the check to plot a newer one.
if hashlib.sha256(scan_path.read_bytes()).hexdigest() != (
"380a81c153b9c2f47f51c67b4478a251b9e98fd1ba0e533b010e9fb5eab92394"
):
raise SystemExit(f"{scan_path} changed since the figure was made")
t, U, dU = np.loadtxt(scan_path, delimiter=",", skiprows=3, unpack=True)
fig = plt.figure(figsize=(85 / 25.4, 60 / 25.4), dpi=300, layout="constrained")
ax = fig.add_subplot()
ax.set_xlabel("$t$ (s)")
ax.set_ylabel("$U$ (V)")
ax.errorbar(t, U, yerr=dU, linestyle="none", marker="o", markersize=4,
capsize=2, elinewidth=1, label="measurement")
ax.legend(fontsize=8, loc="best")
fig.savefig(HERE / "scan.pdf")
Fits are redone in the script with scipy.optimize.curve_fit from the same
start values, and averaged points with scipy.stats.binned_statistic. A test
runs the generated scripts and compares their PNG pixel by pixel with
figscribe's own export.
AI assistants
Describe a change in words, "two panels, residuals below, shared x axis", and an assistant makes it in the open project. It works through the same commands as the GUI, so every change appears at once and can be undone.
What an assistant sees: the project's structure and the names of files and columns, never the data values. It sees the rendered figure only if you allow it (Assistant > Let It See the Figure).
The built-in chat
Assistant > Chat (Ctrl+Shift+A) opens the chat under the figure; Assistant > Choose Model… selects what it talks to:
- Claude through the Anthropic API: set
ANTHROPIC_API_KEYor sign in withant auth login. - ChatGPT, or any server with the OpenAI chat completions API: set
OPENAI_API_KEY. - Google Gemini: a key from Google AI Studio, in
GEMINI_API_KEY. The Gemini API has a free tier. Load models also lists models that a new key cannot use or that do not answer;gemini-3.5-flashworks (October 2026). - A local model in LM Studio (
http://localhost:1234/v1), Ollama (http://localhost:11434/v1), vLLM or llama.cpp: nothing leaves your computer. Load models lists what the server offers; choose one that supports tool calling.
figscribe remembers the model and server. An API key typed into figscribe is kept for the session only and never saved.
Other assistants through MCP
figscribe is also an MCP server, for Claude Desktop, Claude Code, IDEs or front ends for local models:
pipx install "figscribe[mcp]"
claude mcp add figscribe -- figscribe mcp # e.g. for Claude Code
Then enable Assistant > Allow MCP Access; Assistant > How to Connect… shows the configuration for other clients.
Without paying per request
- A local model costs nothing and keeps everything on your computer; how well it works depends on its size (see below).
- Gemini's free tier: a key from Google AI Studio, without billing. It allows 5 requests a minute and 20 a day per model (October 2026), and one change takes the assistant two to four: enough to try the assistant, a handful of changes a day. figscribe says how long to wait; another model has its own limit. Outside the EU, EEA, Switzerland and the UK, Google may use free-tier requests to improve its products and have people read them; inside, its paid-service data terms apply. Either way the model sees names, not data values.
- A subscription (Claude Pro or Max, ChatGPT Plus) does not give API access, and Anthropic does not allow its sign-in in other applications. It can drive figscribe through MCP instead: in Claude Desktop or Claude Code with your Claude plan, or in Gemini CLI with a Google account.
Which model is good enough?
figscribe bench measures it: it gives a model 21 typical requests, from
easy (an axis label, a range, a colour) to hard (an inset zoomed in, a fit
with residual panel), on the example project and checks each result
automatically.
figscribe bench --base-url http://localhost:1234/v1 --model qwen3-30b --no-vision
figscribe bench --model claude-sonnet-5-5 --json sonnet.json
figscribe bench --list shows the cases, --level easy runs only the easy
ones, --repeat 3 evens out variation between runs.
Keyboard and mouse
| Action | Keys |
|---|---|
| New, open, save, save as, quit | the system's usual keys (Ctrl+N, Ctrl+O, Ctrl+S, …) |
| Undo, redo, delete the selection | Ctrl+Z, Ctrl+Shift+Z or Ctrl+Y, Del |
| Duplicate the selected plots, panel or figure | Ctrl+D |
| Copy a plot's style, paste it onto others | Ctrl+Alt+C, Ctrl+Alt+V |
| Copy the figure to the clipboard (PNG and SVG) | Ctrl+Shift+C |
| Import data | Ctrl+I |
| Find a setting of the selection | Ctrl+F, then type |
| Find any setting or menu action | Ctrl+K, then type and Enter |
| Add plot, add fit | Ctrl+Shift+P, Ctrl+Shift+F |
| Export figure, export Python script | Ctrl+E, Ctrl+Shift+E |
| Show the Python code | Ctrl+Shift+Y |
| Show the assistant | Ctrl+Shift+A |
| Zoom a panel | mouse wheel; with Shift only x, with Ctrl or Alt only y |
| Pan a panel | drag its background |
| Edit a label, title, legend entry or note | double-click it |
| Back to the axis ranges of the model | Esc |
On macOS, Cmd takes the place of Ctrl.
Command line
figscribe [PROJECT.figscribe] open the GUI, optionally with a project
figscribe script PROJECT.figscribe [--figure NAME] [-o FILE]
write the Python script for a figure
(by default the first)
figscribe script PROJECT.figscribe --all [-o FOLDER]
write a script for every figure
figscribe mcp serve the open project to an MCP client
figscribe bench [--help] measure a model on typical assistant requests
figscribe --version
How it works
A typed, declarative plot model is the single source of truth:
Plot model (typed, JSON-serializable)
├──> Renderer: model -> Matplotlib figure (preview AND export)
├──> Code generator: model -> standalone Python script
└──> Persistence: model <-> JSON project file
The GUI and the assistants only ever change the model, through commands with undo and redo; they never touch Matplotlib objects. Preview and export share the renderer, and the code generator mirrors its calls, so all three show the same figure.
Design decisions and their reasons are recorded in
docs/decisions.md; the project brief is in
plan/project-brief.md and the work list in
plan/roadmap.md.
Scope
Planned beyond what exists: HDF5 import, several files as one series, number formats and inverted axes, a second x axis on top, more fit models and a confidence band, step plots and bar charts, style templates for journals, snapping and alignment guides, signed installers.
Deliberately out of scope: spreadsheet-style data editing, a statistics suite, a scripting language of its own, and reading generated or other Python code back in. An analysis comes in only as a standard figure of some field that the script can compute in a few lines, such as the Allan deviation (decision 0036).
Development
git clone https://github.com/f4ren/figscribe.git
cd figscribe
pip install -e ".[dev]"
pytest -n auto # all tests, in parallel, the GUI ones without windows
ruff check . && ruff format --check .
The tests include image comparisons of the renderer, the pixel comparison of generated scripts, undo for every command, and GUI tests with pytest-qt. Work happens test-first on one branch per feature, merged by pull request once CI is green on Linux, Windows and macOS.
Built with Python, PySide6 (Qt), Matplotlib, NumPy, SciPy and Pydantic.
License
figscribe is MIT, see LICENSE.
It uses Qt through PySide6 under the LGPL-3.0, and other free software under
its own licenses (BSD, MIT, Apache-2.0 and the like). The installers carry
the license of everything in them in THIRD-PARTY-NOTICES.txt and LICENSE.txt:
beside the app in the macOS disk image, in the install folder on Windows (the
installer also shows the license), in usr/share/doc/figscribe inside the
AppImage, and in the program's licenses folder on every system. The Qt
libraries are separate files in the program's folder, so you can replace them
with another build of the same version. IBM Plex Sans and Mono are under the
SIL Open Font License (OFL.txt next to them in the package).
Metadata
Release files for figscribe 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| figscribe-0.3.0.tar.gz | 2.0 MB | With provenance | Details for figscribe-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| figscribe-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for figscribe-0.3.0-py3-none-any.whl |
Total release size: 3.3 MB
Release files / figscribe-0.3.0.tar.gz
| Download URL | figscribe-0.3.0.tar.gz |
|---|---|
| Size | 2.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aa7b47a1c1f634b9bd93b469747ea5830e7cb88df2a4350b066bee89b38e5281
|
|
BLAKE2b-256 checksum How to use checksums |
db5c560620b1cf2156fd7fca74589993fdad47aad77eab40f5f065a62c4390fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 5, 2026.
Transparency logRelease files / figscribe-0.3.0-py3-none-any.whl
| Download URL | figscribe-0.3.0-py3-none-any.whl |
|---|---|
| Size | 1.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
91d493b3856895c1771a056e3326693a4efc98557b9c1d5f470754702b598314
|
|
BLAKE2b-256 checksum How to use checksums |
e4f4f6b01504d084f3df247259937051ef7eea37bb5f29c879c11f10d679ef11
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 5, 2026.
Transparency logSecurity for figscribe 0.3.0
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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for figscribe-0.3.0.tar.gz
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 Nov 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.