Sankey

Flow volumes between nodes: energy from panels to loads, requests through services, budget through categories. Nodes stack into columns (sources left, sinks right — computed from the link graph), ribbons run between them with thickness ∝ value and a color gradient from source to target. Tapping a node spotlights its flows — everything else fades — and fires nodeAction with the node's in/out totals.

Type

"sankey"

Wide: [3, 4] and up. In a flow grid it auto-sizes to a ~3:2 landscape cell.

Relevant Fields

Inherits all shared fields. Key fields:

Field Type Default Description
label string Header label (selection readout appears beside it)
sankeyConfig SankeyConfig Full configuration object (all optional)
tint string "#667eea" First-node color / palette seed
formatValue string Formatter for throughput readouts
action ActionDefinition Fallback for nodeAction

SankeyConfig

Field Type Default Description
nodeWidth number 10 Node bar width in points
nodeSpacing number 10 Minimum vertical gap between nodes in a column
showLabels bool true Node names beside the bars
showValues bool false Throughput value alongside each label
linkOpacity number 0.32 Ribbon opacity (selection brightens involved ribbons)
colors string[] built-in palette Node color cycle (per-node color wins)
nodeAction ActionDefinition Fired on node tap; {{value}} = {"name", "in", "out"}
scrub bool false Drag sweeps the spotlight from node to node with haptic ticks (the action still fires on tap)

Sync Payload Structure

Nodes plus source→target→value links — natural JSON or an encoded string. Endpoints may be node names or indices:

{
  "nodes": [
    { "name": "Solar", "color": "#FFD93D" },
    { "name": "Battery" },
    { "name": "House" },
    { "name": "Grid" }
  ],
  "links": [
    { "source": "Solar",   "target": "Battery", "value": 3.2 },
    { "source": "Solar",   "target": "House",   "value": 1.8 },
    { "source": "Battery", "target": "House",   "value": 2.1 },
    { "source": "Solar",   "target": "Grid",    "value": 0.9 }
  ]
}

Self-links, unknown endpoints, and zero/negative values are dropped. Cycles don't hang the layout, but a mostly-acyclic flow reads far better.

Examples

Live energy flow

{
  "type": "sankey",
  "id": "energy",
  "position": [0, 0],
  "span": [3, 4],
  "label": "Energy (kW)",
  "sankeyConfig": { "showValues": true },
  "sync": [{ "method": "meshsocket", "type": "listen", "event": "broadcast", "filter": { "msg_type": "power_flow" }, "valuePath": "sankey" }]
}

Tap-to-inspect service traffic

{
  "type": "sankey",
  "id": "traffic",
  "position": [0, 0],
  "span": [3, 4],
  "label": "Requests/min",
  "sankeyConfig": {
    "nodeAction": { "method": "meshsocket", "mode": "broadcast", "event": "broadcast", "payload": { "msg_type": "inspect_service", "node": "{{value}}" } }
  },
  "defaultValue": "{\"nodes\":[{\"name\":\"Edge\"},{\"name\":\"API\"},{\"name\":\"DB\"},{\"name\":\"Cache\"}],\"links\":[{\"source\":0,\"target\":1,\"value\":120},{\"source\":1,\"target\":2,\"value\":80},{\"source\":1,\"target\":3,\"value\":40}]}"
}

Behavior

  • Columns come from the longest path from the sources; pure sinks align to the last column; each column is vertically centered
  • Node height ∝ max(inflow, outflow); ribbons attach in far-end order so they fan instead of crossing at the bar
  • Tap a node to spotlight it (its ribbons brighten, unrelated ones fade to a whisper) and fire nodeAction; tap empty space to clear
  • scrub: true lets a drag sweep the spotlight fluidly across nodes with a tick per hand-off
  • Dataset pushes animate via the control's animation profile

Notes

  • ≤ 4 columns and ≤ ~10 nodes stay readable on a phone
  • The control's value is the dataset JSON — selection never overwrites it
  • For pairwise relationships without direction/stages, Chord is the circular sibling
  • Chord — circular inter-relationships
  • Graph — force-directed node-link topology
  • Pie Chart — single-stage part-of-whole
  • Sync — how flows arrive
  • Actions — how nodeAction fires