Skip to main content

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

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.app once.
  • 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 .npy and .npz files (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 as U (V), U / V or U [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 … 225 with the label f (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_KEY or sign in with ant 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-flash works (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)

Source distribution for figscribe 0.3.0
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)

Table of built distributions (wheels) for figscribe 0.3.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

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