# Plugins

> Add abilities to your canvases and to Benchy, or let Benchy use the apps on your Mac. Install one from a file or make your own.

Canonical: https://benchy.world/docs/plugins
Last updated: 2026-10-10

A plugin adds abilities to Benchy. Workspace plugins run on a canvas in Studio and give Benchy tools of their own; [Benchy Desktop's plugins](#desktop) let Benchy use apps on your Mac.

## Install a plugin [#install]

Only the workspace's owner installs, updates and removes plugins, from **Plugins** in the workspace menu. Everyone in the workspace can see what is installed, under **Installed**.

- **Browse** lists Benchy's own plugins: Swatches and [Print planning](https://benchy.world/docs/print-planning). Choose **Install**.
- **Install from file…** takes one plugin, a single `.html` file.

Before installing, Benchy shows what the plugin can do, in its own words and then the plugin's reason for each, such as **Read what you select on a canvas**. It also lists the tools Benchy can use from it and the file's fingerprint, the start of its SHA-256, so a reviewer can check it is the same file.

An installed plugin is a pinned copy of the file the owner approved; nothing updates by itself. **Update from file** takes a newer file and shows the version change, marking anything new as **New**; a built-in plugin shows **Update** when Benchy has a newer one. **Remove** takes a plugin out at once; what it already added to canvases stays, with its history.

## Run a plugin [#run]

On a canvas, open the canvas menu and choose **Plugins**, then the plugin, or find it under **Plugins** in the canvas side panel. **Run last plugin** (⌥⌘P, or Ctrl+Alt+P) opens the last one again. Its window floats over the canvas beside Layers, or opens as a bottom sheet on a phone. The owner and members who can edit run plugins on canvases they can edit; viewers and guests don't see them.

Every change a plugin makes is a **Plugin edit** in the canvas history and undoes as one step: one press in its window, or everything one Benchy request made with it. See [History](https://benchy.world/docs/canvases-and-docs#history).

## Benchy and plugins [#benchy]

While a canvas is open, Benchy can use the tools of the workspace's plugins: ask in the chat beside it, for example “Make a palette from this blue.” Each use shows a card with the plugin, the tool and its input, and runs only once you approve. After a plugin answers, Benchy doesn't search or fetch the web for the rest of that reply, so nothing a plugin read can leave through a search.

## Make a plugin [#make]

A plugin is one HTML file, at most 256 KB, with everything inline: its window, its styles and its scripts. Its manifest is a JSON block in the same file.

palette.html

```
<!doctype html>
<script type="application/benchy-plugin+json">
{
  "benchyPlugin": 1,
  "id": "acme.palette",
  "name": "Palette",
  "version": "1.0.0",
  "description": "A row of tints from one colour.",
  "author": "Acme",
  "permissions": [
    { "scope": "canvas.read", "why": "Starts from the colour of the layer you select." },
    { "scope": "canvas.place", "why": "Adds the swatches beside your selection." }
  ],
  "ui": { "width": 300, "height": 360 },
  "tools": [
    {
      "name": "make_palette",
      "description": "Place a row of tints of one colour beside the selection.",
      "input": {
        "color": { "type": "string", "maxLength": 7, "description": "A hex colour, like #3366ff." },
        "steps": { "type": "integer", "minimum": 3, "maximum": 9, "description": "How many swatches." }
      },
      "required": ["color"]
    }
  ]
}
</script>
<button id="add">Add to canvas</button>
<script>
  // window.benchy is ready before your scripts run.
</script>
```

| Field | What it holds |
| --- | --- |
| `benchyPlugin` | `1`, the manifest version |
| `id` | Lower-case words joined by dots, such as `acme.palette`: 2 to 6 parts of letters, digits and hyphens, at most 64 characters. It stays the same across versions. Ids starting `benchy.` belong to Benchy's own plugins |
| `name` | At most 40 characters. A plugin from a file can't use Benchy in its name |
| `version` | Three numbers, `major.minor.patch`. An update must be higher |
| `description`, `author` | At most 200 and 60 characters; the author is optional |
| `permissions` | Up to four, each `{ scope, why }`. The reason, at most 160 characters, is shown when the plugin is installed |
| `ui` | The window's size: `width` 240 to 400 and `height` 160 to 600. Leave it out for a plugin that only gives Benchy tools |
| `tools` | Up to eight tools Benchy can call, each with a `name`, a `description` of at most 300 characters and an `input` of up to eight fields, plus the `required` ones. A field is a `string` (with `maxLength` up to 2000 and an optional `enum` of up to 16 values), a `number` or `integer` (with `minimum` and `maximum`), or a `boolean`, and each has a `description` |

A plugin needs a window, tools, or both. A manifest with a field Benchy doesn't know is refused rather than installed with less than it asks for.

| Permission | Shown as | Allows |
| --- | --- | --- |
| `canvas.read` | Read what you select on a canvas | The selected layers, up to 100: each one's id, kind, name, position, size, fill colour and text |
| `canvas.place` | Add shapes and text to a canvas | New rectangles and text. It never changes, moves or removes a layer that is already there |
| `printers.read` | Read the workspace's printers | The workspace's [printers](https://benchy.world/docs/printers): each one's name, model, build volume, nozzle and materials, its bed's shape, and whether it prints without supports. Never a printer's notes |
| `models.check` | Measure the 3D models you select for printing | Benchy's production check of a selected 3D model: its size, the best ways up and whether each fits the printer, supports, wall thickness, grams and print time. The numbers only, never the model file |

## The plugin API [#sdk]

Benchy defines `window.benchy` before the plugin's own scripts run. Every call returns a promise.

js

```
const { nodes } = await benchy.canvas.getSelection();      // canvas.read
const color = nodes[0]?.fill ?? "#3366ff";

await benchy.canvas.place([                                   // canvas.place
  { type: "rectangle", x: 0, y: 0, w: 64, h: 64, fill: color, radius: 8, name: "Swatch" },
  { type: "text", x: 0, y: 76, text: color, fontSize: 14 },
]);                                                           // beside the selection

benchy.tools.handle("make_palette", async ({ color, steps = 5 }) => {
  await benchy.canvas.place(swatches(color, steps));
  return { summary: `Placed ${steps} swatches from ${color}.` };
});

benchy.notify("Added 5 swatches");
benchy.close({ message: "Done" });
```

| Call | Does |
| --- | --- |
| `benchy.canvas.getSelection()` | Needs `canvas.read`. Answers `{ width, height, nodes }`: the selection's size and its layers as `{ id, kind, name, x, y, w, h, fill, text }`, positioned from the selection's top-left corner |
| `benchy.canvas.place(nodes, { anchor })` | Needs `canvas.place`. Adds up to 200 nodes and answers `{ placed }`. A node is `{ type: "rectangle", x, y, w, h, fill?, radius?, name? }` or `{ type: "text", x, y, text, fontSize?, fontWeight?, fill?, w?, name? }`. Positions count from the anchor: `"beside"`, the default, is 40 px to the right of the selection, or the middle of the view when nothing is selected; `"over"` is the selection's top-left corner. A plugin never works in the canvas's own coordinates |
| `benchy.printers.list()` | Needs `printers.read`. Answers `{ printers }`, each `{ id, name, model?, buildVolumeMm?, nozzleMm?, materials, bed, supportFree }`, with sizes in mm and `bed` `"rectangle"` or `"round"` |
| `benchy.models.check(id, options)` | Needs `models.check`. Measures one 3D model layer in the selection, by its `id` from `getSelection()`. The options are `printer` (an id from `printers.list()`, which also needs `printers.read`), `material` (PLA, PETG, PCTG, ABS, ASA, TPU, PA, PA-CF or PC), `layerHeightMm`, `infillPercent` and `longestSideMm` (for a model with no real size). It answers the same result as Benchy's `production_check` tool, with `recommended`, `alternatives`, `time`, `costDrivers` and `mesh`, or `{ status: "not_checked", reason }` for a file it can't read. Up to 20 checks a minute, two at a time |
| `benchy.tools.handle(name, handler)` | Answers one of the manifest's tools when Benchy calls it. The handler gets the input, already checked against the manifest, and returns `{ summary }`, at most 500 characters, which Benchy reads. Everything it places is one step in the canvas history |
| `benchy.notify(text)` | Shows a short message that starts with the plugin's name: plain text, at most 140 characters, five a minute |
| `benchy.close({ message })` | Closes the window, with an optional closing message |
| `benchy.theme`, `benchy.on("theme", fn)` | `"light"` or `"dark"`, following Studio, and a callback when it changes |
| `benchy.manifest`, `benchy.version` | A copy of the installed manifest, and the API version, `1` |

Studio's colours and font reach the window as CSS variables that follow the theme, so a plugin can look like the rest of Benchy:

css

```
body {
  background: var(--benchy-bg);
  color: var(--benchy-fg);
  font: 14px/1.5 var(--benchy-font);
}
```

A refused call rejects with `{ code, message }`. The codes are `permission_denied` (the plugin wasn't granted that permission), `invalid_request`, `rate_limited`, `canvas_unavailable` (the canvas can't be changed right now), `not_found` (the layer isn't a selected 3D model, or the printer isn't in this workspace) and `too_large`.

## Safety and limits [#safety]

- A plugin runs in a sandbox, apart from Studio. It never sees your sign-in, your other files or the rest of the page, and it has no network.
- It can't open windows or tabs, show dialogs, or ask for your camera, microphone or location, and it draws only inside its own window. Benchy never asks you to sign in inside a plugin.
- Everything it does goes through Benchy, as you, within the permissions the owner approved, and is marked as a Plugin edit.
- A plugin can add to a canvas up to 20 times a minute, with up to 200 shapes and text each time. A workspace holds up to 25 plugins.
- A plugin that reads your selection, your printers or a model's measurements could pass on what it reads, so grant those only to plugins you trust. A plugin never receives a model's file.

## Plugins in Benchy Desktop [#desktop]

[Benchy Desktop](https://benchy.world/docs/desktop) has plugins of its own, for apps on your Mac, with Blender built in. Turn one on or off, and see its app, version and tools, under **Settings → Plugins → Installed**. What the Blender plugin does, and how Desktop asks before using it, is under [Blender and other apps](https://benchy.world/docs/desktop#blender).

## Apps Benchy works with [#apps]

Every app Benchy knows by name, and how far it reaches each one today. A skill can be written for any of them; the rows differ in what Benchy can do on its own while the skill runs.

| App | Maker | What works today | Files |
| --- | --- | --- | --- |
| Blender | Blender Foundation | Benchy Desktop runs it through a plugin · Files in and out | `.blend, .glb, .stl, .obj, .3mf, .exr, .hdr, .png` |
| Unreal Engine | Epic Games | Files in and out | `.fbx, .glb, .gltf, .obj, .png, .exr, .hdr` |
| Unity | Unity Technologies | Files in and out | `.unitypackage, .fbx, .obj, .dae, .dxf` |
| Cinema 4D | Maxon | Files in and out | `.c4d, .fbx, .obj, .abc, .glb, .gltf, .usd, .usda, .usdc, .usdz` |
| Houdini | SideFX | Files in and out | `.hip, .hipnc, .fbx, .obj, .abc, .glb, .gltf` |
| OpenSCAD | OpenSCAD | Files in and out | `.stl, .obj, .3mf, .dxf, .svg, .png` |
| Autodesk Fusion | Autodesk | Files in and out | `.f3d, .f3z, .step, .stp, .3mf, .stl, .dxf, .dwg` |
| AutoCAD | Autodesk | Files in and out | `.dwg, .dxf, .lsp` |
| Bambu Studio | Bambu Lab | Files in and out | `.3mf, .stl, .obj, .step, .stp, .gcode` |
| OrcaSlicer | SoftFever | Files in and out | `.3mf, .stl, .obj, .step, .stp, .gcode` |
| PrusaSlicer | Prusa Research | Files in and out | `.3mf, .stl, .obj, .step, .stp, .gcode, .bgcode` |
| Adobe After Effects | Adobe | Files in and out | `.aep, .aet, .mov, .mp4, .png, .exr, .hdr` |
| DaVinci Resolve | Blackmagic Design | Files in and out | `.drp, .drt, .mov, .mp4, .cube, .otio, .edl, .aaf` |
| Remotion | Remotion | Files in and out | `.mp4, .webm, .mov, .gif, .png, .mp3` |

Blender, Unreal Engine, Unity, Cinema 4D, Houdini, OpenSCAD, Autodesk Fusion, AutoCAD, Bambu Studio, OrcaSlicer, PrusaSlicer, Adobe After Effects, DaVinci Resolve, Remotion, Blender Foundation, Epic Games, Unity Technologies, Maxon, SideFX, Autodesk, Bambu Lab, SoftFever, Prusa Research, Adobe and Blackmagic Design are trademarks of their respective owners. Benchy is not affiliated with or endorsed by them.
