Appearance
Scripting & automation
CivilKit Studio can be driven from code through window.CivilKit - load a model, solve it and read the results without clicking. It runs right in the page: the browser console, or a page that embeds Studio. Nothing to install.
Three ways to run it
The same window.CivilKit API is reachable three ways - use whichever fits:
- The in-app Console tab - the results panel has a Console tab with a code box, a Run button and ready-made examples. No devtools needed;
CivilKit, the rawfeminternals, amodelsnapshot andprint()are already in scope, and top-levelawaitworks. - The browser devtools console - press F12 (or right-click → Inspect) → Console, type
CivilKit.version, and if you get a version string back the API is live. - Headless, from any agent or CI - drive a real (invisible) Studio, with screenshots, over MCP or a one-shot CLI. See Headless & MCP.
Wait until it's ready
Right after a page load the solver may still be booting. Gate a script with CivilKit.isReady() (it never throws) before calling anything else. The in-app Console already does this for you.
A curated, client-side surface
window.CivilKit is a v1 surface: a stable, curated subset of what Studio can do, aimed at the common "build → load → solve → read" loop. In the browser every call runs client-side (the solver runs in your page); the headless engine runs the identical API in a headless Chromium. There is no remote REST endpoint - the raw, larger internal API lives on window.__fem if you need to go deeper.
The methods you'll use most
A handful of calls cover almost everything. See the full Scripting API reference for the complete list, arguments and return shapes.
| Method | What it does |
|---|---|
loadSample(key) | Load a built-in gallery model, e.g. 'prattTruss', 'steelPortalReal' |
loadModel(model) / getModel() | Load a model object / read the current one (a clone) |
validate() | Run the model health check; returns a list of issues (empty = clean) |
solve() | Run the analysis (returns a Promise with a results summary) |
assignSection(members, name) | Assign a real catalogue section (e.g. '310UC158') to members |
windLoads(opts) / seismicLoads(opts) | Add AS/NZS 1170.2 wind / AS 1170.4 seismic as load cases |
loadCombinations() | Generate the code load combinations |
report() | Build the calc report HTML (same as File → Report) |
getResultsSummary() | Compact summary of the last solve, or null |
getMemberUtilisations() | Per-member governing utilisation (the design table numbers) |
getReactions() / getDisplacements() | Reactions / nodal displacements for the active load case |
on(event, cb) / off(event, cb) | Subscribe / unsubscribe to 'solved' and 'modelChanged' |
Load a sample, solve, read peak utilisation
This is the bread-and-butter loop: pick a model, solve it, and find the hardest-working member.
js
await CivilKit.loadSample('steelPortalReal');
const summary = await CivilKit.solve();
console.log('Solved:', summary);
// Governing utilisation per member (1.0 = at capacity).
const utils = CivilKit.getMemberUtilisations();
const worst = utils.reduce((a, b) => (b.gov > (a?.gov ?? -1) ? b : a), null);
console.log(`Worst member: M${worst.memberId} u=${worst.gov.toFixed(2)} (${worst.status})`);getMemberUtilisations() returns null if nothing has been solved yet, so always solve() first. Each entry carries gov (the ratio), govBy (the governing clause), status ('OK' / 'OVER') and the design code.
React to every solve (the 'solved' event)
Subscribe to 'solved' to run something automatically each time the model is solved - handy for a live dashboard or a logging script. The callback receives { analType, dt } (analysis type and solve time in ms).
js
const off = CivilKit.on('solved', ({ analType, dt }) => {
console.log(`solved (${analType}) in ${dt} ms`);
const utils = CivilKit.getMemberUtilisations() ?? [];
const overs = utils.filter(u => u.status === 'OVER').length;
if (overs) console.warn(`${overs} member(s) over capacity`);
});
await CivilKit.solve(); // the handler above fires
off(); // unsubscribe when you're doneon(...) returns an unsubscribe function; you can also call CivilKit.off('solved', cb) with the same callback. The other event, 'modelChanged', fires after any edit (programmatic or interactive) and is a good cue that the current results are now stale.
Get the model and export it
To capture the current model - for a snapshot, a diff, or re-loading later - read it with getModel() (a clone, so editing it won't touch Studio) or serialise it with exportJson().
js
const model = CivilKit.getModel();
console.log(`${model.members.length} members, ${model.nodes.length} nodes`);
const json = CivilKit.exportJson(); // re-loadable snapshot
// later: CivilKit.loadModel(JSON.parse(json));For BIM/CAD deliverables there are exportModelIfc(), exportJointsIfc() and exportDxf() - all covered in the reference. For the design numbers themselves, the same utilisations also drive the on-screen analysis and results.
Design a real building end-to-end
The scripting API isn't just "load → solve → read" - it drives the whole design workflow. Given a model (built by hand, imported, or generated), you can assign real catalogue sections, apply the AS/NZS code loads, generate the combinations, solve and check to AS 4100, and produce the report - all in one script.
js
// Real catalogue sections instead of placeholders
await CivilKit.assignSection(columnIds, '310UC158'); // ids from getMembers()
await CivilKit.assignSection(beamIds, '610UB101');
// Code loads as named cases (the built-in AS/NZS engines)
CivilKit.windLoads({ region: 'A', terrain: '3', direction: 'x' }); // AS/NZS 1170.2
CivilKit.seismicLoads({ zone: '0.08', soil: 'De' }); // AS 1170.4
// Combinations from the case types, then solve + check
CivilKit.loadCombinations(); // -> 1.35G, 1.2G+1.5Q, 1.2G+Wu+psiCQ, G+Eu+psiCQ, ...
const summary = await CivilKit.solve();
console.log(summary.memberCapacities, 'members checked to AS 4100');
// The stamped calc report (title block, combinations, member design, results)
const html = CivilKit.report();For combinations to come out right, each load case needs a type: windLoads and seismicLoads tag their own (Wind / Seismic); give your Dead and Live cases a load_case_type (0 Dead, 1 Live) when you build the model. This is exactly how examples/office-building.mjs raises and checks a full 10-storey office from a script.
Read more
This page is the friendly tour. Every method, its arguments, return shape and edge cases live in the Scripting API reference.