Designing a house with the Wadi DSL: an author's guide
This is a task-oriented guide to writing .wdl, the Wadi Design Language, and
driving the editor to turn one text file into a live 3-D house plus 2-D floor
plans, elevations, roof details, and quantities. It is written for the author:
the architect (or coding agent) who describes the building.
New here? Read §1 to §5 in order and you can model a real house. The later sections (parametrics, components, libraries, the configurator) are how you make that house reusable and adjustable.
Companion chapters: the concept (how Wadi uses a DSL), components & libraries (reuse in detail), and the parametric method (the method behind it). New to the whole thing? Start at the documentation index.
Contents
- What you're writing
- Your first house
- The house skeleton: convention, units, site, defaults, coordinates
- Floors: the vertical stack
- Rooms, walls & openings
- Structure: slab, beam, pillar, plinth, ground
- Circulation & fittings: staircase, kitchen
- Furniture: items & assets
- Roofs: flat, shed, gable, hip
- Going parametric: variables, points, grids, formulas
- Components: design once, stamp many
- Libraries: import & reuse across files
- The configurator: knobs for the home-owner
- Layers & the raw escape
- Using the editor: completion, hover, go-to-def, rename, live preview
- Sharing & exporting
- Publishing a template: cover shots, configurator test, publish
- Good practice & gotchas
- Quick reference
1. What you're writing
A .wdl file is a complete, human-readable description of a building. You type
it in the WDL editor (the web app at wadi.house/dsl, or the desktop app's
Window → DSL editor, ⌘⇧D) and the model rebuilds live in the pane beside you: a
3-D view, floor plans, elevations, roof details, and a quantities estimate.
One file is the single source of truth. From it Wadi produces every drawing and the 3-D model, so you never keep them in sync by hand. Because the file is plain text, you can version it, diff it, share it, and let a coding agent write or edit it.
Under the hood the editor compiles your .wdl to a .wadi model (JSON) and
feeds it to the Wadi renderer. You mostly won't see the .wadi, but you can
download it, and you can import a .wadi back into editable .wdl (§16).
2. Your first house
Paste this into the editor. It is the smallest thing that renders:
house MyFirstHouse {
convention center
units feet_inches per_unit 10
site { plot (300, 300) }
defaults { floor_height 116 wall_height 108 slab_thickness 8 wall_thickness 8 }
floor 1 "Ground" {
room Living at (0, 0) size (240, 200) {
wall north east south west
}
}
}
You get a single room, enclosed on all four sides, on one floor. That is a valid house. Everything else in this guide adds to this skeleton.
A few things to notice already:
house Name { … }wraps everything.- Numbers are in project units, and with
per_unit 10there are 10 units to a foot, sosize (240, 200)is a 24 ft × 20 ft room. - The room is placed at
(0, 0)and sized(240, 200). X runs right, Y runs down (top-left origin, like a floor plan on paper). - A bare
room { }with nowalllines is enclosed on all four sides. Here we listed the walls explicitly,wall north east south west, which is the same thing.
3. The house skeleton
Every house begins with a few settings. Here they are, annotated:
house CoastalCottage {
convention center // how room coordinates are interpreted (see below)
units feet_inches per_unit 10 // display: label drawings in feet-inches; 10 units = 1 ft
site { plot (600, 700) ref (0, 0) } // plot is 60 ft × 70 ft; ref = the origin corner
defaults { // house-wide fallbacks, overridable per floor/object
floor_height 116 // floor-to-floor rise
wall_height 108 // standing wall height
slab_thickness 8 // deck thickness
wall_thickness 8 // wall thickness (also drives the center convention)
}
// …vars, points, grids, configurator, layers, floors…
}
The coordinate system (read this once)
- X → right, Y → down. Think of the floor plan as you would draw it on paper: origin at the top-left, X increases rightward, Y increases downward.
- Height is a separate axis.
floor_height,wall_height,z_offset, and a roof'sslope heightlive on the vertical axis. They never mix with X/Y. - Units are abstract. Geometry is stored in plain "project units". The
unitsblock only decides how numbers are labelled on drawings. Withper_unit 10, a wall drawn108units tall is annotated 10'-9". If you change the display unit, the model does not resize. Storage units and display units are independent.
convention center vs convention outer (important)
This decides what a room's at (x,y) size (w,l) means:
center(use this): the numbers are wall centrelines. Two rooms that share a wall use the same coordinate on their shared edge, so they abut on a line, and Wadi grows each footprint outward by half the wall thickness to the real outer face. You never do wall math.outer(legacy): the numbers are the outer wall face. Adjacent rooms must overlap by a wall thickness, so you are constantly adding and subtracting thicknesses. Avoid it in new designs.
With center, a two-room house is as simple as:
room Living at (0, 0) size (200, 200) { wall north south west }
room Bedroom at (200, 0) size (150, 200) { wall north south east }
// they share the line X=200, no overlap, no arithmetic
defaults and per-floor/per-object overrides
defaults are the fallback dimensions. Any floor can override height,
wall_height, slab_thickness; any object can override its own dimensions. You
only specify what differs from the defaults.
4. Floors
A house is a stack of floors. Array order is the physical stack: the first
floor you write is the bottom.
floor 0 "Plinth" height 40 { … } // a low base floor
floor 1 "Ground Floor" { … } // sits on top of floor 0
floor 2 "Loft" { … } // and so on
floor <number> "<Name>": the number is a label/id; the order in the file is what stacks.- Optional per-floor overrides, in this order:
height N,wall_height N,slab_thickness N. - The plinth floor. By convention floor
0is a low "Plinth" floor carrying the terrain (ground) and the raised base (plinth); give itheightequal to the plinth height (see §6). The floors above it are the living levels.
Inside a floor is a list of objects: rooms, walls, structure, furniture, roofs, component instances. The rest of §5 to §11 is that vocabulary.
5. Rooms, walls & openings
The room is the main object. It is a rectangle that grows its own walls.
room Living at (main.x1, main.yA) size (200, 200) {
wall west // a plain exterior wall
wall north { window LivN at 55 size (55, 55) sill 35 } // a wall carrying a window
wall south { door LivDoor at 60 size (32, 80) } // a wall carrying a door
// (east omitted → that side is left open, e.g. a verandah)
}
Rules of thumb:
- A bare
room { }is enclosed on all four sides. As soon as you write anywallline, you get exactly the sides you list, so list them all if you want a fully closed room. - List plain walls compactly:
wall north east southon one line. - Give a wall its own
{ … }block only when it carries an opening (door or window). One side per block. - Omit a side to leave it open. That is how you model a verandah or a sit-out.
Doors & windows
Openings live inside a wall's block:
wall south { door Main at 120 size (36, 84) [open] }
wall west { window W1 at 100 from end size (60, 50) sill 35 [open] }
at <offset>is the distance along the wall to the opening.from start|center|end(defaultstart) picks which end<offset>is measured from, so the opening keeps its place when the wall or room scales — no formula needed.start: from the wall start to the near edge (the default).end: from the wall end to the far edge (at 0 from end= flush to the end).center: a signed shift of the opening centre from the wall midpoint (at 0 from center= centred; the offset may be negative).size (width, height)is the hole.sill <s>(windows) is the height of the sill above the floor.openmarks it as an actual opening (a doorway or opening with no leaf) vs a closed door/window.
Free-standing walls
For a partition or garden wall that isn't a room edge, use a wall … from … to …:
wall Partition from (100, 0) to (100, 350) height 108 facing east
from/toare the two endpoints (centreline).height_end <h2>(optional) different fromheightmakes a sloping-top wall (e.g. a gable wall).facing <side>picks which face is the "front".- Free walls don't auto-mitre. Two walls that merely touch at a point leave a small notch. Overlap the endpoints (extend past the shared point by ≥ half the wall thickness) so the bodies fill the corner. Room walls handle their own corners.
6. Structure
The load-bearing and base elements. All of these use at (x,y) size (w,l) with the
top-left corner at (x,y) (same as rooms):
slab at (main.x1, main.yA) size (main.x3 - main.x1, main.yC - main.yA) // an RCC deck
beam name "Tie" at (0, 340) size (300, 8) height 8 // a horizontal member
pillar C1 at (main.x1, main.yA) size (10, 10) height 116 // a structural column
plinth name "Plinth" at (0, 0) size (300, 300) height 40 // the raised base
ground name "Ground" at (0, 0) size (600, 700) // the terrain plane
slab/beam/plinth/groundtake an optionalname, thenat/size, then an optionalheight(orthicknesson a slab).pillarneeds a name andat (x,y) size (w,l) height h. Itsatis the top-left corner (not the centre). To centre a column on a point(cx,cy)place it at(cx - w/2, cy - l/2). Where a pillar overlaps a wall, the wall auto-trims to butt against the column (2-D and 3-D), so you can lay columns on the grid without fighting the walls.groundis the site plane.plinthis the raised platform the house sits on. Put both on the floor-0 "Plinth" floor.
7. Circulation & fittings
Staircase
staircase name "Stair" at (208, 64) step (7, 11, 44) direction south climb up
total_height 116 max_run 90 turn clockwise
at (start_x, start_y): withclimb up, the bottom of the stair.step (rise, tread, width): one step's dimensions.direction north|south|east|west: the way it climbs.climb up|down(defaultdown): where the stair is anchored.climb up(recommended) is bottom-anchored — put the stair on the lower floor it rises FROM,atis the bottom step, and it ascends intodirection.climb down(the legacy default) is top-anchored — put it on the upper floor andatis the top; it descends. (See convention C5: aclimb downstair on the wrong floor can land below ground.)total_height <h>: the height to climb (usually the floor height); Wadi computes the number of steps.max_run <r>: optional. If set, the stair auto-switchbacks into multiple flights with landings whenever a straight run would exceedr. Tune withlanding_depth,landing_thickness,flight_gap, andturn clockwise|anticlockwise.
Kitchen platform
An L- or U-shaped counter, described as a polyline path of its inner edge:
kitchen name "Counter" path ((40, 40), (140, 40), (140, 120)) side right depth 24 height 36
path (…points…): the centreline of the run.side left|right: which side of the path the counter body is on.depth: counter depth.height: counter height.base_z: optional lift.
8. Furniture
Furniture is a GLB model dropped into the scene as an item.
From a pack (see §12 for import):
import "std-furniture" as f
// …then, on a floor:
item f."bed_double" at (60, 60) rotation 0 scale 1
Anchored inside a room (no x/y; placed relative to the room box):
room Bedroom at (0,0) size (200,200) {
wall north east south west
item f."bed_double" anchor bottom-right gap (12, 12)
}
A one-off model (inline asset, not in any pack):
item name "Sofa" asset { id "sofa" src "furniture/sofa.glb" dims (1.9, 0.8, 0.9) category "living" }
at (150, 300) rotation 90 scale 1
at (x,y)places by plan coordinate, or useanchor(inside a room) /anchor_to "Room" anchor center gap (gx,gy)to pin to a room corner/centre.rotation <deg>(yaw, 0 = facing south, 90 = east),scale <s>optional.dims (w, h, d)in metres. Wadi scales the GLB to those real dimensions.
9. Roofs
One roof object covers the whole roof. A type (flat / shed /
pitched) plus per-segment geometry gives you flat, shed, gable, and hip roofs.
The model: a roof is made of segments, each an axis line
(from→to) with a width centred on it. Roof coordinates are wall
centrelines, exactly like rooms. Author the segment on the same grid lines as
the walls and the roof auto-grows to the outer wall face; then overhang extends
beyond. Don't add half-wall fudge factors.
A gable roof (open ends), with a truss:
roof name "Gable Roof" pitched endpoint open slope angle 30 overhang 20 {
segment "seg0" from (150, 0) to (150, 400) width 300 gable_overhang (20, 20) tie_beams 2
truss "seg0" fink at (80, 200, 320)
}
Key knobs:
pitched | shed | flat: the roof type.endpoint open | closed: an open end is a gable (triangular end wall); a closed end is a hip (sloped triangle). Set the default here, or per segment withstart_endpoint/end_endpoint.- Pitch, pick one:
slope angle <deg>: symmetric pitch by angle;slope height <ridge_h>: by ridge height;slope angle (<left>, <right>): an asymmetric (saltbox) gable, two different pitches; eaves stay put, the ridge shifts.
overhang <o>: uniform eave; per-side overrides exist (overhang_left/right/start/end/low/high).slab_thickness,parapet <h> x <t>(flat-roof parapet),gable_wall_thickness.- Inside
{ … }:segment "id" from … to … width …(+ per-segmenthigh_side,hip_setback (a,b),gable_overhang (a,b),hip_ridge_extension (a,b),tie_beams N), andtruss "segId" fink|mono_pitch at (pos, …)for structural trusses at positions along the segment.
Choosing a roof by type: flat (add a parapet); shed
(single-slope, set high_side); gable = pitched + endpoint open; hip =
pitched + endpoint closed.
10. Going parametric
Everything so far is a fixed house. The next step is making it a parametric model: a design you can resize and vary. Four constructs do this, all optional.
Variables: the degrees of freedom
var wallT = 8
var pillarW = 10
var pilInset = (pillarW - wallT) / 2 // vars may reference each other
var roof_style = 3
A var is a named number (or a formula). These are the knobs your design turns on.
Points: named anchors that double as sizes
point House { x = 420, y = 470 }
Reference a point's coordinates in formulas as House.x / House.y, and, as a
convenience, the same values as a size: House.W (= x) and House.L (= y).
So one point House gives you both the origin math and the overall width/length.
Grids: the structural scaffold
A grid is a set of named centrelines per axis: X lines numbered 1,2,3…,
Y lines lettered A,B,C…, each positioned by a formula:
grid main {
x: 1 @ wallT / 2 role structural,
2 @ House.W / 2,
3 @ House.W - wallT / 2 role structural
y: A @ wallT / 2 role structural,
B @ House.L / 2,
C @ House.L - wallT / 2 role structural
}
Each line is published as a formula symbol: main.x1, main.x2, main.yA, … Now
place rooms, slabs, and columns by grid line, with no wall math:
room Living at (main.x1, main.yA) size (main.x2 - main.x1, main.yB - main.yA) { … }
slab at (main.x1, main.yA) size (main.x3 - main.x1, main.yC - main.yA)
pillar C1 at (main.x1, main.yA) size (pillarW, pillarW) height 116
Change House.W or the bay spacing and every room, slab, and column bound to
the grid re-flows together. Optional per-line thick <expr> (a "tartan" grid)
and role structural|planning tag lines for clarity.
Formulas: the universal hook
Any numeric field, anywhere, can be a formula instead of a literal. Just write an expression:
size (main.x3 - main.x1, main.yC - main.yA) // width/length as grid spans
height wall_height + slab_thickness // a derived height
enabled 1 - min(1, abs(roof_style - 2)) // a 0/1 gate (see below)
The formula language is small: arithmetic only:
+ - * / ( ) -unary
min max clamp round floor ceil abs
references: a var · a point (House.W) · a grid line (main.x3 - main.x1)
There are no if/comparisons; you emulate them with arithmetic. The idiom
1 - min(1, abs(x - N)) evaluates to 1 when x == N, else 0, which is how you
build on/off gates (next).
enabled: turn objects on and off
Every object accepts enabled <expr> in its common tail. enabled 0 (or a
formula that resolves to 0) removes the object from every view:
room Pooja at (…) size (…) enabled has_pooja { … } // optional room
Placement of the common tail. For most objects the tail (
z_offset/enabled/layer/material) comes at the very end. Aroomis the exception: because it ends with a{ … }block of walls, its tail goes before the block:room R at (…) size (…) enabled e layer "id" { … }.
Two idioms fall out of this:
- Optional parts: gate a whole room/feature on a
0/1variable. - Mutually-exclusive variants: put four roofs in the file, each gated
enabled 1 - min(1, abs(roof_style - N)), and exactly one renders for a givenroof_style. Aselectknob then swaps whole roofs with no code.
The common tail (every object accepts these)
… z_offset <expr> enabled <expr> layer "id" [material "id"]
z_offset lifts the object above its floor base (for split levels); layer
assigns it to a display layer (§14); material picks a material.
11. Components
When a sub-assembly repeats (a stair core, a bathroom, a bench, a verandah), promote it to a component: define it once, stamp it many times.
Define a component in its own local coordinates (origin 0,0), with
optional param knobs:
component Bench goal "a low bench to sit on" {
param blen = 60 label "Bench length" // a knob with a default
param bdep = 18
beam name "Top" at (0, 0) size (blen, bdep) height 6
}
Use (stamp) it onto a floor with use, optionally naming the instance,
rotating it, and overriding params (overrides use =, not :):
use Bench as "WindowBench" at (60, 60) rotation 90 with { blen = 80 }
at (x, y)offsets the component's local origin onto the floor.rotation <deg>(optional) turns the whole stamped assembly. Right angles (0/90/180/270) are exact for any component; a non-right angle is allowed only for a furniture-only component (a free angle on something structural is a compile error; arbitrary structural rotation is a future feature).with { p = v, … }overrides params; un-overridden ones fall back to defaults. A= formulaparam is evaluated in the host's scope, so it can read the house's variables.- A component expands byte-identical to writing its objects inline, so there is no runtime cost.
Components nest: a component may use another component and place
item furniture, including ones from a library it imports.
The goal "…" string documents intent and is the discovery key when browsing
libraries (§12).
12. Libraries
A library is a .wdl file whose top level holds component / asset
declarations (no house needed). Another file pulls it in with import and uses
its exports under a namespace.
house Home {
import "konkan/base" as kb // a component pack
import "std-furniture" as f // a furniture pack
floor 1 "Ground" slab_thickness 0 {
use kb.Verandah at (20, 20) with { across = 200 } // a component from the pack
item f."bed_double" at (60, 60) // an asset from the pack
}
}
import "name" as nsmakes the module's exports available asns.Comp(use) andns."assetId"(item). A bareimport "name"merges its exports un-namespaced.- Imports resolve transitively: a library may itself import libraries, and a
library component may
usea sibling or place furniture from its own imports.
Two bundled packs (always importable, no setup)
std-furniture: ~120 furniture assets →item f."bed_double"etc.konkan/base: goal-tagged Konkan parts:Stairwell,Verandah,Otla,Bathroom,Kitchen,TulsiVrindavan,Parapet→use kb.Verandah ….
Save & reuse your own libraries
The editor keeps a cache of loaded libraries that import resolves from, the
same on the web app and desktop. Open the 📚 Library toolbar menu:
| Action | What it does |
|---|---|
| 💾 Save current as library… | names the current file and puts it in the cache |
| 📂 Load library file… | loads one or more .wdl files into the cache |
| (the cached list) | click a name to insert its import line · ✎ open it · × remove it |
Resolution order: your cache → the bundled packs. If a file imports something
uncached, the editor names exactly what's missing so you can load it. On the
desktop app, any .wdl beside your open file (or in a modules/ subfolder) is
auto-loaded and importable by its basename.
See components & libraries for the full story.
13. The configurator
The configurator is where you, the architect, decide which variables a
home-owner may adjust, and within what bounds, turning your parametric model
into a bounded template. Each knob targets a var by name; turning it
writes a number into that variable and the whole house re-flows.
configurator {
title "Configure your home"
note "Everything re-flows to fit."
slider pillarW "Column size" ft [8 .. 14 step 1]
number plotDepth "Plot depth" ft // NB: `depth` is reserved, use plotDepth
toggle has_pooja "Include a pooja room"
select roof_style "Roof style" { Flat = 0, Shed = 1, Gable = 2, Hip = 3 }
group "Structure" note "advanced" {
slider wallT "Wall thickness" [6 .. 12 step 1]
}
}
slider target "Label" [unit] [min .. max step s] [note "…"]number target "Label" [unit] [note "…"]toggle target "Label" [note "…"]: a 0/1 switch (pair it with anenabledformula on the optional object).select target "Label" { Thin = 6, "10 ft (std)" = 100 }: labels are bare ids or quoted strings; each maps to the number the var is set to.group "Section" [note "…"] { …inputs… }sections the owner's panel.
To expose a plot dimension, model it as a
var, reference it from apoint(point House { x = W }), and target the knob atW. Knobs target vars, not point fields.
This is the architect → owner handoff: you ship the template; the owner opens it in the app and turns your knobs to fit their plot and taste, without touching the geometry.
14. Layers & the raw escape
Layers are display groups (show/hide, colour), visual only, never geometry:
layer "structure" "Structure" color "#8B5A2B" group "Frame"
// then, on any object:
pillar C1 at (…) size (…) height 116 layer "structure"
raw is an escape hatch to write a literal model object as JSON, for anything
without first-class syntax yet. Every model type now has its own syntax, so you
should rarely need it:
raw "some_future_type" { key: 1, nested: { a: 2 } }
15. Using the editor
The WDL editor is a full IDE for the language, the same at wadi.house/dsl and in
the desktop app (⌘⇧D).
Live loop. Type; the model recompiles and the pane beside you rebuilds: 3-D, Floor Plans, Elevations, Roof Details, Layout, Quantities tabs. A status pill shows ✓ rendered or the first error; parse/validation errors also appear as squiggles and in a Problems panel with line numbers.
IDE features (powered by the language itself, so they understand your imports):
- Completion: after
useit suggests in-scope components (in-file and from imported libraries, e.g.kb.Verandah); afteritemit suggests asset ids. Also keyword completion. - Hover: hover a
use kb.Compto see the component's goal + params; hover anitem f."id"to see the asset's dimensions. - Go-to-definition (F12 / ⌘-click): jump to a component's definition, in this file or into the imported library (which opens read-only).
- Find references (⇧F12): every place a component is used.
- Rename (F2): rename an in-file component and all its
usesites at once.
Toolbar: a sample picker (✨ New), Open / Save .wdl, ⬇ .wadi
(download the compiled model), 📚 Library (§12), ↩ Import .wadi (§16), and
📖 Reference, a built-in cheat-sheet of every construct.
16. Sharing & exporting
- Download the model: ⬇ .wadi gives you the compiled JSON model (what the renderer consumes; also what you would publish as a template).
- Import a
.wadiback to code: ↩ Import .wadi decompiles an existing.wadimodel into editable.wdl, so you can round-trip a model authored in the form UI (or an older template) back into the DSL. - Share a house: the Wadi app can encode a whole house into a share link
(no server needed) and open native
.wadifiles directly (open-in-app). - Save
.wdl: keep the source file itself under version control; it is the durable source of truth.
17. Publishing a template
A parametric .wdl becomes a home an owner can pick from the gallery and adjust.
The WDL editor's 📦 Publish template button runs the whole workflow in one
panel:
- Capture cover images. Opening the panel switches the live preview to the design view, which shows the capture toolbar. 📸 Shot grabs the current 3D frame; ✨ Auto grabs several angles plus a floor plan; 🗂 Shots manages the set and picks the cover. The first shot is the card cover.
- Test the configurator. Use Preview as owner in the preview to open the owner configurator and exercise each knob, so you ship a template whose adjustments you have actually tried.
- Fill in the details. Give the template an id, title, description, and style / roof tags. Bedrooms, bathrooms, floor count, and the adjustable badge are derived from the model. The panel shows a live gallery-card preview.
- Save it to your templates folder. The design is saved as one
self-describing
.wadi: its cover images and the details you typed live inside the file (atemplateblock), so the folder itself is the catalog. The desktop app writes it into your templates folder; the browser downloads it so you can drop it into an online folder (for example a shared Google Drive) with that platform's own tools. There is no index file to maintain and no upload step — the app lists the folder and indexes every.wadiin it, so a new file shows up in the gallery straight away.
The saved .wadi keeps its full parametric layer (variables, points, grids,
formulas, configurator), so the owner gets the same adjustable model you authored.
Keeping cover shots in your source. Screenshots are large images, so the .wdl
never stores them inline. If you maintain your template as .wdl source (not just
the published .wadi), use 🖼 Save cover shots to source in the Publish panel
(desktop): it writes the captured shots as PNG files in a thumbnails/ subfolder
next to your .wdl and adds a thumbnails line to the template {} block that
references them by relative path:
template {
title "Coastal cottage"
thumbnails "thumbnails/cottage-1.png", "thumbnails/cottage-2.png"
}
The paths are always relative to the .wdl, so the .wdl plus its thumbnails/
folder travel together. When you reopen the .wdl, the app reads those files and
re-inlines them into the compiled .wadi (and into every published copy). Move the
.wdl, and move its thumbnails/ folder with it.
Pointing the app at a folder. The gallery reads from a templates folder that
you can change: a local folder (desktop, managed in Finder), a shared Google
Drive folder (needs a Drive API key in the source settings), or the bundled
starter set. Drop .wadi files in, remove them, rename them — the gallery follows
the folder.
18. Good practice & gotchas
- Use
convention center. It removes all wall-offset arithmetic. - Author on a grid. Define a
gridfrom your plotpoint, then place rooms/ slabs/columns by grid line. Resizing then means changing one number. - Numbers, not formulas, in geometry when it's fixed. A formula is an overlay; a plain number always renders. Reach for a formula when a field should follow a variable/grid.
- Reserved words can't be
paramnames. Grammar keywords (width,depth,height,size, …) are reserved; name paramsacross,deep,tall,blen, etc. - Free walls don't mitre: overlap their endpoints at corners (§5). Room walls handle corners themselves.
- Furniture dims are metres; geometry is project units (10 = 1 ft). Don't mix.
- A house-less library shows a "no floors" notice in the preview; that is
expected; give it a demo
houseif you want to preview its components. - Structural conventions (the built-in linter). The editor's status pill and
check.shflag a growing set of conventions (C1-C10, plus per-primitive rules) — for example a plinth floor'sheightshould equal itsplinthobject's height (C1); exterior room sides should carry awall(C2); a floor with no slab should setslab_thickness 0(C3). Errors (e.g. C1, C3) failcheck.sh; warnings (e.g. C2, an intentional verandah) are advisory. The full, generated list is inreference/conventions.md.
19. Quick reference
// ── skeleton ─────────────────────────────────────────────────────────
house Name {
convention center // or: outer
units feet_inches per_unit 10
site { plot (W, L) ref (x, y) }
defaults { floor_height 116 wall_height 108 slab_thickness 8 wall_thickness 8 }
// vars, points, grids, configurator, layers, components, floors…
}
// ── parametric core ──────────────────────────────────────────────────
var name = <expr>
point Name { x = <expr>, y = <expr> } // ref: Name.x / Name.W / Name.L
grid g { x: 1 @ <expr> [thick <e>] [role structural|planning], 2 @ <e>
y: A @ <expr>, B @ <e> } // published as g.x1 / g.yA
// formulas: + - * / ( ) -unary min max clamp round floor ceil abs
// ── floors & objects (common tail: z_offset · enabled · layer · material) ──
floor 1 "Ground" [height N] [wall_height N] [slab_thickness N] { …objects… }
room Name at (x,y) size (w,l) [height h] [enabled <e>] [layer "id"] { // room tail: BEFORE the block
wall east west north // plain sides (list several)
wall south { door D at <off> [from start|center|end] size (w,h) [open] }
wall west { window W at <off> [from start|center|end] size (w,h) [sill s] [open] }
item asset { … } anchor center [gap (gx,gy)]
}
wall Name from (x1,y1) to (x2,y2) [height h] [height_end h2] [facing dir] { …openings… }
slab [name "N"] at (x,y) size (w,l) [thickness t]
beam [name "N"] at (x,y) size (w,l) [height h]
plinth [name "N"] at (x,y) size (w,l) height h
ground [name "N"] at (x,y) size (w,l) [height h]
pillar Name at (x,y) size (w,l) height h // at = TOP-LEFT corner
staircase [name "N"] at (sx,sy) step (rise,tread,width) direction north|south|east|west
[total_height h] [max_run r] [landing_depth …] [flight_gap …] [turn clockwise|anticlockwise]
kitchen [name "N"] path ((x,y),(x,y),…) side left|right depth d height h [base_z z]
item [name "N"] (f."id" | asset { id "…" src "…glb" dims (w,h,d) [category "…"] })
at (x,y) [rotation d] [scale s] [anchor_to "Room" anchor center gap (gx,gy)]
roof [name "N"] pitched|shed|flat [endpoint open|closed]
[slope angle <d> | slope height <h> | slope angle (<l>,<r>)]
[overhang o] [slab_thickness t] [parapet h x t] {
segment "id" from (x,y) to (x,y) width w [start_endpoint …] [gable_overhang (a,b)] [tie_beams N]
truss "segId" fink|mono_pitch at (pos, …)
}
// ── reuse ────────────────────────────────────────────────────────────
component Name [goal "…"] { param p = default [label "…"] …objects in local coords… }
use Name [as "id"] at (x,y) [rotation deg] [with { p = v, … }]
import "name" [as ns] // ns.Comp · ns."assetId" (bundled: std-furniture, konkan/base)
// ── owner knobs ──────────────────────────────────────────────────────
configurator {
title "…" note "…"
slider target "Label" [ft] [min .. max step s] [note "…"]
number target "Label" [ft]
toggle target "Label"
select target "Label" { Label = value, "Quoted" = value }
group "Section" [note "…"] { …inputs… }
}
// ── display ──────────────────────────────────────────────────────────
layer "id" "Label" [color "#rrggbb"] [group "Group"]
raw "type" { …literal JSON… } // escape hatch
See also the built-in 📖 Reference panel in the editor, the full
components & libraries chapter, the example files in
wadi-dsl/examples/ (start with minimal.wdl, then coastal.wdl
for a grid-driven house and complete.wdl for every construct at once), and the design
philosophy in the parametric method.