ArchLang Playground

Courtyard House — an ArchLang floor plan example

A 16.3 x 11.8 m house whose rooms ring an open courtyard — which is a hole in the building, not a room.

This plan compiles to 11 rooms, 163 m² of floor area and 15 windows; every room is reachable from the entrance.

Living 19.3 m² Entry 12.3 m² Dining 12.3 m² Gallery 24.0 m² Bedroom 1 16.5 m² Bedroom 2 16.5 m² Bedroom 3 15.0 m² Study 17.5 m² Kitchen 12.3 m² Bathroom 8.8 m² Utility 8.8 m² 2100 1600 3900 1600 3650 1600 1850 3500 1500 500 3500 2000 1500 3500 16300 1400 1000 300 800 1950 1400 4950 3500 2500 1000 1500 3000 11800 5150 2000 2950 1600 2000 1400 1200 3500 1500 500 3500 2000 1500 3500 16300 1450 900 2250 600 1900 600 4100 3500 2500 1000 1500 3000 11800 100 150 200 300 ROOM SCHEDULE NO. NAME AREA (m²) Living wing 01 Living 19.25 02 Entry 12.25 03 Dining 12.25 04 Gallery 24.00 SUBTOTAL 67.75 Private wing 05 Bedroom 1 16.50 06 Bedroom 2 16.50 07 Bedroom 3 15.00 08 Study 17.50 SUBTOTAL 65.50 Service wing 09 Kitchen 12.25 10 Bathroom 8.75 11 Utility 8.75 SUBTOTAL 29.75 TOTAL 163.00 LEGEND poche wc basin bathtub kitchen_sink counter stove fridge bed wardrobe sofa table bench desk bookcase washer planter N 02 m PROJECTCourtyard HouseDRAWN BYArchLangDATE2026-08-16SCALE1:100
"Courtyard House" — a 11-room floor plan, 163 m² total: Living (19.25 m²), Entry (12.25 m²), Dining (12.25 m²), Gallery (24 m²), Bedroom 1 (16.5 m²), Bedroom 2 (16.5 m²), Bedroom 3 (15 m²), Study (17.5 m²), and 3 more; 10 doors, 15 windows, entrance via d_front.
Open in the editor

The source

This is the whole plan. The drawing above is what the compiler produced from it — nothing was drawn by hand.

