Toolbench
Small interactive tools for any web page.
Write one function and one JSON file. Toolbench builds the form, runs the function, and draws the answer as fields, tables, charts or bytes.
New in 0.15.0 Colour input, date input, multiselect. What's new
tools/histogram/tool.jsonThe file you write
{ "sdk": 3, "id": "histogram", "name": "Histogram", "blurb": "Bins a list of numbers and draws the counts as bars, with the running total on a second axis.", "version": "1.0.0", "capabilities": ["pure"], "runtime": { "entry": "index.ts", "thread": "main" }, "kinds": ["series", "error"], "card": "live", "autoRun": true, "inputs": [ { "id": "values", "type": "textarea", "label": "Measurements", "description": "Numbers separated by spaces, commas or newlines. Units are yours; the tool does not care.", "dir": "ltr", "rows": 4, "maxLength": 20000, "default": "2 3 3 4 4 4 5 5 5 5 6 6 6 7 7 8 9 11 14 18" }, { "id": "bins", "type": "number", "label": "Bins", "description": "Equal-width buckets between the smallest and largest value. More bins show finer shape and more empty buckets.", "unit": "bins", "default": 8, "min": 2, "max": 30, "step": 1 } ], "samples": [ { "label": "Even spread", "input": { "values": "1 2 2 3 3 3 4 4 5", "bins": 4 } }, { "label": "One spike", "input": { "values": "1 1 1 1 1 1 1 1 1 20" } }, { "label": "All the same", "input": { "values": "7 7 7 7 7" } }, { "label": "Not a number", "input": { "values": "2 3 4 nope 6" } } ], "help": "README.md", "tags": ["statistics", "chart"]}
LiveWhat a visitor gets
Click into a field to light up the lines it was built from.
By the numbers
- 19.8 KBruntime, gzipped
- 0dependencies
- v9contract, and every older one still runs
- 12chart types
- 14result kinds
- 10input types
- 5graph layouts
- tested on every change
The whole tool is a function
It takes the form's values and returns a result. It has no idea it is on the web, so it runs the same in a page, in a worker, and in your test suite.
import type { Tool } from "@toolbench/sdk"; export default { run({ text }) { return { kind: "fields", fields: [{ label: "reversed", value: [...text].reverse().join("") }] }; },} satisfies Tool<{ text: string }>;Then put it on any page, in any framework or none:
<tool-host tool="reverse" mode="card"></tool-host>What you get
- Tiny
- 19.8 KB gzipped for the runtime, once per page.
- No dependencies
- Both packages install nothing else. No framework on the page, and none inside the tool.
- Lazy
- A tool's code downloads when someone opens it. A card nobody clicks costs nothing but its markup.
- Accessible
- Real labels, keyboard operation and a status line on every tool. An error points at the input that caused it, and every chart also ships its numbers as a table.
- Themeable
- A dozen CSS custom properties restyle every tool, and a stylesheet of your own reaches the rest.
Install
Version 0.15.0. Node 24 for development; any bundler for your site.
pnpm add @toolbench/sdk @toolbench/runtime
- Write the tool. A directory with
tool.json,index.ts, known answers incases.json, and a README. - Register it. One call to
defineToolHost, once per site. - Put it on a page.
<tool-host tool="your-tool">, anywhere in your HTML.