Repository navigation
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.
{
"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 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 } ]
}
}| 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.
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.
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.
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).
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.
⬇️ 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.
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