Verified CNC toolpath strategies and the small motion IR they compose onto — the reusable core extracted from the ShopBot Labs step-toolpath app.
Each strategy is a self-contained function that turns a geometric feature (a pocket, a profile, an edge chamfer, a bored hole, a 3D relief surface) into machine moves on a shared rail, checked by an independent verifier before it becomes G-code or ShopBot SBP. The idea is that you can lift one file — say the chamfer strategy — understand it on its own, and reuse the concept in your own CAM code.
Grab a strategy. Want the chamfer logic? It's
strategies/chamfer.js. The strategies depend only on each other, their/rail, and a bundled copy of Clipper — no framework, no build step.
feature (pocket / profile / chamfer / bore / relief)
│
strategies/*.js ← the "how to cut it" — geometry → toolpath
▼
ir/moves.js ← the canonical motion rail (the neck): every
strategy emits the SAME move vocabulary
│
ir/job.js ← compose ops into one multi-tool job
ir/verify.js ← admission gate: refuses gouges / cut-throughs /
bad plunges BEFORE anything posts
▼
G-code · ShopBot SBP
Strategies never post directly and never trust themselves — they emit moves, and
ir/verify.js re-derives the safety constraints independently. That separation
(a wide set of strategies above a narrow verified rail) is the whole point.
strategies/ — the toolpath generators:
| file | what it does |
|---|---|
chamfer.js |
Edge-break / bevel along a feature edge with a V-bit. |
pocket.js |
Pocket clearing — the core area-clearing primitive. |
profile.js |
Cut the part free (profile/perimeter, with tabs). |
bore.js |
A circular hole cut by a bit near its own diameter. |
rest.js |
Rest machining — a smaller bit cleans what the bulk bit couldn't reach. |
surface-raster.js |
Ballnose-compensated 3D raster over a heightmap (reliefs, dishes, textures). |
tool-select.js |
Which bit machines which feature, and when to add a smaller one. |
ir/ — the composition IR the strategies share:
| file | what it does |
|---|---|
moves.js |
The canonical motion rail — the common move vocabulary. |
job.js |
Compose ops into one multi-tool job. |
verify.js |
The verifier — the admission gate (gouge / depth / plunge checks). |
tools.js |
Shared tool drawer + a deterministic feeds/speeds engine. |
placement.js |
Transform op-local moves into job coordinates. |
arc-fit.js |
Optional arc-fitting pass on the moves rail. |
vendor/clipper.js — Clipper 6.4.2 (Angus
Johnson), polygon offsetting/clipping, wrapped as an ES module. Boost Software
License; see NOTICE.
Pure ES modules — browser or Node, no build step, no dependencies to install
(Clipper is vendored). Clone it and run the example below with node.
This cuts a rectangular part free from a board and posts ShopBot .sbp. The
shape of every job is the same: run a strategy → compose → verify → post.
import { generateProfile } from './strategies/profile.js';
import { composeJob, postJobToSbp } from './ir/job.js';
import { verifyJob } from './ir/verify.js';
// 1. A feature — a 3" x 2" part outline. Points are { x, y } in inches, placed
// with clearance inside the stock so an outside cut stays on the board.
const region = { outer: [ { x: 0.5, y: 0.5 }, { x: 3.5, y: 0.5 },
{ x: 3.5, y: 2.5 }, { x: 0.5, y: 2.5 } ] };
// 2. A tool — a 1/4" flat endmill, carrying the machine's real &Tool number.
const tool = { number: 1, diameter: 0.25, kind: 'flat', flutes: 2 };
// 3. Run a strategy — profile-cut the part free, tool OUTSIDE the line, with
// hold-down tabs so the part doesn't break loose mid-cut.
const prof = generateProfile(region, tool, {
side: 'outside', totalDepth: 0.5, depthPerPass: 0.25, safeZ: 0.25,
tabs: { height: 0.08, length: 0.3 },
});
// 4. Wrap it as a one-operation job over a piece of stock.
const job = {
units: 'in',
stock: { w: 4, h: 3, thickness: 0.5 },
safeZ: 0.25,
spindleSpeed: 16000,
tools: { 1: { name: '1/4" endmill', diameter: 0.25, kind: 'flat', rpm: 16000 } },
operations: [
{ name: 'Cut out', tool: 1, feedRate: 120, plungeRate: 40, moves: prof.moves },
],
};
// 5. Compose → verify → post. verifyJob MEASURES the real motion (stock
// envelope, cut depth, plunge) and refuses anything unsafe before it
// becomes machine code — the strategy is never trusted to police itself.
const moves = composeJob(job);
const report = verifyJob(job, moves);
if (!report.ok) throw new Error('refused: ' + report.errors.join('; '));
const sbp = postJobToSbp(job, moves, { title: 'quickstart' }); // ShopBot .sbp
// const gcode = postJobToGcode(job, moves, { title: 'quickstart' }); // or G-code
console.log(sbp);The verifier is real. Move the part flush to the corner — { x: 0, y: 0 } —
and verifyJob returns ok: false, because an outside cut with a 1/4" tool
would run off the stock. Nothing posts until the motion checks out.
Swap generateProfile for generatePocket, generateChamfer, generateBore,
or generateSurfaceRaster to cut a different feature — same compose/verify/post
loop, same job shape.
The strategies are written to be read, not just called — each file opens with a comment explaining the geometry and the reasoning, because the goal is to share the toolpathing concepts, not just an API.
This is an extracted, reusable subset — the strategy + IR core. The surrounding application (STEP-file feature detection, the UI, the LLM intent layer, the hosting) is not included here. Interfaces may still change; treat it as a reference and a starting point rather than a frozen library.
Apache License 2.0 — permissive, with an explicit patent grant. See
NOTICE for third-party attributions.