Plan format

An outside assistant can write a short JSON plan. The app itself has no AI. Lengths are millimetres. x increases to the right, and y increases downward. A plan needs walls or room polygons. The full floorplanner.plan version 1 file is also accepted.

Schema: https://openfloorplan.app/plan-format.schema.json (/plan-format.schema.json).

Minimal example

One room, given as a polygon.

{
  "name": "One room",
  "rooms": [
    {
      "name": "Room",
      "polygon": [
        [0,0],
        [4000,0],
        [4000,3000],
        [0,3000]
      ]
    }
  ]
}

Open this plan

Three-bedroom example

Walls, named rooms, doors, windows, and a few pieces of furniture.

{
  "name": "3 bed",
  "walls": [
    {
      "a": [0,0],
      "b": [10000,0]
    },
    {
      "a": [10000,0],
      "b": [10000,8000]
    },
    {
      "a": [10000,8000],
      "b": [0,8000]
    },
    {
      "a": [0,8000],
      "b": [0,0]
    },
    {
      "a": [0,4000],
      "b": [10000,4000]
    },
    {
      "a": [3000,0],
      "b": [3000,4000]
    },
    {
      "a": [6000,0],
      "b": [6000,8000]
    }
  ],
  "rooms": [
    {
      "name": "Bedroom 1",
      "at": [1500,2000]
    },
    {
      "name": "Bedroom 2",
      "at": [4500,2000]
    },
    {
      "name": "Bathroom",
      "at": [8000,2000]
    },
    {
      "name": "Living room",
      "at": [3000,6000]
    },
    {
      "name": "Bedroom 3",
      "at": [8000,6000]
    }
  ],
  "doors": [
    {
      "wall": 2,
      "center": 7000
    },
    {
      "wall": 4,
      "center": 1500
    },
    {
      "wall": 4,
      "center": 4500
    },
    {
      "wall": 6,
      "center": 2000
    },
    {
      "wall": 6,
      "center": 6000
    }
  ],
  "windows": [
    {
      "wall": 0,
      "center": 4500
    },
    {
      "wall": 1,
      "center": 2000
    },
    {
      "wall": 3,
      "center": 2000,
      "type": "bay",
      "width": 2400,
      "depth": 600,
      "angle": 45
    }
  ],
  "furniture": [
    {
      "id": "double-bed",
      "x": 1500,
      "y": 2000
    },
    {
      "id": "double-bed",
      "x": 4500,
      "y": 2000
    },
    {
      "id": "king-bed",
      "x": 8000,
      "y": 6000
    },
    {
      "id": "sofa",
      "x": 2000,
      "y": 6000
    }
  ]
}

Open this plan

Fields

name, units, and ceilingHeight

name is optional. units is "metric" or "imperial". Omit it for metric. ceilingHeight is millimetres, from 1800 to 6000. Omit it for 2400.

walls

Walls are centre lines, 100 mm thick by default. Each wall is { a, b }. A point is [x, y] or {x, y}. thickness is optional, in millimetres. Omit it for 100.

rooms

A room is either { name?, polygon } with 3 or more points, or { name?, at } with a point inside the walls. Closed walls can derive rooms. Give walls or rooms, or both.

Room areas are measured to the inner faces, so a 4000 × 3000 mm centre-line room shows 3.9 m × 2.9 m = 11.31 m². There is no separate inside-dimensions option. To get an exact inside size, offset the centre lines outward by half the wall thickness. For a 100 mm wall that is 50 mm each side, so 4100 × 3100 centre lines give a 4000 × 3000 inside room.

doors

wall is an index from 0, or a wall id. center is millimetres from wall end a. Omit it for the middle. type is single, double, or sliding. The default is single. Width defaults are 800 for single, 1500 for double, and 1800 for sliding. Height defaults to 2000. hinge is left or right, and the default is left. swing is in or out, and the default is in.

windows

Windows use the same wall and center. type is standard or bay. The default is standard. A standard window defaults to width 1200, height 1200, and sill 900. A bay defaults to width 2400, depth 600, and angle 45. angle is 45 (splayed) or 90 (box). side is in or out, and it is optional. depth, angle, and side are only for type bay.

