Skip to content
OpenChipwiki

The .openchip file format

A .openchip file is plain JSON, pretty-printed with two-space indentation and a trailing newline. There is no compression, no binary blob, and no wrapper: the file is the project object. You can read it, diff it, and put it in a repository next to the paper.

{
"formatVersion": 1,
"meta": { "name": "Counter-flow exchanger", "units": "mm", "...": "..." },
"printerProfile": { "...": "..." },
"chipParams": { "...": "..." },
"channelGraph": { "nodes": [], "edges": [] },
"fluidProfiles": [],
"fluidSetup": { "...": "..." },
"savedImages": [],
"drainageVents": [],
"presetLibraryRef": null
}

Unknown fields survive. Every container is validated loosely, so a file written by a newer build opens in an older one and keeps its unrecognised keys through a save round-trip. Silently deleting data because you opened a file in last month’s build would be data loss, and this project is not going to do that to anyone’s design.

Missing fields get defaults, not errors. Anything added after the first release is optional on read and filled in with a documented default. A file from before drainage vents existed still opens.

The exception is the block below: these are required, and a file without them is rejected with a message naming the field.

OpenChipProject

Field Type Required Notes
formatVersion positive integer yes Currently 1.
meta object yes Below.
printerProfile object yes Below.
chipParams object yes Below.
channelGraph object yes Below.
fluidProfiles array no — defaults [] Below.
fluidSetup object no — defaults to no assignments Below.
savedImages array no — defaults [] Below.
drainageVents array no — defaults [] Below.
presetLibraryRef string or null no — defaults null The template id this project started from.

ProjectMeta

Field Type Constraint
name string Not empty.
createdAt string ISO-8601.
modifiedAt string ISO-8601. Stamped on save.
units "mm" The only accepted value.

units is a single-member union on purpose. OpenChip works in millimetres throughout — the canvas, the solver, the exporters — and a file claiming inches would be a file whose numbers mean something else. If you need imperial, convert on the way in.

PrinterProfile — the machine this design is being checked against. It is stored in the project, not just in your settings, so a file you send someone else carries the assumptions it was designed under.

Field Type Constraint
name string Not empty.
xyResolutionMicrons number > 0. Pixel pitch in the XY plane.
layerHeightMicrons number > 0.
minFeatureSizeMm number > 0. Drives channel-below-min-feature.
buildVolumeMm Vec3Mm x, y, z, each > 0. Drives footprint-exceeds-build-volume.

See Printer profile fields for what each one means in practice and how to measure your own.

ChipParams — the block the channels are cut into.

Field Type Default Constraint
width number 40 > 0. Chip X extent, mm.
length number 60 > 0. Chip Y extent, mm.
baseThickness number 4 > 0. Total block height, mm.
wallThickness number 2 > 0. Minimum material between a channel and anything else.
minEdgeSpacing number 3 ≥ 0. Your own rule for how close anything may sit to the chip edge.
splitType "two-piece" | "monolithic" "two-piece" See Monolithic vs. two-piece.
splitHeight number 2 ≥ 0. Height of the cut plane, mm.
defaultChannelWidthMm number 0.8 > 0. Used where a segment has no dimension marker.
defaultChannelHeightMm number 0.5 > 0.
defaultCrossSection "round" | "rect" | "trapezoid" "rect"

The last three are defaulted rather than required: files written before they existed still open.

wallThickness and minEdgeSpacing are different rules and are often confused. wallThickness is material between a channel and anything, including another channel. minEdgeSpacing is specifically about the chip boundary.

ChannelGraph — the design itself.

Field Type
nodes array of ChannelNode
edges array of ChannelEdge

Beyond the field types, the loader enforces referential integrity: node ids must be unique, edge ids must be unique, and every edge’s fromNodeId and toNodeId must resolve to a node that exists. A dangling reference is rejected at load rather than crashing the canvas later.

Every node shares three fields (NodeBase) and then discriminates on type.

