Tutorial 1 — Your first diagram
Goal: starting from an empty repository, get a diagram you can read in the browser, change it in TypeScript and see the change, and export a PNG you can commit.
You will type every command. Nothing here explains why the pieces work the way they do — for that, read How a diagram becomes a picture afterwards.
Time: about ten minutes.
Before you start
Install the CLI. One line, no Node required (macOS and Linux):
curl -fsSL https://raw.githubusercontent.com/Ferroman/diagc/main/install.sh | sh
With Node 22 or newer already set up, npm works too:
npm i -g @diagc/cli
Check it with diagc --help. Nothing in this tutorial needs a checkout of diagc, or a package manager in your project.
Then make an empty repository to work in:
mkdir shop-diagrams && cd shop-diagrams
git init
Step 1 — Create a diagram
diagc init shop
✓ .diagrams/src/shop.diagram.ts (basic starter)
✓ .gitignore (+ .diagrams/.artifacts/, .diagrams/html/, .diagrams/diff/)
✓ compiled -> .diagrams/.artifacts/shop.diagram.json
Next:
diagc studio look at it
diagc guide how to write diagrams (for you or your agent)
diagc init --agents point coding agents at the guide (AGENTS.md, Claude Code skill)
Three files, one of them yours. Open .diagrams/src/shop.diagram.ts:
import { model } from '@diagc/core';
// A starter: a customer and one system holding two services and a database.
// `contains` nests, `relate` draws an arrow. The system rests folded, with the
// arrows into it bundled, until you double-click it.
const m = model('shop', { name: 'Shop' });
const customer = m.node('customer', { type: 'person', name: 'Customer' });
const shop = m.node('shop', { type: 'system', name: 'Shop' });
const web = m.node('web', { type: 'service', name: 'Storefront' });
const api = m.node('api', { type: 'service', name: 'Orders API' });
const db = m.node('orders-db', { type: 'database', name: 'Orders DB' });
shop.contains(web, api, db);
m.relate(customer, web, { kind: 'sync', label: 'Browses' });
m.relate(web, api, { kind: 'sync', label: 'Places orders' });
m.relate(api, db, { kind: 'writes', label: 'Order rows' });
export default m;
m.node declares a box, .contains nests boxes inside another, and m.relate draws an arrow. The export default at the end is what the compiler looks for.
The compiled file, .diagrams/.artifacts/shop.diagram.json, is the artifact — your model, validated. Open it if you like; it is plain, readable JSON. You never edit it by hand, and init has already gitignored it.
If you break the source — a relation pointing at a node that does not exist, say — the compile fails with a message naming the problem, and no artifact is written. That is the point: a diagram that cannot be drawn correctly never reaches the browser.
Step 2 — Look at it
diagc studio
Your browser opens on the studio (if it does not, open the address the terminal prints — normally http://127.0.0.1:5173). If shop is not already showing, pick it from the diagram picker in the top bar.
You will see Customer, and a Shop box with a dashed border and a badge counting what is inside. That is a folded group: the storefront, the API and the database are in there, but the diagram rests folded so you see structure before detail. The arrow from Customer lands on the group.
Double-click the Shop box. It unfolds and the view glides into it, revealing Storefront, Orders API and Orders DB with the arrows between them. Double-click empty canvas to fit the whole diagram again.
This is semantic zoom, and it is the reason a large diagram stays readable. You will meet it properly in What you see is not what is stored.
The diagram carries a read-only chip: it is TypeScript, so the file is where you change it. Leave the studio running — it is also recompiling your sources whenever they change.
Step 3 — Change it
Add a second service to shop.diagram.ts, above the export default line:
const worker = m.node('worker', { type: 'service', name: 'Fulfilment' });
shop.contains(worker);
m.relate(worker, db, { kind: 'reads', label: 'Open orders' });
Save the file. The terminal running the studio prints a ✓ line as it recompiles. Reload the browser tab — the studio reopens shop — and double-click Shop again: Fulfilment is there with its arrow to the database.
Step 4 — Export a picture
Stop the studio (Ctrl+C) and run:
diagc publish
This writes two kinds of output:
.diagrams/html/shop.html— a self-contained interactive page. Open it in a browser: it has the same fold/unfold behaviour and needs no server. Anindex.htmlbeside it lists every diagram..diagrams/static/shop.png— a flat image, sized to your diagram, with every group unfolded.
Embed the PNG in any markdown file:

If you see
No Chrome found: the PNG step drives a real browser. Install Chrome or Chromium, or pointCHROME_PATHat an existing binary.diagc publish --no-imagesskips PNGs and still writes the HTML.
What you have now
.gitignore written by init — commit it
.diagrams/
src/shop.diagram.ts you changed this — commit it
.artifacts/shop.diagram.json generated, gitignored
html/shop.html generated, gitignored
html/index.html generated, gitignored
static/shop.png generated — commit this one
Next
- Tutorial 2 — Draw one in the browser, which produces the same kind of model without writing TypeScript.
diagc guide— how to write diagrams, printed by the CLI you just installed: the DSL, the vocabularies, and one topic per diagram type.diagc init shop --type c4would have started you from a C4 diagram instead.- Write diagrams with a coding agent — what the
diagc init --agentsline is for. - Add diagc to an existing repository — what to commit, editor types,
diagc lintin CI. - Author diagrams in TypeScript for the rest of the DSL: descriptions, metadata, colours, styles.
- Builder API reference for every method and option.