Terrace Row (mirror) — an ArchLang floor plan example
A row of four terrace dwellings generated from one component, placed four times and mirrored in pairs.
This plan compiles to 16 rooms, 199.8 m² of floor area and 18 windows; every room is reachable from the entrance.
The source
This is the whole plan. The drawing above is what the compiler produced from it — nothing was drawn by hand.
# A row of four terrace dwellings generated from ONE component, placed and mirrored.
#
# A terrace is the same plan four times, so it should be written once. `component` +
# `place` is how ArchLang says that, and this file is the smallest complete example of
# the pattern:
#
# component unit(w, d, gable) { … } one dwelling, authored from (0,0) in its
# OWN coordinates, parameterised by width
# place unit(W1, DEPTH, 1) as u1 at (…) an addressable INSTANCE — every id inside
# becomes `u1.<id>`, auto-ids restart per
# instance, so the four are order-independent
# … as u2 at (…) mirror x a real reflection: door swings, hinges and
# the whole fit-out come out mirror-image
#
# Three things about the row are worth reading closely.
#
# MIRRORING PAIRS THE UNITS. `mirror x` reflects about the INSTANCE's own origin, so a
# mirrored unit is placed at its FAR x edge — u2 is 5400 wide and placed at 10800, which
# puts it on 5400…10800. Alternating the mirror is what makes a terrace a terrace: u1 and
# u2 meet local-x=w to local-x=w, so their bathrooms and kitchen runs back onto one shared
# party wall, and u2 and u3 meet local-x=0 to local-x=0. The consequence to notice is that
# the row's two FREE gables are both the local x=0 wall — which is why `gable` is 1 for u1
# and u4 and 0 for the two in the middle, and why the `if` inside the component puts a side
# window on that wall and nowhere else.
#
# THE OFFSETS ARE ARITHMETIC, NOT TYPING. `widths` is an array and X2/X3/X4 are a running
# sum of it. `place` cannot go inside a `for` (the `as <name>` is a plain identifier, not
# an interpolation), so the four lines are unrolled — but change a width and every unit
# after it moves on its own.
#
# THE STAGGER IS ONE COORDINATE. Alternate units sit 600 further from the street, which on
# this sheet means u2 and u4 are placed at y = 0 and u1 and u3 at y = SETBACK. The party
# walls still coincide over everything but that 600, so each junction draws the short
# exterior return a sawtooth terrace actually has.
#
# WHY THE PLAN IS THIS PLAN. A row of four is drawn on ONE sheet, so the unit that survives
# is the one whose rooms are still legible at a quarter of the width — which is a drawing
# constraint, not an architectural one, and it is worth being honest that it drove the
# layout. Four rooms, not five: living and kitchen are one front room with the galley run
# on the party wall, the way a 5.4 m frontage is actually built. Names of one word. Nothing
# narrower than 1.8 m. The test is not "does it compile" but "does every room still say its
# own name inside its own walls" — so `arch describe --json` reporting four rooms per unit
# and the PNG showing sixteen unobstructed labels are the same check, run two ways. Three
# of the four names are therefore PINNED with `label "…" at (x,y)`, written in the
# COMPONENT's own coordinates: a pinned label is a point like any other, so `place` carries
# it through the frame, and the two mirrored units get the mirrored position for free. Get
# the name right in one unit and it is right in four.
#
# `theme blueprint` is a NAMED theme base — a whole cyanotype palette from one word, with
# no `theme { … }` block to maintain.
plan "Terrace Row" {
units mm
# 50, not 100: the partitions are 100 thick and the fit-out is anchored `flush` to
# their inner faces, which lands on half-thickness coordinates.
grid 50
north up
dims auto overall
theme blueprint
let DEPTH = 9000 # every unit is the same depth; only the width varies
let SETBACK = 600 # the sawtooth: alternate units step back from the street
let widths = [5400, 5400, 6000, 5400]
let W1 = widths[0]
let W2 = widths[1]
let W3 = widths[2]
let W4 = widths[3]
# Running sum of the widths — the left edge of each unit in plan coordinates.
let X1 = 0
let X2 = X1 + W1
let X3 = X2 + W2
let X4 = X3 + W3
# ---- one dwelling ---------------------------------------------------------
component unit(w, d, gable) {
let BACK = 4000 # the sleeping band, at the back of the plot
let HALLD = 1800 # the cross hall between the two bands
let BATHW = 2200
let BEDW = w - BATHW
let FRONT = d - BACK - HALLD
wall id=shell exterior thickness 250 { (0,0) (w,0) (w,d) (0,d) close }
wall id=w_bath partition thickness 100 { (BEDW,0) (BEDW,BACK) }
wall id=w_hall_n partition thickness 100 { (0,BACK) (w,BACK) }
wall id=w_hall_s partition thickness 100 { (0,BACK + HALLD) (w,BACK + HALLD) }
room id=bed at (0,0) size BEDW x BACK label "Bed" at (1400,800) uses bedroom
room id=bath at (BEDW,0) size BATHW x BACK label "Bath" at (BEDW + 1100,2100) uses bath utility
room id=hall at (0,BACK) size w x HALLD label "Hall" uses hall circulation
room id=living at (0,BACK + HALLD) size w x FRONT label "Living" at (w / 2,6900) uses living dining kitchen
# Front door off the street into the front room; the garden door is a slider, so no
# leaf sweeps the bedroom floor.
door id=front at (1700,d) width 950 wall shell swing into living
door id=rear sliding at (1200,0) width 1400 wall shell slide left
door id=d_bed on w_hall_n at 2300 width 800 swing into bed
# `on <wall> at <pos>` takes a literal position, so the one opening whose distance
# depends on the unit WIDTH is written as a point on the same wall's centreline.
door id=d_bath at (w - 1400,BACK) width 800 wall w_hall_n swing into bath
opening id=o_hall on w_hall_s at 900 width 1000
window id=win_liv at (600,d) width 900 wall shell
window id=win_liv2 at (w - 900,d) width 1000 wall shell
window id=win_bed at (BEDW - 450,0) width 800 wall shell
window id=win_bath at (w - 500,0) width 500 wall shell
# Only the two units with a free gable get a side window — the other two have a
# party wall there, and a window in a party wall is not a window.
if gable == 1 {
window id=win_gable at (0,2100) width 1200 wall shell
}
# Fit-out by anchor: every piece is measured from its room's backing wall FACE
# (`flush`), so nothing here writes a coordinate or a half-thickness.
furniture id=f_bed bed in bed anchor bottom-left flush inset 100 size 1400x1900 label "Bed"
furniture id=f_ward wardrobe in bed anchor right flush size 600x1200
furniture id=f_shower shower in bath anchor top-left flush size 900x900
furniture id=f_basin basin in bath anchor top-right flush size 600x450
furniture id=f_wc wc in bath anchor bottom-right flush size 400x700
furniture id=f_sofa sofa in living anchor bottom-left flush size 800x1800 label "Sofa"
# The galley run rides the party wall: `against` derives position, depth and the
# quarter-turn from the wall, and each catalogued fixture brings its own footprint.
furniture id=f_sink kitchen_sink against wall shell segment 1 offset 6300 in living
furniture id=f_stove stove against wall shell segment 1 offset 7200 in living
furniture id=f_fridge fridge against wall shell segment 1 offset 8100 in living
}
# ---- the row --------------------------------------------------------------
# A mirrored instance is placed at its FAR x edge, so u2 covers X2…X2+W2 and u4 covers
# X4…X4+W4. u1 and u4 carry the gable flag; u2 and u4 take the sawtooth's front row.
place unit(W1, DEPTH, 1) as u1 at (X1, SETBACK)
place unit(W2, DEPTH, 0) as u2 at (X2 + W2, 0) mirror x
place unit(W3, DEPTH, 0) as u3 at (X3, SETBACK)
place unit(W4, DEPTH, 1) as u4 at (X4 + W4, 0) mirror x
title {
project "Four-unit terrace"
drawn_by "ArchLang"
date "2026-08-16"
}
}