Pie Chart

One radial control, four personalities. pieStyle picks the mode:

  • pie / donut — the classic part-of-whole chart, with tappable slices, an optional exploding selection, and a donut center readout.
  • wheel — a spin-to-select wheel: flick (or tap) to spin, physics wind it down with a haptic tick per slice, and the winning slice fires the action. Slice values become raffle weights, so a weighted giveaway is just data.
  • menu — a radial command pad: equal slices with icons, each slice a button. Eight actions in the footprint of one round control.

Type

"pieChart"

Square cells — [2, 2] minimum, [3, 3] for wheels with many slices. The control keeps itself round.

Relevant Fields

Inherits all shared fields. Key fields:

Field Type Default Description
pieStyle string "pie" pie | donut | wheel | menu
pieConfig PieConfig Full configuration object (all optional)
tint string "#667eea" First-slice color / palette seed
icon string SF Symbol in the wheel/menu hub before a result lands
formatValue string Formatter for slice/center values
hideValue bool false Suppress the donut/hub center readout

PieConfig

Field Type Default Description
donutRatio number per style Inner-radius fraction 0–0.85 (pie 0, donut 0.62, wheel 0.18, menu 0.3)
startAngle number 0 Degrees the first slice starts at (0 = 12 o'clock, clockwise)
showLegend bool false Wrapping dot-chip legend under the chart
showLabels bool true* Slice labels (auto-hidden on thin slices; *menu defaults false — icons lead)
showValues bool false Append each slice's value to its label
percent bool false Show values as % of the total
centerText string Donut center text; {{total}} interpolates the slice sum
explodeSelected bool true** Tapped slice pops outward (**wheels default false)
colors string[] built-in palette Slice color cycle (per-slice color wins)
sliceAction ActionDefinition Fired on slice tap and wheel landing; {{value}} = slice label
spinDuration number 4 Approximate seconds a full-strength spin takes to settle
spinTicks bool true Haptic tick per slice boundary while spinning

If sliceAction is omitted, taps/landings fall back to the control's own action.

Sync Payload Structure

{"slices": […]} or a bare array of slices — natural JSON or an encoded string:

{
  "slices": [
    { "label": "Heating", "value": 41, "color": "#FF6B6B" },
    { "label": "Compute", "value": 33, "color": "#667eea" },
    { "label": "Idle",    "value": 26 }
  ]
}

Slice Fields

Field Type Required Description
label string yes Display name — also the {{value}} an action receives
value number no Share of the whole / raffle weight (default 1; menu mode ignores it)
color string no Slice color (hex)
icon string no SF Symbol drawn in the slice (menu mode leads with it)

Examples

Donut with live total

{
  "type": "pieChart",
  "id": "power-split",
  "position": [0, 0],
  "span": [2, 2],
  "label": "Power draw",
  "pieStyle": "donut",
  "pieConfig": { "centerText": "{{total}}W", "showLegend": true, "percent": true },
  "sync": [{ "method": "meshsocket", "type": "listen", "event": "broadcast", "filter": { "msg_type": "power" }, "valuePath": "split" }]
}

Spin-to-select wheel (weighted raffle)

{
  "type": "pieChart",
  "id": "prize-wheel",
  "position": [0, 0],
  "span": [3, 3],
  "label": "Who's on call?",
  "pieStyle": "wheel",
  "pieConfig": {
    "spinDuration": 5,
    "sliceAction": { "method": "meshsocket", "mode": "broadcast", "event": "broadcast", "payload": { "msg_type": "oncall_pick", "person": "{{value}}" } }
  },
  "defaultValue": "{\"slices\":[{\"label\":\"Ava\",\"value\":1},{\"label\":\"Ben\",\"value\":1},{\"label\":\"Cass\",\"value\":2},{\"label\":\"Drew\",\"value\":1}]}"
}

Radial command menu

{
  "type": "pieChart",
  "id": "scene-menu",
  "position": [0, 0],
  "span": [2, 2],
  "label": "Scenes",
  "pieStyle": "menu",
  "icon": "sparkles",
  "pieConfig": {
    "sliceAction": { "method": "meshsocket", "mode": "broadcast", "event": "broadcast", "payload": { "msg_type": "scene", "name": "{{value}}" } }
  },
  "defaultValue": "{\"slices\":[{\"label\":\"Movie\",\"icon\":\"tv\"},{\"label\":\"Focus\",\"icon\":\"moon\"},{\"label\":\"Party\",\"icon\":\"music.note\"},{\"label\":\"Off\",\"icon\":\"power\"}]}"
}

Behavior

  • Slice datasets animate between pushes; taps highlight (and optionally explode) a slice
  • Wheel: flick to spin with your own force, or tap for a strong randomized spin; the pointer sits at 12 o'clock; each boundary crossing ticks; settling fires a success haptic + the action with the winner, and the hub shows the result
  • Menu: every slice is a button; the hub echoes the last pick
  • The wheel result is delivered via action only — the control's value stays the dataset, so a re-render never re-fires a pick

Notes

  • Wheel odds follow value weights — equal weights = fair spin, and the server can re-weight live between spins
  • Slices under ~4% of the circle drop their inline label; use showLegend for long tails
  • A polar/rose chart is a pie whose values map to radius — out of scope v1
  • Chart — cartesian bar/line/area/scatter
  • Gauge — single-value radial readout
  • Progress Ring — single-fraction ring
  • Actions — how slice/wheel actions fire
  • Sync — how slice data arrives