# A 16.3 x 11.8 m single-storey house whose rooms ring an open courtyard.
#
# The courtyard is the whole point of the plan, and the first thing to understand about
# it is that IT IS NOT A ROOM. Nothing is declared at (5000,3500)-(11000,7000): it is
# the hole the building is wrapped around, open to the sky, and calling it a `room` with
# a `uses` would put 21 m2 of weather into the floor schedule. What encloses it is a
# single three-segment partition, `w_court`, and everything that reads the plan — the
# schedule, `describe().totals`, the circulation flood-fill — simply sees no floor there.
#
# THE FACING RULE. A courtyard is the shape that breaks the naive way of answering
# "which way does this window look?". Take `w_court_w`, the window on the courtyard's
# WEST wall. That wall is west of the courtyard, so a rule that compared the window with
# the middle of the building would call it west-facing. It is not: the gallery's floor is
# on the west side of that wall and the open air is on the east, so the window looks
# EAST. ArchLang answers it by probing one wall thickness off each face of the window's
# own host segment and taking the side no room occupies, which is exact on a courtyard
# and on a sloped facade alike. Read it back with:
#
#   arch describe examples/courtyard-house.arch --json --select windows
#
# and `w_court_w` reports `"facing": "east"`, `w_court_e` reports `"facing": "west"` —
# each looking ACROSS the courtyard, which is what someone standing at them sees.
#
# ZONES. `zone living { … }`, `zone private { … }` and `zone service { … }` group the
# room statements into wings. A zone has ZERO geometry: every statement inside resolves
# exactly as if the wrapper were deleted, same coordinates, same ids, byte-identical
# drawing. Membership is DECLARED, never inferred from where a room happens to sit —
# which is why the schedule below can carry a subtotal per wing that no amount of
# looking at the drawing could have derived. `arch describe --json --select zones` rolls
# the areas up; `--zone private` reads one wing on its own.
#
# The gallery is one POLYGON room, not three rectangles: a U wrapping the courtyard on
# west, south and east. Written as three rooms it would need three openings between
# pieces of the same corridor, and the schedule would list a corridor three times. Its
# centroid falls in the courtyard — a U is the shape whose average point is outside it —
# so the label is placed by hand in the south arm.
#
# Also here: `left-of` and `above`, the two relational directions no other example uses;
# `sliding` doors where a room opens onto the courtyard (a leaf that swings 900 mm into
# a 1500 mm gallery is a leaf in the way); and one `pocket` door for the same reason.
plan "Courtyard House" {
  units mm
  # `grid 50`: partitions are 100 thick, so a `flush` fixture lands on a ...50 face.
  grid 50
  paper A3 landscape
  scale 1:100
  north up
  dims auto all
  schedule rooms
  legend

  # The plot fronts the street on the WEST. `site` draws nothing; it names five
  # directions on `describe --json --select site` — street (W), back (E), equator_side
  # (S, north of the equator), sunrise_side (E), sunset_side (W). They are a DRAFTING
  # HEURISTIC for an aspect: ArchLang has no sun model, no latitude and no date.
  site { street west hemisphere north }

  # ---- shell, bands and the courtyard wall ----------------------------------
  wall id=shell exterior thickness 300 { (0,0) (16000,0) (16000,11500) (0,11500) close }

  # Two full-width partitions do most of the work: one behind the north band of rooms,
  # one in front of the south band of bedrooms. Each is a single wall because the line
  # is continuous even though what it separates changes along it.
  wall id=w_band_n partition thickness 200 { (0,3500) (16000,3500) }
  wall id=w_band_s partition thickness 200 { (0,8500) (16000,8500) }

  # The courtyard itself: one wall, three segments, mitred at both corners.
  wall id=w_court partition thickness 200 { (5000,3500) (5000,7000) (11000,7000) (11000,3500) }

  # 150, not 100: a pocket door needs a partition thick enough to swallow its panel.
  wall id=w_gal_w  partition thickness 150 { (3500,3500) (3500,8500) }
  wall id=w_gal_e  partition thickness 100 { (12500,3500) (12500,8500) }
  wall id=w_entry  partition thickness 100 { (3500,0) (3500,3500) }
  wall id=w_dining partition thickness 100 { (9000,0) (9000,3500) }
  wall id=w_kitch  partition thickness 100 { (12500,0) (12500,3500) }
  wall id=w_bath   partition thickness 100 { (12500,6000) (16000,6000) }
  wall id=w_bed1   partition thickness 100 { (5500,8500) (5500,11500) }
  wall id=w_bed2   partition thickness 100 { (11000,8500) (11000,11500) }

  # ---- rooms, grouped into wings by `zone` ----------------------------------
  zone living "Living wing" {
    room id=r_living at (3500,0) size 5500x3500 label "Living"  uses living
    # `left-of` places the entry against the living room's west face and `align top`
    # lines their north edges up — no second coordinate is written, and moving the
    # living room moves the entry with it.
    room id=r_entry  left-of r_living align top size 3500x3500 label "Entry" uses entry hall
    room id=r_dining at (9000,0) size 3500x3500 label "Dining" uses dining

    # The gallery: a U around the courtyard. Its centroid is in the open air, so the
    # label point is given explicitly (otherwise `W_ROOM_LABEL_OUTSIDE`).
    room id=r_gallery polygon
      (3500,3500) (5000,3500) (5000,7000) (11000,7000) (11000,3500)
      (12500,3500) (12500,8500) (3500,8500)
      label "Gallery" at (8000,7750) uses circulation hall
  }

  zone private "Private wing" {
    room id=r_bed1 at (0,8500)     size 5500x3000 label "Bedroom 1" uses bedroom
    room id=r_bed2 at (5500,8500)  size 5500x3000 label "Bedroom 2" uses bedroom
    room id=r_bed3 at (11000,8500) size 5000x3000 label "Bedroom 3" uses bedroom
    # `above` is the other new direction: the study sits directly on top of bedroom 1,
    # its own depth up from that room's north edge, west edges aligned.
    room id=r_study above r_bed1 align left size 3500x5000 label "Study" uses office
  }

  zone service "Service wing" {
    room id=r_kitchen at (12500,0)    size 3500x3500 label "Kitchen"   uses kitchen
    room id=r_bath    at (12500,3500) size 3500x2500 label "Bathroom"  uses bath
    room id=r_util    at (12500,6000) size 3500x2500 label "Utility"   uses utility
  }

  # ---- doors ----------------------------------------------------------------
  # `swing in` and not `swing into r_entry`: a relationally-placed room is deliberately
  # NOT a `swing into` target, because its rectangle is DERIVED — the compiler will not
  # resolve an authored clause against a position it computed for you. On an exterior
  # wall the explicit word is the plain one anyway: `in` is the shell's inward face.
  door id=d_front at (0,1750) width 1000 wall shell hinge left swing in

  # Onto the courtyard. A slider is what a full-height garden opening actually is, and
  # a hinged leaf here would sweep either the gallery or the courtyard threshold.
  door id=d_court_l sliding on w_band_n at 6000 width 1800 slide left
  door id=d_court_g sliding on w_court  at 6500 width 2000 slide right

  # The study, off a gallery only 1350 mm wide between faces. A pocket keeps both sides
  # clear; `slide right` drives the panel south into 2100 mm of blank wall (it needs
  # 850 — its own 800 plus a jamb), where `slide left` would aim it at the corner.
  door id=d_study pocket on w_gal_w at 2500 width 800 slide right

  door id=d_bath  on w_gal_e  at 750   width 800 swing into r_bath
  door id=d_util  on w_gal_e  at 4000  width 800 swing into r_util
  door id=d_kitch on w_kitch  at 1750  width 900 swing into r_kitchen
  door id=d_bed1  on w_band_s at 4500  width 900 swing into r_bed1
  door id=d_bed2  on w_band_s at 8000  width 900 swing into r_bed2
  door id=d_bed3  on w_band_s at 11750 width 900 swing into r_bed3

  # Cased openings: the public rooms read into one another with no leaf at all.
  opening id=o_entry  at (3500,1750)  width 1500 wall w_entry
  opening id=o_dining at (9000,1750)  width 1600 wall w_dining
  opening id=o_gal_w  at (4250,3500)  width 1200 wall w_band_n
  opening id=o_gal_e  at (11750,3500) width 1200 wall w_band_n

  # ---- windows: outer facades first -----------------------------------------
  window id=n_living  at (6000,0)      width 2000 wall shell
  window id=n_dining  at (10750,0)     width 1600 wall shell
  window id=n_kitchen at (14250,0)     width 1400 wall shell
  window id=e_kitchen at (16000,1750)  width 900  wall shell
  window id=e_bath    at (16000,4750)  width 600  wall shell
  window id=e_util    at (16000,7250)  width 600  wall shell
  window id=s_bed1    at (2750,11500)  width 1600 wall shell
  window id=s_bed2    at (8250,11500)  width 1600 wall shell
  window id=s_bed3    at (13500,11500) width 1600 wall shell
  window id=w_study   at (0,6000)      width 1400 wall shell
  window id=w_entry_l at (0,2950)      width 800  wall shell

  # ---- ...then the courtyard walls ------------------------------------------
  # These three are the facing demonstration. `w_court_w` is hosted on the courtyard's
  # WEST wall and faces EAST; `w_court_e` is on its east wall and faces WEST; the dining
  # room's and the living room's courtyard windows are on the band wall and face SOUTH,
  # which is the same equator side the bedrooms take from the outer facade.
  window id=w_court_l at (8000,3500)  width 1200 wall w_band_n
  window id=w_court_w at (5000,5250)  width 1200 wall w_court
  window id=w_court_e at (11000,5250) width 1200 wall w_court
  window id=w_court_d at (10000,3500) width 1600 wall w_band_n

  # ---- furniture ------------------------------------------------------------
  furniture sofa  in r_living anchor top flush size 2400x900 label "Sofa"
  furniture table in r_living centered size 1600x900 label "Table"

  furniture table in r_dining centered size 1600x900 label "Dining table"

  # The kitchen run rides the shell's north segment; `offset` is the distance of each
  # piece's CENTRE along it from (0,0), and the footprints come from the catalogue.
  furniture kitchen_sink against wall shell segment 0 offset 13100 in r_kitchen
  furniture stove        against wall shell segment 0 offset 13800 in r_kitchen
  furniture counter      against wall shell segment 0 offset 14400 in r_kitchen
  furniture fridge       against wall shell segment 0 offset 15000 in r_kitchen

  furniture bathtub in r_bath anchor top-right    flush size 1700x700
  furniture wc      in r_bath anchor bottom-left  flush size 400x700
  furniture basin   in r_bath anchor bottom-right flush size 600x450

  furniture counter in r_util anchor top flush size 1800x600
  # 900 x 600 of plumbed appliance bay, flush to the east wall. It is drawn 900 wide
  # because a label is drawn INSIDE its own footprint and the renderer has no metrics
  # to shrink one with: at 1:100 the word "Washer" is 825 mm long on the plan.
  # `rotate 90` puts the machine's BACK — where its supply and waste run — on the east
  # wall it stands against. `washer` is catalogued as a wall-requiring fixture as of
  # v1.28, so leaving it at rotate 0 reads as an appliance plumbed into open floor
  # (W_FIXTURE_BACK_TO_ROOM). The footprint is square-ish and the piece is a plain
  # rectangle, so nothing in the drawing moves.
  furniture washer  at (14950,7300) size 900x600 label "Washer" rotate 90

  # The study is placed relationally, so its pieces take absolute points rather than
  # anchors — the room's own rectangle is derived, and hanging furniture off a derived
  # edge would hide which of the two the author actually meant.
  furniture desk  at (300,3700) size 1600x700 label "Desk"
  furniture bookcase at (1500,8000) size 1800x400 label "Bookcase" rotate 180
  furniture bench    at (150,3000)  size 1200x400 label "Bench"

  furniture bed      in r_bed1 anchor bottom-left  flush size 1500x2000 label "Double"
  furniture wardrobe at (4000,11000) size 1400x350 label "Wardrobe" rotate 180
  furniture bed      in r_bed2 anchor bottom-right flush size 1500x2000 label "Double"
  furniture bed      in r_bed3 anchor bottom-right flush size 1500x2000 label "Double"

  # The one piece with no `in <room>` at all — because there is no room to name. A
  # planter standing in the courtyard is furniture in the open air, and every rule that
  # would ask which room it belongs to simply does not apply to it.
  furniture planter at (7250,4750) size 1500x1500 label "Planter"

  # ---- the eaves --------------------------------------------------------------
  # `shell` is the plan's one closed exterior wall — a plain rectangle. The courtyard
  # is cut by an INTERIOR partition, `w_court`, so it never enters this offset at all;
  # the concave gallery that wraps it is a room polygon, not a wall, and `roof` only
  # ever reads the wall ring.
  roof overhang 600

  title {
    project "Courtyard House"
    drawn_by "ArchLang"
    date "2026-08-16"
  }
}

Read more