Toolbench

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

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.

tools/reverse/index.ts
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:

page.html
<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
  1. Write the tool. A directory with tool.json, index.ts, known answers in cases.json, and a README.
  2. Register it. One call to defineToolHost, once per site.
  3. Put it on a page. <tool-host tool="your-tool">, anywhere in your HTML.

Read the full walkthrough or browse the source.