Start
Quickstart
From nothing to a full sheet set in about a minute.
Write a model
One JSON object per line. Save as cabin.ndjson.
{"t":"project","id":"cabin","name":"Cabin","unit":"m"}
{"t":"footprint","id":"fp","x":0,"y":0,"w":7.2,"d":5.4}
{"t":"level","id":"g","name":"Ground","elev":0,"height":2.7}
{"t":"room","id":"main","label":"Living","use":"living","level":"g","x":0.3,"y":0.3,"w":6.6,"d":4.8}
{"t":"wall","id":"n","level":"g","side":"north","type":"exterior","thickness":0.3}
{"t":"wall","id":"s","level":"g","side":"south","type":"exterior","thickness":0.3}
{"t":"wall","id":"e","level":"g","side":"east","type":"exterior","thickness":0.3}
{"t":"wall","id":"w","level":"g","side":"west","type":"exterior","thickness":0.3}
{"t":"opening","id":"d1","on":"s","kind":"door","at":"center","width":0.9,"room":"main"}
{"t":"opening","id":"w1","on":"n","kind":"window","at":"center","width":1.8,"sill":0.9,"head":2.3}
{"t":"roof","id":"r","type":"gable","ridgeAxis":"x","pitch":0.45,"overhang":0.4}Draw it
npx bimtex cabin.ndjson -o drawings/A-101.svg
A-201.svg
A-301.svg
A-401.svg
Plans, four elevations, two sections and axonometrics — all derived, none
authored. Add a parcel and you also get A-001; add a second room and you
get the A-501 egress plan.
Or call it directly
import { compile, parseNdjson, drawPlan, drawElevation, validate } from 'bimtex';
const { entities } = parseNdjson(source);
const model = compile(entities, { detail: 1 }); // 0 MASSING · 1 STANDARD · 2 DETAILED
drawPlan(model, model.levels[0]); // SVG string, cut at +1.20 m
drawElevation(model, 'south'); // the elevation with the door in it
validate(model); // { ok, diagnostics }drawPlan returns an SVG fragment. There is no canvas, no headless browser and
no 3D engine anywhere in the drawing path.
What you just made
Compiling drawing set…
The ridge height on that section was never typed. It is
eave + (halfSpan + overhang) × pitch — derived, because a ridge that
disagrees with its eaves is exactly the contradiction this format exists to
prevent.