Import & export project JSON
Back up a project, move it between browsers, or inspect its wiring data.
Project file format
Linked multi-canvas document · version 3
New saves and exports use this strict envelope. The core fields below are required; metadata is optional. Unknown fields are rejected. A document contains 1–30 canvases, each embedding the complete version 1 canvas object described below. Canvas IDs are unique within the document; object, port and pin references are scoped to their own canvas. Cross-canvas cables are explicit entries in the document-level links array; local cables stay inside each canvas. activeCanvasId must identify an existing canvas. kind is building (Sections) or electronics (Sections). The document name and each canvas name are independent.
{
"format": "wirecharm",
"version": 3,
"links": [],
"name": "My building",
"kind": "building",
"activeCanvasId": "ground",
"canvases": [
{"id": "ground", "canvas": {
"format": "wirecharm", "version": 1,
"name": "Ground floor", "description": "",
"units": "m", "pixelsPerUnit": 100,
"parts": [], "junctions": [], "cables": [],
"walls": [], "notes": [], "background": null
}}
]
}Version 1 and 2 projects remain importable: v1 migration wraps the unchanged canvas in a v3 document; v2 migration retains all canvases and adds an empty links array. Existing walls or a background infer Building; otherwise Electronics. All inactive canvases are validated on import. Local saves, project archives and JSON exports retain every canvas. Undo snapshots the entire document, so cable creation, edits and deletion are atomic across both canvases. Switching canvases preserves history; project changes and reloads reset it.
Cables between sections
links is required in v3 (empty is allowed), with up to 1,500 cable bundles. Each link has a unique ID, name, gauge, from/to ends, and 1–16 wires. Each end specifies a canvas ID, a normal local endpoint ({partId,portId} or {junctionId}), and the canvas-pixel position of its continuation label. The two ends must be on different canvases. A wire’s fromPin and toPin refer to the actual endpoint connector or junction pins in those canvases. Pin IDs are scoped locally, so identical IDs may exist on different floors.
{
"id": "upper-supply", "name": "Upper floor supply", "gauge": "14 AWG",
"from": {
"canvasId": "ground", "endpoint": {"partId": "panel", "portId": "out"},
"position": {"x": 400, "y": 250}
},
"to": {
"canvasId": "upper", "endpoint": {"partId": "outlet", "portId": "in"},
"position": {"x": 300, "y": 200}
},
"wires": [{"id": "live", "label": "L", "color": "#f47c89", "fromPin": "l", "toPin": "l"}]
}This is one entry in the document’s links array; its canvas, object and pin IDs must resolve. See the complete linked-floor example. Colored conductors run from the real connector pins to paired labels. Clicking a label selects the far-end part or junction on its canvas. Label positions are independently movable. Geometric crossings do not connect; connect to a junction when branching. Deleting a part or junction removes its incident local and cross-canvas cables; Undo restores the full connectivity. Removing a linked pin or connector in the part editor, JSON import, or AI edit is rejected unless the corresponding link is also removed or remapped. SVG exports show continuations for the active canvas; JSON retains both ends.
Saved library categories are browser template metadata (category: building|hobby, catalog: true), separate from the portable v1 part object. Clicking a library card opens its editor. For new, generated, and library parts, Save part changes only the catalog; Save & Add also places a fresh instance on the canvas. Saving an existing canvas part updates that instance. The card’s + action or dragging adds an independent canvas instance. The Part Workshop defaults new parts to Building in building projects and Electronics otherwise. Saving publishes a reusable library card in that category and All parts. A renamed or edited saved revision keeps its full custom connector definitions; placement copies receive fresh part IDs.
1. The project envelope
Use a single JSON object. All listed fields are required, including empty arrays, empty descriptions, and a null background. No additional properties are allowed at any level. JSON must contain finite numbers and no comments, trailing commas, or Markdown fences.
{
"format": "wirecharm",
"version": 1,
"name": "My physical wiring plan",
"description": "What is being built and where parts belong.",
"units": "mm",
"pixelsPerUnit": 2,
"parts": [],
"junctions": [],
"cables": [],
"walls": [],
"notes": [],
"background": null
}
| Field | Contract |
|---|---|
format, version | Exactly "wirecharm" and 1. |
units | One of mm, cm, m, in, ft. |
pixelsPerUnit | Greater than 0, at most 10,000. Coordinates always use canvas pixels; lengths divide centerline pixels by this scale. |
| Coordinates | X grows rightward, Y downward. Negative coordinates are supported. Absolute numeric coordinates must be within ±1,000,000. |
| IDs | 1–100 ASCII letters, digits, underscores, or hyphens. All parts, cables, junctions, walls, and notes share one unique object ID namespace. Port IDs are unique within each part; pin IDs within each connector or junction; wire IDs within each cable. |
| Text and colors | Text is plain text up to 2,000 characters. Colors are six-digit #RRGGBB values. Labels are rendered as escaped text. |
2. Parts, connectors, and pins
Each part is a physical rectangle with independently named connectors. A connector contains one or more pins; each pin is an individually addressable conductor endpoint. All connector definitions are embedded in the project, so a custom project does not depend on a private library.
{
"id": "battery", "type": "battery", "name": "Battery pack",
"x": 100, "y": 200, "width": 162, "height": 106,
"color": "#edc879", "notes": "Mount beside the controller.",
"ports": [{
"id": "out", "label": "OUTPUT", "side": "right", "offset": 0.65,
"direction": "out",
"pins": [
{"id": "v", "label": "V+", "color": "#f47c89"},
{"id": "g", "label": "GND", "color": "#8e9db5"}
]
}]
}
Allowed part types: controller, servo, led, battery, charger, fuse, switch, panel, outlet, wall-switch, lamp, and custom. The type selects the illustration; the embedded ports define connectivity.
Width and height range from 60 to 2,000 canvas pixels. Side is left, right, top, or bottom. Offset runs from 0 to 1 along that edge: left/right from top to bottom, top/bottom from left to right. Directions are in, out, or bidirectional; these annotate intent without enforcing electrical compatibility. A part may contain 0–24 connectors, each with 1–16 pins. Canvas pin ordering follows the array, with 12 canvas pixels between adjacent pins.
3. Cables are bundles, not just lines
{
"id": "supply", "name": "Protected power harness",
"from": {"partId": "battery", "portId": "out"},
"to": {"partId": "controller", "portId": "power"},
"points": [{"x": 350, "y": 269}, {"x": 350, "y": 400}],
"gauge": "24 AWG",
"wires": [
{"id": "supply-v", "label": "V+", "color": "#f47c89", "fromPin": "v", "toPin": "v"},
{"id": "supply-g", "label": "GND", "color": "#8e9db5", "fromPin": "g", "toPin": "g"}
]
}
The endpoint shape is exactly {"partId": "…", "portId": "…"} or exactly {"junctionId": "…"}. Do not mix these shapes. Each endpoint and each referenced pin must exist in the project. Endpoints must differ. A cable has 1–16 conductors. Within a cable, each source pin and each destination pin can appear only once. Fanout happens through a junction or separate cable runs.
points holds only interior route anchors, in source-to-destination order; the connector anchors are computed from part geometry. Use [] for automatic orthogonal routing. Explicit points stay in world coordinates when a part moves. The renderer inserts horizontal-then-vertical elbows between diagonal anchors. For predictable routing, provide an orthogonal sequence yourself. Up to 200 anchors are allowed per cable. Gauge is descriptive text.
New interactive connections match equal pin IDs or equal labels. The saved format supports arbitrary valid explicit fromPin / toPin mappings through the inspector. Pins not mapped by a cable remain unconnected. Colors belong to individual wire objects; junction pin labels express continuity across branch runs.
4. Branches have real topology
To branch a supply into two loads, create a junction with its own pins, an incoming cable, and two outgoing cables. Incoming wires map to junction pin IDs; outgoing wires map from those same junction pin IDs. Crossing cable geometry does not connect anything.
{
"id": "power-branch", "x": 450, "y": 300,
"pins": [
{"id": "v", "label": "V+", "color": "#f47c89"},
{"id": "g", "label": "GND", "color": "#8e9db5"}
]
}
Incoming cable: from = {"partId":"battery","portId":"out"}
to = {"junctionId":"power-branch"}
Outgoing cable: from = {"junctionId":"power-branch"}
to = {"partId":"eye-left","portId":"in"}
The last four lines illustrate endpoint assignments; they are not standalone importable JSON. Download a complete example above for a valid full document. The editor’s branch gesture splits an existing cable into two runs and preserves all conductor mappings before attaching the new run. Deleting a part or junction removes its incident cable runs. Moving a junction preserves its topology.
5. Floor plans, walls, and notes
"walls": [{
"id": "north-wall", "name": "North wall",
"a": {"x": 50, "y": 80}, "b": {"x": 1250, "y": 80},
"thickness": 12
}],
"notes": [{
"id": "room-name", "text": "WORKSHOP / FIRST FLOOR",
"x": 300, "y": 220
}],
"background": null
Walls must be horizontal or vertical, with thickness 1–100 canvas pixels. Walls guide placement and snapping but do not store cable attachments; cable route anchors are the source of truth. Moving a wall does not move existing cables automatically. Use Follow a wall to place a cable’s route on that wall’s axis, or drag a selected cable segment close to it to snap.
An optional raster underlay replaces null with an object containing exactly dataUrl, x, y, width, height, and opacity. Data URLs must embed base64 PNG, JPEG, or WebP. No external URLs or SVG underlays. Width/height are positive, at most 100,000; opacity is 0–1. The UI defaults to 1,200 px wide and 25% opacity. Keep backgrounds small enough for local storage; the importer permits at most an 18 MB JSON file.
6. Limits and validation
| Collection | Maximum |
|---|---|
| Parts | 500 |
| Junctions | 500 |
| Cables | 1,500 |
| Walls / notes | 500 each |
| Connectors / pins / conductors | 24 per part / 16 per connector or junction / 16 per cable |
The published schema checks shapes, types, and limits. Additional semantic validation rejects duplicate object IDs, duplicate port/pin/wire IDs in their scope, missing endpoint references, missing pin references, repeated pin mappings inside a bundle, self-connections, and diagonal walls. Imports and JSON edits are transactional: invalid documents leave the existing project untouched.
Optional studio metadata
The optional metadata.studio object saves this project's control layout. AI generators should omit it; the studio manages it independently of wiring edits and Undo. It travels with browser saves, cloud saves, imports, exports, and project copies. Older documents without metadata use the default layout.
{
"metadata": {
"studio": {
"labels": false,
"inspectorCollapsed": true,
"panels": {
"tools": {"x": 0.5, "y": 1, "dock": "bottom", "bottom": 8},
"overview": {"x": 1, "y": 1, "dock": "bottom", "bottom": 8, "collapsed": true, "mobileCollapsed": true},
"zoom": {"x": 0, "y": 1, "dock": "bottom", "bottom": 8}
}
}
}
}Panel x and y are normalized positions from 0 to 1 within the space available after accounting for panel size and an 8 px canvas margin. dock is bottom or free. A bottom-docked panel keeps its bottom offset in pixels when the canvas changes size. Collapsing Overview temporarily docks its header at the bottom; expanding restores its saved placement. The phone toolbar retains its accessible rail layout.
Unconnected cable crossings use a small visual gap. Shared junction IDs retain a visible branch dot. These marks do not change connectivity or cable route anchors.
Cable colors and physical breakouts
Optional color (#RRGGBB) sets the jacket. Building jackets default to yellow; electronics use a conductor accent. Single wires render in their conductor color, without a jacket. Cross-canvas links also accept color. Wire colors are editable independently. Default DC V+ is red (#ef5350); ground is black (#111111), or brown (#8b5a2b) when needed. Independent signals should use distinct colors. Explicit saved or documented part colors take precedence.
Ground wires always remain visible. The optional legacy display field is retained for import compatibility; generate display: cable. The former ground-symbol presentation is ignored by the renderer. Parallel overlapping runs are separated visually without changing topology or stored manual points.
For a servo harness, route a V+/GND/Signal bundle from the servo to a nearby breakout junction with three separate pins. Route V+ and GND from those pins to the appropriate supply trunks. Route Signal alone to the board. A junction pin is the electrical join, so different pins on the same junction remain independent. The breakout editor retains the chosen bundled end and replaces each outgoing conductor destination with its selected endpoint and pin.
For multiple loads, generate a trunk with several explicit junctions near the loads instead of a star of long overlapping branches. Copy the trunk's conductor pin identities consistently at each tap. Every trunk interval is a cable; every local branch is a separate cable. Distributing branches in the Inspector builds this same topology. Deleting an interior segment extends neighboring segments while retaining both endpoint anchors and all mappings.