COSCAD(1) General Commands Manual COSCAD(1)

coscad — glyph/pipeline CAD language compiler: .coscad and .assemble to native STL, print beds, build plans and stress reports

coscad part.coscad [-o part.scad]

coscad validate [--json] [--stdin | --project-stdin] part.coscad | spec.assemble

coscad stl part.coscad [-o part.stl]

coscad spec.assemble

coscad next [--strict] spec.assemble

coscad check spec.assemble [--keep-temp]

coscad plan spec.assemble [--png]

coscad calibrate times.json

coscad site dir spec.assemble ...

coscad doctor

coscad capabilities --json

coscad stress part.coscad [-o report.json] [--full]

coscad --version | --help

validate checks a .coscad or .assemble file, including recursive part sources and rigid mating constraints, without writing output files or rendering meshes. It is suitable for editor diagnostics. It does not run interference or instruction-access checks. With --json it prints one JSON document on standard output (schema ‘coscad.validate/1’: ‘valid’, ‘exit’, and ‘diagnostics’, each with severity, file, line, column, message and source excerpt) and nothing on standard error; the exit status is unchanged. With --stdin the file's text is read from standard input (an unsaved editor buffer); part and subassembly references still resolve from the file's directory. With --project-stdin the input is a JSON snapshot with schema coscad.project/1 and a files array of objects containing absolute path and text strings. All supplied buffers are used for recursive references; other sources come from disk. The response includes canonical dependency paths, including missing references. The snapshot limit is 256 buffers and 8 Mi characters. Aliases of the same buffer path are rejected. Neither input mode writes source files.

capabilities --json reports schema coscad.capabilities/1, compiler version, build default and geometry backend (‘native’), feature names (including ‘openscad-retired’) and material names/moduli.

stress renders and analyses the part's support/load/stress directives. The full report includes node and cell fields. Exit 2 means the requested safety factor was not met, 1 means analysis could not run. Validation checks analysis settings and datum identities without solving the part. coscad meshes CoScad parts itself (its own tessellation plus the pinned Manifold kernel) and writes STL, print beds, build plans with rendered step images, and stress reports. It does not use OpenSCAD or BOSL2; a part can be exported as OpenSCAD source for people who continue in OpenSCAD. A .coscad file is a list of definitions, one per line; the definition named ‘main’ is the rendered shape. An .assemble file declares physically separate parts, how many to print, and how they sit together; the manufacturing stage orients and packs them onto print beds.

Anchors, attachments, bezier outlines, lofts, fit checks, print orientation, and bed packing are all computed in the compiler, and any expression, including composites, can be attached to again. The exported OpenSCAD source stays plain (translate, rotate, union, difference, BOSL2 primitives).

