{
  "docVersion": 1,
  "id": "template-multiplayer-canvas-figma-style",
  "name": "Multiplayer canvas",
  "purpose": "Figma-style multiplayer: clients stream property deltas and cursor positions over websockets to a per-file session actor, which merges via delta-CRDTs, rebroadcasts, and persists a compacted changelog.",
  "nodes": {
    "c1": {
      "type": "user",
      "label": "Designer A",
      "x": 20,
      "y": 60,
      "runtime": "drags rect #7"
    },
    "c2": {
      "type": "user",
      "label": "Designer B",
      "x": 20,
      "y": 280,
      "runtime": "recolors rect #7"
    },
    "c3": {
      "type": "user",
      "label": "Viewer",
      "x": 20,
      "y": 500,
      "runtime": "read-only · cursors visible"
    },
    "gw": {
      "type": "websocket",
      "label": "Connection gateway",
      "x": 340,
      "y": 280,
      "runtime": "sticky by file id",
      "color": "blue",
      "patterns": [
        "autoscale"
      ],
      "notes": "Fans thousands of sockets into per-file sessions. Presence (cursors, selections) is filtered here — throttled, coalesced, and never sent to storage."
    },
    "sess": {
      "type": "server",
      "label": "File session (actor)",
      "x": 660,
      "y": 280,
      "runtime": "delta-CRDT map · authority",
      "color": "purple",
      "patterns": [
        "leader-elected"
      ],
      "notes": "The single ordering point for one file: applies deltas to the object map (per-property LWW — A's position and B's fill on rect #7 BOTH win), rebroadcasts minimal updates."
    },
    "log": {
      "type": "storage",
      "label": "Changelog",
      "x": 980,
      "y": 140,
      "runtime": "deltas · compacted",
      "patterns": [
        "backup"
      ],
      "notes": "Compaction keeps only the latest value per object property — a year of editing compacts to the size of the file."
    },
    "cdn": {
      "type": "cdn",
      "label": "Asset CDN",
      "x": 980,
      "y": 420,
      "runtime": "images · fonts · exports"
    }
  },
  "edges": {
    "e1": {
      "from": "c1",
      "to": "gw",
      "fromSide": "right",
      "toSide": "left",
      "label": "deltas + cursor @60Hz",
      "animated": true,
      "patterns": [
        "batch",
        "backpressure"
      ]
    },
    "e2": {
      "from": "c2",
      "to": "gw",
      "fromSide": "right",
      "toSide": "left",
      "animated": true
    },
    "e3": {
      "from": "c3",
      "to": "gw",
      "fromSide": "right",
      "toSide": "left",
      "label": "subscribe",
      "style": "dashed"
    },
    "e4": {
      "from": "gw",
      "to": "sess",
      "fromSide": "right",
      "toSide": "left",
      "label": "mux by file",
      "animated": true
    },
    "e5": {
      "from": "sess",
      "to": "gw",
      "fromSide": "top",
      "toSide": "top",
      "label": "broadcast merged deltas + presence",
      "style": "dashed",
      "color": "purple"
    },
    "e6": {
      "from": "sess",
      "to": "log",
      "fromSide": "right",
      "toSide": "left",
      "label": "append → compact",
      "patterns": [
        "ordered"
      ]
    },
    "e7": {
      "from": "c3",
      "to": "cdn",
      "fromSide": "bottom",
      "toSide": "bottom",
      "label": "fetch assets",
      "style": "dashed",
      "color": "gray"
    }
  },
  "boxes": {
    "b1": {
      "x": 620,
      "y": 40,
      "w": 660,
      "h": 560,
      "label": "per-file authority + durability",
      "color": "purple",
      "opacity": 0.04
    }
  },
  "texts": {
    "t1": {
      "x": 340,
      "y": 500,
      "text": "why both edits win: A moved rect #7, B recolored it —\ndifferent properties of the same object are independent\nCRDT registers. True conflict (both move it) = LWW per\nproperty, which is fine for geometry.",
      "color": "purple",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}