Skip to content

File Format

André Borchert edited this page Aug 22, 2026 · 3 revisions

File Format

A room is a UTF-8 JSON document. The same envelope is what ⬇️ Export writes to a .rcad file, what the client sends to POST /api/save, and what is stored in the rooms.json column — so a file exported today can be imported again, or POSTed to the API unchanged.

Envelope

{
  "format": "com.maria.roomcad-v2.room",
  "version": 1,
  "room": { … }
}

A wrong format or a version newer than the app knows is rejected on load — RoomCAD never guesses at a document it does not understand.

A complete, valid example

A 4 × 3 m bedroom on an 8 × 6 m canvas, with one door, one window, a bed, a ceiling bulb and a patch of shared floor (trimmed for length):

{
  "format": "com.maria.roomcad-v2.room",
  "version": 1,
  "room": {
    "id": "b1b0…",
    "name": "Bedroom",
    "width": 4, "length": 3, "height": 2.6,
    "canvas": { "width": 8, "length": 6 },
    "origin": { "x": 2, "z": 1.5 },
    "grid": "fiveCentimeters",
    "walls": [
      { "id": "wall1", "start": { "x": 2, "z": 1.5 }, "end": { "x": 6, "z": 1.5 } },
      { "id": "wall2", "start": { "x": 6, "z": 1.5 }, "end": { "x": 6, "z": 4.5 } },
      { "id": "wall3", "start": { "x": 6, "z": 4.5 }, "end": { "x": 2, "z": 4.5 } },
      { "id": "wall4", "start": { "x": 2, "z": 4.5 }, "end": { "x": 2, "z": 1.5 } }
    ],
    "doors":   [ { "id": "d1", "wallID": "wall1", "offset": 1.2, "width": 0.9,
                   "open": true, "swingInside": true } ],
    "windows": [ { "id": "w1", "wallID": "wall2", "offset": 0.8, "width": 1.2 } ],
    "furniture": [
      { "id": "f1", "kind": "bed",   "center": { "x": 3, "z": 2.2 }, "rotationDegrees": 90 },
      { "id": "f2", "kind": "light", "center": { "x": 4, "z": 3   }, "rotationDegrees": 0 }
    ],
    "publicAreas": [ { "x": 2, "z": 1.5, "w": 1.5, "l": 1 } ]
  }
}

Fields

Room

Field Meaning
width, length, height the room itself, in metres (2–20, 2–20, 2.2–5)
canvas the buildable base plate you can draw on (up to 60 × 60 m)
origin where the room's corner sits on that canvas
grid oneCentimeter, twoCentimeters or fiveCentimeters
publicAreas {x, z, w, l} rectangles of shared floor, skipped by auto-layout

Everything is metres, x runs east–west and z north–south — the top of the 2D plan is north. The canvas is deliberately larger than the room so you can draw extra rooms beside it; only the real wall envelope is built in 3D.

Walls

Axis-aligned segments between two points. Thickness is not stored — it is a constant 10 cm. Walls shorter than 15 cm are dropped on load.

Doors and windows

Positioned along their wall, not in world space: offset is the distance from the wall's start to the near edge of the opening.

Field Doors Windows
width 0.6–1.4 m 0.4–2.0 m
open true (default) or false —
swingInside true (default) or false —
height 2.10 m tall sill 0.90 m, 1.00 m of glass

An opening whose wallID no longer exists, or that no longer fits, is dropped on load; offsets are clamped to leave 10 cm of wall at each end.

Furniture and fixtures

kind is one of:

Category Kinds
furniture bed, table, chair, wardrobe, desk, sofa, shelf, nightstand, dresser, armchair
fixture light (60 W bulb), lightPanel (200 W office panel)

Size, colour and climb height come from the kind — the document only stores center and rotationDegrees (snapped to 0 / 90 / 180 / 270).

Loading is forgiving, then strict

On load RoomCAD fills in missing fields (canvas, origin and publicAreas are optional, for documents written before they existed), then sanitises: values are clamped to their legal ranges, orphaned openings are removed and furniture is pulled back inside the canvas. A document that survives this is guaranteed to be renderable.

Exporting a drawing instead

⬇️ Export → Drawing (.svg) writes the plan as a vector drawing rather than a RoomCAD document: white ground, solid walls with the openings cut out, door swing arcs, furniture outlines, room areas, dimension lines and a title block.

It is drawn for paper. The page is sized in millimetres and the scale is chosen from the architectural ladder (1:20, 1:25, 1:50, 1:100, 1:200, 1:500) — the closest one that fits an A4 sheet either way up — and printed in the title block, so the drawing measures correctly with a scale rule. There are no external references, so the file stands alone in any program that reads SVG.

An .svg is a picture of the plan and cannot be opened back into RoomCAD. Keep the .rcad for that.

Generating one yourself

The model is a plain ES module with no dependencies, so scripting it is easy:

import * as P from "./roomcad/web/plan.js";

const room = P.freshRoom("Studio", 5, 4, 2.7);
P.centerRoom(room);
room.furniture.push({
  id: P.uid(), kind: "desk",
  center: { x: 3, z: 2 }, rotationDegrees: 0,
});
P.sanitize(room);
console.log(P.serializeRoom(room));   // ready to import or POST to /api/save

Clone this wiki locally