coscad part.coscad [-o out.scad]
Export one part as OpenSCAD source, for use in OpenSCAD. Nothing in coscad reads it. Writes part.scad next to the input unless -o is given.
coscad stl part.coscad [-o out.stl]
Mesh the part natively and write an ASCII STL, then print the triangle count, volume, and bounding box. The part's OpenSCAD source is written beside the STL as an export. Geometry comes from coscad's own tessellation and the pinned Manifold kernel: boxes, polygonal cylinders, spheres, cones, frusta, regular prisms, tubes, wedges, prismoids, chamfered and rounded cuboids and cylinders, ordinary tori, transforms, booleans, hulls, Minkowski sums, extrusions, round 2D offsets of a single outline, and lofts (triangulated the way BOSL2's skin does it). Unsupported operations are errors; there is no fallback. --backend=native (or the earlier --backend=native-experimental) is accepted by every rendering command for older scripts; --backend=openscad names the backend retired in 1.6.0.0 and is an error. Manufacturing manifests record the geometry backend (‘native’).
coscad spec.assemble
Design stage. Renders the assembled view (spec_asm.png) and the packed plate(s) (spec_plate.png, or spec_plate1.png, spec_plate2.png ...) natively, writes the same geometry and one print-oriented spec_part_<variant>.scad per unique (part, orientation) as OpenSCAD source exports (spec_asm.scad, spec_plate.scad ...), and the design manifest spec_design.json (schema coscad.design/1). It never writes spec_manifest.json; if it finds a design manifest left there by coscad 1.2 or earlier it prints a note to standard error and leaves the file in place.
coscad next [--strict] spec.assemble
Manufacturing stage. Renders each variant once, chooses a print orientation where none was declared (scoring bed contact, overhang, height, and slenderness on the real mesh), packs instances largest-first across as many beds as needed, and writes spec_bed<N>.stl plus spec_manifest.json (schema coscad.next/1), recording per-variant orientation and printability and per-instance placements for slice-once, stamp-many slicing. Printability warnings go to standard error and to the manifest, each with a code in ‘warning_codes’ (‘small_contact, ‘tall_narrow, ‘flat_overhang, ‘elevated_start’’’’). With --strict, any warning makes next exit 2 after the outputs are written. Instances with a brim (see ASSEMBLIES) are packed with the brim reserved around their footprint; each placement records ‘brim_mm’.
coscad check spec.assemble [--keep-temp]
Interference and clearance check on real meshes. Every part is rendered as placed by the ‘asm’ expression; close pairs are intersected exactly by the native kernel. Reports ‘OVERLAP’ with its volume, ‘contact’ for touching faces, and ‘WARN’ for gaps below the declared ‘clearance’. Exit status 1 if any overlap. Scratch renders go to the system temp directory and are removed unless --keep-temp.
coscad plan spec.assemble [--png]
Assembly instruction generator. From the ‘asm’ placement, part hints, and ‘fastener’ and ‘joint’ lines, meshes every part and measures how each can come in (swept against every other part), derives the requires-before graph (T-nuts before the slot they need is covered, host and clamped part before each screw, a part before anything that would block its every way in), checks every step for support and balance on the real contact patches, a clear path and finger room, driver access, nut entry and screw joints turning freely, counts the hands each step needs, and searches rest-face phases with weighted cost terms so the build needs few flips and few extra hands. Writes spec_plan.md (bill of materials, T-nut preload sheet, numbered steps with torque per host material), spec_plan.json, and one spec_stepN.scad scene per step (this step orange, earlier parts grey, screw heads red, a grey bench slab); --png also renders them to 1200 by 900 PNGs with a built-in CPU renderer: native meshes, cached across steps, orthographic framing, depth testing, lighting and antialiasing. No GPU or display is needed. The screw-head proxies, part highlights and rest-face bench are drawn too. The step scenes are also written as OpenSCAD source exports. A failed render fails the command instead of publishing a successful plan with missing images. Design errors (colliding screws, too little thread reach, a clamp not on its host face) are reported and exit 2. See ASSEMBLIES.
coscad calibrate times.json
Fit the plan cost weights to timed builds. times.json lists plan JSON files and the seconds each step took (‘{"builds": [{"plan": x_plan.json, step_seconds: [40, null, ...]}]}’); the weights that best explain the times (non-negative least squares on each step's cost terms) are printed as a ‘plan’ line, in seconds. Terms no timed step used keep their defaults.
coscad site dir spec.assemble ...
Companion site: runs plan with renders for every assembly and writes a static, phone-first site under dir: one page per build (step cards with images, rest face, parts, T-nut preloads, screws with torque, warnings; prev/next navigation, filter within the build) and an index that searches across builds by name, part, material, or screw spec. Plain HTML, no server needed: open dir/index.html from a file or host the folder anywhere.
coscad doctor
Report the linked geometry kernel and the image renderer, and render a boolean test part natively against its known volume. Exit status 1 if the round trip disagrees.
, --help
Print the version, or the command list with a language cheat sheet.

coscad reads no environment variables for rendering: geometry and images need nothing outside the binary. The test suite (not the installed command) uses COSCAD_OPENSCAD and COSCAD_BOSL2 to find OpenSCAD and BOSL2 for its optional oracle checks.

One definition per line, ‘name = expression’. Definitions resolve by dependency, not order. Comments start with ‘//’. A line beginning with ‘|>’ continues the previous definition. The first code line may select a vocabulary: ‘!glyph’ (glyph operators), ‘!simple’ (ASCII words such as ‘Box’, ‘Translate.x’, ‘Hull’, ‘*’ for union and ‘-’ for difference), or neither, which accepts both.

A definition whose right-hand side is arithmetic is a numeric binding. Numbers may reference numbers, in any order, and are usable wherever a number goes. Argument positions take one atom (a literal, a name, ‘-name, or a parenthesized expression’) so juxtaposed arguments stay unambiguous:

w = 20
t = w / 5 - 1
main = box w (w / 2) t |> cutat top 0 0 (-t / 2) (zcyl 1 50)

■ s cube, corner at the origin (OpenSCAD)
▬ x y z box, corner at the origin
● r sphere
◎ r h cylinder, z from 0 to h
▻ r h cone
⎏ n r h n-sided prism
cube s / box x y z centered cuboid (BOSL2)
sphere r / cyl r h centered sphere, cylinder
xcyl r l / ycyl / zcyl axis-aligned centered cylinders
tube or ir h centered tube
torus R r torus
wedge x y z right-triangular prism, profile in the YZ plane
▣ x y z c cuboid, chamfer c
◙ x y z r cuboid, rounding r (< half the smallest dimension)
⌭ r h c / ⌽ r h r2 cylinder with chamfered / rounded ends
⊚ or ir h tube
⏢ x1 y1 x2 y2 h prismoid
◉ R r torus
⊿ x y z wedge

‘△ r’, ‘⬠ r’, ‘⭘ r’ (triangle, pentagon, circle) and ‘✎ x0 y0 x1 y1 ...’ (a closed piecewise-cubic bezier outline, 3k+1 control points, evaluated by the compiler). Extrude with ‘⮕ h profile’ or ‘|> extrude h’; revolve with ‘⟲ deg profile’ or ‘|> revolve deg’, with an optional segment count ‘⟲ deg segs profile’; the profile's y becomes height and only the +X half is kept. The ‘.scad’ export intersects a crossing profile with its +X square first, since OpenSCAD refuses crossing profiles. Offset with ‘profile ↯ ● r’. A profile used where a solid is expected, or the reverse, is a compile error.

‘loft z0 p0 z1 p1 ...’ (glyph ‘⟰’, ‘Loft’ in !simple) skins 2D profiles at ascending z into one solid, triangulated the way BOSL2's () does it. Profiles may differ in vertex count. ‘p0 |> loft z1 p1’ starts a loft with ‘p0’ at z = 0; further ‘|> loft z p’ stages append. Profiles must be single closed outlines: the 2D primitives, beziers, and their in-plane transforms (χ ψ ω ⬈ ⇋ ↯); booleans between profiles, ζ, θ, and ϕ are rejected.

‘⊕’ union, ‘⊖’ difference (left minus right), ‘∩’ intersection, ‘⇓’ hull, ‘⊞’ minkowski. Left-associative, one precedence level.

‘χ ψ ζ d’ translate along x/y/z; ‘θ ϕ ω deg’ rotate about x/y/z (about the origin); ‘⬈ sx sy sz’ scale; ‘⇋ nx ny nz’ mirror; ‘⚓ anchor’ re-origin a shape at one of its own anchors. A transform applies to the next primary expression; use parentheses or ‘$’ (‘χ 5 $ a ⊕ b’) for a compound argument.

‘a |> stage |> stage ...’ applies postfix stages: ‘x y z d’, ‘move dx dy dz’, ‘rotx roty rotz deg’, ‘scale’, ‘mirror’, ‘extrude h’, ‘revolve deg [segs]’, ‘loft z p’, ‘anchor v’, ‘add cut isect hull mink shape’, and the relational stages ‘at v [dx dy dz] child’ (union; the child's opposite anchor meets anchor v), ‘on v [dx dy dz] child’ (union; child rotated so +Z points along v, bottom mated there), and ‘cutat v [dx dy dz] cutter’ (difference; the cutter is centered at the anchor). Anchors are bounding-box points of the shape so far: ‘top bot lft rt fwd bak ctr’, combinable as ‘top+rt’ or ‘lft+fwd’. Glyph forms: ‘a ⌖ v b’ positions, ‘a ⋈ v b’ attaches.

plate 250 250 6 180 brim=8           // bed width depth [margin [max height]] [brim=N|auto]
clearance 0.15                       // minimum gap for `coscad check`
tolerance 0.00001                    // loop closure: mm [rotation] (defaults 1e-6, 1e-8)
gap = 2                              // numbers work here too
center ← center.coscad ×1
larch  ← l_arch.coscad ×1 ▽top seam=rear
rail   ← rail.coscad ×0              // ×0: premade, assembly-only
corner ← corner.assemble ×2          // recursive subassembly
asm = center ⊕ (larch |> move (55 + gap) -13 -7) ⊕ rail

‘▽anchor’ declares which face goes on the bed; undeclared parts get the automatic orientation search in next. ‘key=value’ entries are free hints carried into the manifest. The optional 4th ‘plate’ argument is the tallest part the printer builds in mm (default 250; 180 for an A1 mini). next never stands a part on a face taller than that, and exits 2 when a declared ‘▽’ or every face is too tall; the design stage only warns. The packer turns parts 90° about Z when that saves a shelf or a plate (recorded as ‘rot_z’ in each placement) and fills gaps with later, smaller parts before it opens another plate.

‘fixed’ poses and the ‘roll=’ / ‘gap=’ options of ‘mate’ take numeric atoms: a literal, a numeric binding, ‘-w’, or ‘(w / 2)’. ‘tolerance mm [rotation]’ sets how far a mating loop may miss and still close. A frame inside a constrained subassembly is addressed through instance aliases: ‘mate l @a s @q.b’. ‘brim=N’ reserves an N mm brim around each instance when packing; ‘plate … brim=N’ reserves it in next for parts whose printability recommends a brim (small first layer, tall and narrow); ‘brim=auto’ reserves 5 mm. A part's own ‘brim=0’ opts out. For plan, the hints ‘profile=2020’ (a T-slot rail: four slot faces, ends open unless ‘ends=open,blocked’), ‘material=aluminium|printed|steel’ (torque class; printed parts default to printed, premade ‘×0’ parts to aluminium), ‘torque=Nm’, and ‘mass=g’ describe the parts, and one line per screw group describes the joints:

fastener M5x10 br#1 rx#1 bot at=30          // spec clamped host face
fastener M5x10 px ry#2 rt ×2                // two screws along the overlap
fastener M5x10 br1 railA top through=6      // counterbored: 6 mm under the head
fastener M3x10 corner#1 bar#1 top nut=none  // threads into the printed bar
fastener peg strut#1 corner#1 rt            // friction joint, no screw
plan beam=30 rest=bot,top                   // optional search settings

Instance names take ‘#n’ when a part is placed more than once. The part hint ‘after=instance[,instance...]’ requires those instances to be placed first (for example, before a cap covers a rotor's entry). Unknown, ambiguous, and cyclic references are errors. Options: ‘×n’, ‘at=mm[,mm]’ (from the host's marked end), ‘nut=dropin|slidein|hex|none’, ‘pocket=face’ (hex nuts: the host face the nut pocket opens on; ‘pocket=clamped.face’ for a pocket in the clamped part), ‘head=button|socket’, ‘through=mm’, ‘inset=mm’ (peg joints only: seating plane this far inward from the named host face), ‘torque=Nm’. The ‘plan’ line takes ‘beam=N’ (search width, default 30), ‘rest=faces’, and the cost weights ‘flip vertical driver shrink sibling loose’.

For a screw located by model frames, replace the host face with ‘@hole’ and use ‘seat=@head_seat’ on the clamped part. The hole and head frames must be coaxial, with matching outward normals. With ‘nut=hex’, ‘pocket=@nut_seat’ names the host nut bearing seat (or ‘pocket=clamped.@nut_seat’ for a seat in the clamped part), with its normal opposing the head. Thread reach is computed from the distance between bearing seats. Frame-based joints take one screw per declaration, without ‘at=’, ‘through=’, or ‘inset=’.

The part hint ‘preload=hand’ loads nuts into the independent part before placing it. The default ‘preload=placed’ loads nuts after placement and checks access against the bench and other parts.

coscad exits 0 on success and 1 when a command could not run: a usage error, a missing file, a syntax or geometry error, or a kernel failure. It exits 2 when the input is well formed but the design has a problem, so a pipeline can tell "fix the model" from "fix the setup": validate and plan with design errors (colliding screws, too little thread reach, a clamp off its host face), check when any overlap is found, and next --strict when any part has a printability warning, and the design stage and next when a part does not fit the plate (its footprint either way round, or, in next, its height over the plate's max height). Every command that loads an assembly (validate, the design stage, next, plan, check, site) also exits 2 when its mating constraints contradict each other (a loop that does not close, or two fixed roots that disagree), when a part is reached by no fixed root, or when an instance is fixed twice. Unknown instances or frames, syntax errors and non-finite values in constraints still exit 1. doctor exits 1 when the native round trip fails.

Every diagnostic goes to standard error as ‘Error: file:line:col: reason’, optionally followed by a source excerpt with a caret; design errors from plan read ‘Error: file.assemble: design: reason’. A constraint conflict is reported at the constraint declared last on the loop and is followed by one ‘ note: file:line:col: constraint’ line for every constraint on the loop, any of which may be the wrong one; a floating part is reported at its declaration. Progress and results go to standard output.

A bracket built relationally, then rendered:

// bracket.coscad
plate  = box 5 15 20
flange = box 16 15 5
main = plate
  |> at top 5.5 0 -5 flange
  |> cutat lft 3.5 0 0 (xcyl 2.7 12)      // M5 clearance
  |> cutat top 2.5 0 -4 (zcyl 1.4 10)     // M3 pilot

$ coscad stl bracket.coscad
Wrote bracket.scad
Wrote bracket.stl: 664 triangles, volume 2638.931 mm^3, bounds ...

A funnel from numeric bindings and lofts:

r_top = 20
r_bot = 8
h = 40
wall = 2
outer = loft 0 (⭘ r_bot) h (⭘ r_top)
inner = loft -1 (⭘ (r_bot - wall)) (h + 1) (⭘ (r_top - wall))
main = outer ⊖ inner

Generate build instructions for a 2020 frame:

$ coscad plan cube.assemble --png
plan: 29 part instances, 38 fasteners on 12 hosts
Wrote cube_plan.md (29 steps, 4 flips, cost 183)
Wrote cube_step1..29.scad and .png

Manufacture a three-part assembly and check its fits:

$ coscad next bow3.assemble
Wrote bow3_bed1.stl (3 parts)
Wrote bow3_manifest.json
$ coscad check bow3.assemble
check: 3 parts, clearance = 0.0mm
contact  center x larch
contact  center x rarch
check: 0 overlaps, 0 clearance warnings

Check the binary on a new machine:

$ coscad doctor
geometry    native   CoScad tessellation + Manifold 3.5.2 kernel, linked statically (pinned by native-kernel.lock.json, with CrossSection)
images      native   built-in CPU renderer for plan --png and site (no display or GPU needed)
external    none     OpenSCAD and BOSL2 are not used; `coscad part.coscad` still exports .scad for OpenSCAD users
trip native ok       box 10 10 10 ⊖ zcyl 2 20 -> 874.67 mm^3 (expected 874.67), 216 triangles
all good

Errors carry ‘file:line:col’ and quote the offending line. Distinguished cases: syntax errors, undefined names, circular dependencies (only the cycle is listed), duplicate definitions, a number used as a shape or a shape as a number, 2D/3D mismatches (extruding a solid, offsetting a solid, combining a profile with a solid, a 2D ‘main’), and invalid loft profiles. A kernel failure or an unsupported operation is an error, never a silent fallback.

The full language reference, the topological-modelling guide, and the manufacturing guide ship as LANGEXTENSION.md, TOPOLOGICAL.md, and MANUFACTURING.md under the documentation directory (share/doc/coscad with Homebrew). The exported .scad files open in OpenSCAD with the BOSL2 library: https://openscad.org and https://github.com/BelfrySCAD/BOSL2.

Jasen Qin

 macOS 27.0 October 29, 2026 COSCAD(1)