Field Type Notes
id string Not empty. Unique across the graph.
x number Finite. Chip millimetres, origin at the bottom-left.
y number Finite.
type "waypoint" | "port" | "chamber"

WaypointNode adds nothing. It is a bend or a junction in the path.

PortNode — an opening to the outside.

Field Type Notes
portType "syringe" | "luer" | "luer-lock" | "custom"
diameterMm number > 0. The bore — the through-hole into the channel.
customSpec record of string → number | string Optional. Per-type geometry: taper diameters, socket depth, thread pitch.

ChamberNode — a widened volume at a node.

Field Type Notes
shape "sphere" | "cylinder"
sizeMm number > 0. Diameter.
autoPlaced boolean True when the app placed it at a corner rather than you.

ChannelEdge — one channel segment between two nodes.

Field Type Notes
id string Not empty, unique.
fromNodeId string Must resolve.
toNodeId string Must resolve.
curveType "straight" | "bezier"
controlPoints array of Point2 Optional. Kept when you toggle a bezier back to straight, so toggling again restores the curve.
crossSection "round" | "rect" | "trapezoid" Per segment, not per chip.
dimensionPoints array of DimensionPoint May be empty — the chip defaults then apply.

Point2 is { x, y }, both finite.

DimensionPoint — a width pinned at a position along a segment.

Field Type Constraint
t number 0–1. Position along the segment.
widthMm number > 0.
heightMm number > 0. Optional — falls back to the chip default.

The solid generator interpolates between consecutive dimension points, and across a connected chain of edges when you dimension a whole path. See Dimension markers and lofting.

FluidProfile — a fluid’s physical properties. Every numeric field is defaulted from water, so a file with a bare fluid entry still opens.

Field Type Default Notes
id string Not empty.
name string Not empty.
builtIn boolean False for fluids you created.
densityKgM3 number 998.2 > 0.
dynamicViscosityPaS number 0.001002 > 0.
thermalConductivityWmK number 0.598 > 0.
specificHeatCapacityJkgK number 4182 > 0.
inletTemperatureC number 20 Finite. Default inlet temperature.
glycerolVolumePercent number Optional. Set on glycerol/water blends so the UI offers the percentage control.
note string Optional. Free-text caveat shown under the fields.
nonNewtonianNote string Optional. Shown prominently when the real fluid is meaningfully non-Newtonian.

See Fluid property tables for the shipped values and where they come from.

FluidSetup — how a run is configured, as opposed to what the chip is.

Field Type Default
assignments array of FluidAssignment []
chipMaterialThermalConductivityWmK number 0.2 — typical photopolymer resin

FluidAssignment — one port’s role in the run.

Field Type Default Notes
portId string Must name a port node.
role "inlet" | "outlet"
fluidProfileId string or null null
flowRateUlPerMin number 10 ≥ 0. Ignored for an outlet.
inletTemperatureC number or null null Overrides the fluid’s own default for this run.

DrainageVent — a hole added so uncured resin can escape.

Field Type Notes
id string Not empty.
nodeId string The node the vent is placed at.
diameterMm number > 0. Default bore is 0.8 mm.
autoPlaced boolean True when the drainage planner placed it.

SavedImage — an export kept with the project.

Field Type Notes
id string Not empty.
kind "schematic-svg" | "cad-snapshot" | "heatmap"
label string
createdAt string ISO-8601.
mimeType string Not empty.
data string The payload — base64 for raster, source text for SVG.

These are stored inline, so a project with several high-resolution snapshots gets large. They are yours to delete from the Project menu.

It is plain JSON and nothing stops you. Two things to know:

  • Referential integrity is checked on load, so renaming a node id means renaming it in every edge that references it.
  • modifiedAt is overwritten on the next save. Don’t rely on it as a record of your edit.

If a file will not open, the error names the field and what it expected — the loader rewrites raw validation paths into sentences for exactly this case.