Browse by type
A small browser tool for generating clean black-and-white (or colour-band-accented) dial scale graphics. Pick a shape, dial in the range and graduations, configure typography and colour zones, and download the result as a vector .svg, a multi-scale .png, or copy SVG markup straight to the clipboard.

Live: https://artofpilgrim.github.io/Dials/
min (e.g. range 3–23 step 10 yields 3, 13, 23, not 0, 10, 20).{ color, endValue } data model. Inner / outer position relative to the rim, adjustable thickness, configurable start / end value range (defaults to full dial range), and a stacked preview bar that updates live as you edit. Four built-in presets (Traffic light, Warning, Cool, Mono) re-scale to your current range on insert.°, %, mph), and per-tick custom labels (L, M, H …) that override the numeric value at any index.<link> injected into <head>. The chosen family applies to tick numbers, custom labels, and the centre title text.
localStorage and reload it later.minorStep → subdivisions, etc.).#hash; sharing the URL reproduces the exact dial.hashchange is honoured so back / forward and pasted URLs work, and clearing the hash resets to defaults.− / % / + / Fit controls.ResizeObserver so the canvas always fills the available stage area while preserving its aspect ratio.
<text> element with a vector <path> (via opentype.js) so the receiver of the file doesn't need the font installed. opentype.js and the TTF are lazy-loaded only when the user actually exports with outlining on, so the main bundle stays small.localStorage.npm install
npm run dev # http://localhost:5173
npm run build # produces dist/
npm run preview # serves dist/ for sanity checking the build
.
├── index.html # Vite entry; lean shell + <noscript> fallback
├── src/
│ ├── main.jsx # ReactDOM root
│ ├── App.jsx # State, controls, presets, zoom, export
│ ├── Dial.jsx # SVG renderer (straight + arc + circle + colour band)
│ └── styles.css # All styles
├── vite.config.js # base: '/Dials/' for the GitHub Pages subpath
├── .github/workflows/
│ └── deploy.yml # Builds with Vite, publishes via actions/deploy-pages
└── package.json
Every push to main triggers a GitHub Action that runs npm ci && npm run build and publishes dist/ via the official actions/deploy-pages workflow. Pages is configured with build_type: workflow (set once via the GitHub API; not stored in the repo).
If you fork this and want to deploy under a different repo name, change the base in vite.config.js to match your new subpath (e.g. /your-repo-name/).
min/max are coerced to numbers with a fallback span so equal or inverted ranges never produce NaN coordinates.ResizeObserver so the canvas always fills the available stage area while preserving its aspect ratio.dialMaker.presets.v1 — saved presets (view state — zoom / pan — is intentionally not stored).dialMaker.section.<id> — open/closed state per sidebar section.DEFAULTS, written as short-key URL params via URLSearchParams (e.g. #s=arc&sa=135&sw=270). A default dial has no hash at all. Legacy base64-JSON hashes still decode for backward compatibility with older shared links. The colour-band zones array uses a custom compact color:value,color:value encoding rather than nested JSON.DEFAULTS.shape, tickDirection, numberPlacement, orientation, tickSide, bg, fontFamily, colorBandPosition) are checked against an allowlist; unknown values fall back.tickColor must match ^#[0-9a-f]{6}$; anything else falls back.true / false only. The hash encoding is 1 / 0; any other value leaves the field unset so DEFAULTS wins.numberSuffix, customLabels, centerText) are type-checked and soft-capped at 32 / 1024 / 256 characters so a maliciously long hash can't stall the renderer.#rrggbb colours, and that the final stop reaches max. Zones outside the band's [start, end] range are clipped at render time.
$ claude mcp add Dials \
-- python -m otcore.mcp_server <graph>