OrbitDiagram renders a hierarchy as concentric rings around a center. The root node is at the middle, depth-1 children on the first orbit, depth-2 on the next, and so on. Children rotate around their parent slowly enough to read; the composition is intentionally evocative. That's the point: it works when the goal is to make the existence of structure feel like something a viewer wants to spend time with or draw their attention to.
Why this exists
Most hierarchy charts (TreeDiagram,Treemap, CirclePack) are analytic: tree for path-and-depth questions, treemap for "which subtree is the biggest," circle pack for size-with-hierarchy. OrbitDiagram is the answer to a different question:"how do I show off that this thing has structure, without making my audience read a spreadsheet?"Org charts in conference talks, system architecture diagrams in product launches, taxonomy explorations on landing pages, the kind of executive report visual where the reader is meant to nod and feel oriented rather than make a decision.
The animation isn't gratuitous. A static radial layout leaves the viewer wondering whether the spatial position of any specific node matters. The slow rotation tells the eye: position is decorative; ring membership is the encoding. And, importantly: orbits are hierarchies. It is a natural metaphor for hierarchies that any audience who is aware of the structure of our solar system or an atom would understand.
Live demo
The solar system, because the metaphor IS the layout. Sun at the center, eight planets on the first orbit out, the named moons of each planet on a second orbit ring around their planet. Sizes are log-scaled earth-masses, so Jupiter and Saturn dominate their inner-system neighbors and the Moon is a recognizable dot next to Earth.
How to read it
- Rings encode the depth level of the hierarchy. Depth-1 children sit on the first orbit out from center, depth-2 on the next, etc.
- Rotation reinforces the hierarchies.
When to reach for it
Reach for OrbitDiagram when:
- The audience needs to be engaged. Showcase pages, intro slides, "here's the shape of our org" briefings.
- The hierarchy is small enough that labels stay readable (≤30 leaves works; beyond that they overlap).
- The chart's job is "look, structure" rather than "answer this question."
Reach for something else when:
- The audience needs to compare sizes precisely. Use aTreemap, it's unbeatable for "which subtree is biggest."
- Path-from-root matters. Use a TreeDiagram which shows lineage explicitly.
- The hierarchy is deep (5+ levels) or wide (50+ leaves). OrbitDiagram's rings get crowded, and CirclePack handles deep nesting better.
Wiring it up
import { OrbitDiagram } from "semiotic" <OrbitDiagram data={root} // hierarchical { name, children: [...] } childrenAccessor="children" nodeIdAccessor="name" // label + ID field on each node nodeRadius={(n) => 4 + Math.log10(n.data?.mass ?? 1) * 2} colorByDepth orbitMode="flat" // or "tilted" speed={0.2} // radians/sec animated showLabels />Two layout modes: orbitMode="flat" (concentric rings, 2-D) andorbitMode="tilted" (rings tilted toward the viewer, faux-3-D). Both rotate continuously unless animated={false} is set.
Streaming / push mode
OrbitDiagram is the one chart family in Semiotic wherepush mode doesn't apply. The hierarchy layout has to consider the whole tree at once. Sibling positions on each orbit depend on how many children each parent has, the orbit radii depend on tree depth, the animation speed reads against the full ring. There's no "push one more leaf" that has a sensible meaning; appending a node restructures the layout.
The supported pattern when the underlying hierarchy changes is: keep your tree in state, mutate it, pass the new tree as the data prop. The chart's animated transitions still pick up: orbit radii ease, nodes that survive between trees keep their angular position, new nodes fade in, removed nodes fade out. Same visual result as push for charts that support it; just a different mechanism under the hood.
const [tree, setTree] = useState(initialTree) // Adding a moon to Saturn setTree((root) => structuredClone(root, (...) => /* mutate */)) <OrbitDiagram data={tree} childrenAccessor="children" nodeIdAccessor="name" />Same family applies to TreeDiagram,Treemap, andCirclePack: all hierarchy HOCs are state-prop driven rather than push-driven. Flat-data chart families (XY, ordinal, network with explicit edges, geo) all support push.