Writing a custom feature
Make your own parametric smart feature in TypeScript, like FeatureScript: typed parameters become a dialog, build() adds recipe steps, and it runs sandboxed in the browser and on our servers.
A custom feature is your own smart feature: a small TypeScript program that takes typed parameters and adds recipe steps. Once saved, anyone in the workspace inserts it from ⌘K in any Thing. Its parameters become a dialog with a live preview, and in the Recipe it reads as one sentence, like "3 × 2 Gridfinity bin, 6 units tall", with its numbers as editable chips.
Think of it as FeatureScript, in TypeScript, using the same calls as the script view.
Start one
Open Workspace → Custom features (or ⌘K, Custom features) and press New feature for a small example, or Start from the Gridfinity example for a real one. The editor checks your code as you type, in the same sandbox the build uses, and shows two things on the right:
- The dialog: the parameters you declared, as people will see them.
- Steps it adds: what one run with the defaults produces.
Press Save version when it works. Each save is a new version (v1, v2…). Steps already in a recipe keep the version they were made with.
The shape of a feature
export default feature({
name: "Peg row",
description: "Pegs in a row on top of a part.",
summary: "{count} pegs, {spacing} mm apart",
params: {
count: number({ label: "Pegs", default: 4, min: 1, max: 20, integer: true }),
spacing: length({ label: "Spacing", default: 15, min: 2 }),
diameter: length({ label: "Diameter", default: 4 }),
square: boolean({ label: "Square pegs", default: false }),
},
build(p, ctx) {
const z = ctx.bbox ? ctx.bbox.max[2] : 0;
for (let i = 0; i < p.count; i++) {
const x = (i - (p.count - 1) / 2) * p.spacing;
if (p.square) box("peg_" + i, { size: [p.diameter, p.diameter, 6], position: [x, 0, z + 3] });
else cylinder("peg_" + i, { radius: p.diameter / 2, height: 6, position: [x, 0, z] });
}
},
});summaryis the Recipe sentence;{name}is replaced by the parameter's value.build(p, ctx)receives the parameters already evaluated (millimetres and degrees, even when the person typed a formula likewidth / 2).ctx.bboxis the bounding box of what's built so far (ornull),ctx.hasBodysays whether there is anything yet, andctx.warn("…")adds a note to the step.
Parameter types
| Declare | In the dialog | Your code gets |
|---|---|---|
number({ … integer }) | a number field | a number |
length({ … }) | a field in mm | a number (mm) |
angle({ … }) | a field in degrees | a number (°) |
boolean({ … }) | a switch | true / false |
choice(["a", "b"], { … }) | buttons or a list | the chosen value |
face({ default: "top" }) | the picked face, or a side | a face selector |
edge({ default: "vertical" }) | the picked edges, or a group | an edge selector |
Each takes label, default, an optional help line, and for numbers min, max and step. Values outside min/max are refused in the dialog and in the build, with your label in the message.
Building blocks
Everything from the script view: sketch, extrude, revolve, sweep, loft, primitive, shell, fillet, chamfer, draft, hole, pattern, mirror, boolean, rib, transform, offsetFaces, split and the built-in smart features through macro(…); planes XY(), XZ(), YZ(), onFace(selector); shapes rect, circle, ellipse, polygon, slot, path (with line, arc3…); selectors topFace(), faces(…), edges(…). Two shortcuts: box(id, { size, position }) and cylinder(id, { radius, height, position }). Plus plain JavaScript: loops, Math, functions.
Step ids are local: write sketch("base", …) and extrude("body", { sketch: "base", … }). Solid Vibes prefixes them with the step's id, so the same feature can be used twice in one piece.
The sandbox
Custom features run in a separate JavaScript engine (QuickJS, compiled to WebAssembly) in your browser and on our geometry servers:
- No network, no page, no files. There is no
fetch,window,document,processorrequire;importis not available. - Same result every time.
Dateis not available andMath.random()always returns the same sequence, so builds can be cached and replayed. - Limits. 1.5 seconds and 32 MB per run, at most 500 steps. An endless loop is stopped with a clear message on the step.
The code travels inside the recipe step, so commits, exports, the server and remixes all build it without looking anything up.
Share it
- Everyone in the workspace can insert it from ⌘K.
- Publish to Marketplace (on the feature's page) releases it as a Feature listing, shown with an example Thing built with its defaults. Others press Add to my features on the listing to get a copy in their workspace, and can read its code first.
- Plugins can bring custom features with them (the Gridfinity bin generator does).
Did this help?
Keep reading
For CAD pros
Script view and the edit vocabulary
Every piece is also readable code, editable both ways. When to use script view, and the small set of edit types that describes every change anyone, human or agent, can make.
Pro4 min readFor CAD pros
Selectors and topological naming: how Solid Vibes stays robust
Recipes never store face or edge indices. They store semantic queries. Here is the selector vocabulary, why it survives upstream edits, and how to debug an empty match.
Pro4 min readFor CAD pros
REST API quickstart
Read a Thing, change a parameter with versioned ops, build it, read its mass and export an STL from your own script, with curl or the TypeScript SDK.
Pro6 min read