furniture

id is a catalogue id from the table below. x and y are the centre of the piece, in millimetres. They are optional. Give both, or neither. Neither uses the middle of the first room. rotation is degrees, clockwise. The default is 0. width, depth, and height are optional, in millimetres. height is the vertical size. Omit them to use the catalogue size.

Furniture catalogue

Use these ids. Do not invent one.

IdNameWidth (mm)Depth (mm)Height (mm)
single-bedSingle bed9001900600
double-bedDouble bed13501900600
king-bedKing bed15002000600
sofaSofa2000900450
armchairArmchair850850450
dining-tableDining table1600900750
dining-chairDining chair450500900
coffee-tableCoffee table1000500400
deskDesk1200600750
wardrobeWardrobe10006002100
fridgeFridge6006501800
toiletToilet380700780
bathBath7001700560
basinBasin500400850
wall-mounted-tvWall-mounted TV123060690
tv-on-standTV on stand12302501050
tv-unitTV unit1600400500
bookcaseBookcase8003001800
sideboardSideboard1600450800
2-seat-sofa2-seat sofa1500900850
ottomanOttoman600600450
rugRug2000140010
floor-lampFloor lamp3003001600
plantPlant4004001200
fridge-freezerFridge-freezer6006501850
dishwasherDishwasher600600850
washing-machineWashing machine600600850
tumble-dryerTumble dryer600600850
range-cookerRange cooker900600900
kitchen-islandKitchen island1800900900
bar-stoolBar stool400400750
chest-of-drawersChest of drawers800450800
bedside-tableBedside table450400550
cot-bedCot bed1440740900
desk-chairDesk chair6006001000
filing-cabinetFiling cabinet4706201320
radiatorRadiator1000100600
shower-trayShower tray9009001900
upright-pianoUpright piano15006001200
garden-tableGarden table1500900750
customCustom10001000

How to make a link

For a short plan, use #plan=json: plus encodeURIComponent of the JSON.

For a longer plan, use #plan=z:. That is deflate-raw bytes, then base64url. Use - and _, and strip =. The opener also accepts zlib deflate, but this snippet uses CompressionStream with deflate-raw.

Keep the link under about 8,000 characters. Count the full URL, including https://openfloorplan.app/, for both json: and z:. A json: link is used when https://openfloorplan.app/#plan=json: plus the encoded JSON is at most 8,000 characters. Otherwise use z:, counted the same way.

If the compressed link is still over about 8 kB, do not use a link. Give the person a .floorplan.json file in the full floorplanner.plan version 1 format (the schema's second form). They import that file with Import. The short JSON is for links and for Paste plan. File import does not accept the short JSON.

async function floorPlanLink(plan) {
  const json = JSON.stringify(plan)
  const jsonUrl = 'https://openfloorplan.app/#plan=json:' + encodeURIComponent(json)
  if (jsonUrl.length <= 8000) return jsonUrl
  const compressed = new Response(
    new Blob([new TextEncoder().encode(json)]).stream().pipeThrough(new CompressionStream('deflate-raw')),
  )
  const bytes = new Uint8Array(await compressed.arrayBuffer())
  let binary = ''
  for (const byte of bytes) binary += String.fromCharCode(byte)
  const b64 = btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')
  const zUrl = 'https://openfloorplan.app/#plan=z:' + b64
  if (zUrl.length <= 8000) return zUrl
  return null
}

A null result means give them a .floorplan.json file instead.

From a photo of an estate agent's plan

The JSON cannot hold the photo. The person can import the picture in the app. Use a JPG, PNG, WebP, or SVG. A PDF is not imported.

Read the printed lengths. Convert metres to millimetres. 4.00 m is 4000.

Draw each wall as a segment from a to b. Put the top-left of the picture near [0, 0]. y increases downward.

Put doors and windows on the wall. Measure center from that wall's a end.

A bay window is type "bay", with width, depth, and angle 45 or 90.

Name each room with at (a point inside that room) or with a polygon.

Do not invent a length that is not printed.

Open the planner