# How the Sequence Renderer Manages Participants, Segments, and Messages in Archify

> Discover how the Archify Sequence renderer manages participants, segments, and messages by transforming JSON into validated SVG geometry with precise z-order rendering.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: internals
- Published: 2026-09-01

---

**The Sequence renderer transforms a declarative JSON model into a validated SVG by materializing participants, segments, and messages into geometry objects, then rendering them in a strict z-order that places participants on top.**

The `archify/renderers/sequence/render-sequence.mjs` module implements a three-stage pipeline—**materialization**, **validation**, and **rendering**—that converts abstract sequence diagram definitions into production-ready SVG output. This article examines the internal mechanics of how the renderer handles the three core data types: **participants**, **segments**, and **messages**.

## Materialising Participants into Geometry Objects

The renderer first converts raw participant definitions into fully-computed geometry objects stored in a `Map` for O(1) lookup during message routing.

```js
const participants = new Map(
  asArray(sequence.participants).map((participant, index) => [
    participant.id,
    {
      ...participant,
      index,
      cx: participantX(index),
      x: participantX(index) - layout.participantW / 2,
      y: layout.topY,
      width: layout.participantW,
      height: layout.participantH,
      cy: layout.topY + layout.participantH / 2,
    },
  ])
);

```

Each participant receives:

- **`cx`**: Horizontal center (used for message start/end points)
- **`x`, `y`**: Top-left corner for the rendered box
- **`cy`**: Vertical center of the participant header
- **Computed dimensions**: Pulled from `layout.participantW` and `layout.participantH`

The helper `participantX(index)` (lines 62-64) calculates horizontal placement using `layout.leftX + index * layout.colGap`, distributing participants evenly across the canvas width.

## Building Segment Frames for Visual Grouping

Segments define vertical bands that group related messages into logical phases. The renderer transforms each segment into a **frame object** with explicit geometry:

```js
const compositionFrames = asArray(sequence.segments).map((segment, index) => ({
  id: index,
  label: segment.label,
  kind: 'segment',
  x: 48,
  y: segment.from,
  width: viewBox[0] - 96,
  height: segment.to - segment.from,
  radius: 10,
}));

```

Key properties:

| Property | Purpose |
|----------|---------|
| `y` / `height` | Vertical span from `segment.from` to `segment.to` |
| `width: viewBox[0] - 96` | Full canvas width minus 48px margins on each side |
| `radius: 10` | Corner rounding for the background rectangle |

These frames serve dual purposes: visual backdrop (rendered via `renderSegment` at lines 20-22) and **collision boundaries** for label placement algorithms.

## Computing Message Geometry and Routing

Message rendering depends on precise geometric calculations that connect participant centers with directional awareness:

```js
function messageGeometry(message) {
  const from = participants.get(message.from);
  const to   = participants.get(message.to);
  if (!from || !to || typeof message.y !== 'number') return null;
  const direction = to.cx > from.cx ? 1 : -1;
  const start = from.cx + direction * 7;
  const end   = to.cx - direction * 7;
  return { start, end, center: (start + end) / 2 };
}

```

The computation proceeds as follows:

1. **Lookup**: Resolve `message.from` and `message.to` against the participant `Map`
2. **Validation**: Ensure both participants exist and `message.y` is numeric
3. **Direction**: Determine left-to-right (+1) or right-to-left (-1) flow
4. **Inset**: Apply 7px padding from participant edges to avoid visual collision
5. **Center**: Pre-compute midpoint for label placement

The vertical position (`message.y`) originates from the source JSON and is validated to fall within the readable timeline bounds (lines 61-68).

## Deterministic Rendering Order in renderSvg()

The `renderSvg()` function (lines 13-44) assembles the final SVG through a carefully sequenced series of calls that establish proper visual layering:

1. **Background grid** — Static reference lines
2. **`renderSegment`** — Segment frames as shaded backgrounds
3. **`renderLifeline`** — Vertical dashed lines extending from participants
4. **`renderActivation`** — Execution bars showing active periods
5. **`renderMessage`** — Arrows and labels using `messageGeometry()` and `messageLabel()`
6. **`renderSegmentLabel`** — Phase labels with collision avoidance
7. **`renderParticipant`** — Participant boxes, sublabels, and brand marks
8. **`renderLegend`** — Optional key/explanation block

**Critical design decision**: Participants render **last** so they visually override lifelines and message endpoints, matching standard sequence diagram conventions where participant headers appear "in front" of connecting lines.

## Validation Pipeline Before SVG Generation

The `validateSequence()` function (lines 35-91) executes a comprehensive suite of checks that prevent malformed output:

- **Identity**: Unique participant IDs across the diagram
- **Space**: Minimum 120px between `lifelineBottom` and `lifelineTop`
- **Fit**: Participant labels must fit within computed box widths
- **Legibility**: `availableNodeTextWidth` ensures sublabel readability
- **Proximity**: Message endpoints must be ≥ 60px from participant centers
- **Collision**: `cleanFlowProblems` and `cleanCrossingProblems` detect arrow overlaps, label intersections, and segment frame conflicts

Failed validations trigger `throwDiagnosticProblems`, which aborts rendering and returns a detailed error list rather than producing broken SVG.

## Complete Input-to-Output Example

This minimal JSON demonstrates the full data model:

```json
{
  "participants": [
    { "id": "alice", "label": "Alice", "type": "external" },
    { "id": "bob",   "label": "Bob",   "type": "external" }
  ],
  "messages": [
    { "from": "alice", "to": "bob", "label": "Hello", "y": 200 }
  ],
  "segments": [
    { "label": "Login Flow", "from": 150, "to": 300 }
  ]
}

```

Excerpted SVG output:

```svg
<svg viewBox="0 0 920 760" …>
  <rect x="48" y="150" width="824" height="150" rx="10" class="c-lane"/>
  <path d="M 150 200 L 770 200" class="a-default" …/>
  <text x="460" y="190" class="t-backend" font-size="9" text-anchor="middle">Hello</text>
  <g …>
    <rect x="62" y="72" width="86" height="54" rx="6" class="c-mask"/>
    <rect … class="c-external"/>
    <text …>Alice</text>
  </g>
</svg>

```

The segment renders as a background rectangle, the message as a horizontal path with centered label, and participants as layered groups with masking rectangles to ensure clean edges.

## Source Code Architecture

| File | Responsibility |
|------|--------------|
| `archify/renderers/sequence/render-sequence.mjs` | Core pipeline: materialization, validation, SVG assembly |
| `archify/shared/geometry.mjs` | Collision detection (`rectsOverlap`, `cleanFlowProblems`) |
| `archify/shared/utils.mjs` | Utilities (`asArray`, `textUnits`, `esc`) |
| `archify/shared/cli.mjs` | SVG serialization (`svgRootAttrs`, `writeDiagram`) |
| [`archify/schemas/sequence.schema.json`](https://github.com/tt-a1i/archify/blob/main/archify/schemas/sequence.schema.json) | JSON Schema for input validation |

## Summary

- **Materialization** converts abstract JSON entities into geometry objects with computed coordinates
- **Participants** store center points (`cx`, `cy`) that enable efficient message routing
- **Segments** become frame objects with explicit bounds for background rendering and collision detection
- **Messages** derive horizontal endpoints from participant geometry plus directional insets
- **Validation** enforces spatial constraints and prevents visual collisions before any SVG is generated
- **Rendering order** places participants last in the z-stack to achieve conventional diagram aesthetics

## Frequently Asked Questions

### What data structure stores participants during rendering?

A JavaScript `Map` keyed by participant ID. This provides O(1) lookup when resolving `message.from` and `message.to` references during message geometry computation.

### How does the renderer prevent message arrows from overlapping participant boxes?

The `messageGeometry()` function applies a 7-pixel inset using `direction * 7` on both ends, ensuring arrows start and stop slightly inside the participant center lines rather than at exact edges.

### Why are participants rendered after messages in the SVG output?

The `renderSvg()` sequence (lines 13-44) deliberately places `renderParticipant` near the end so participant headers visually cover lifeline extensions and message endpoints. This matches standard sequence diagram conventions where participant boxes appear "on top."

### What happens if validation fails in validateSequence()?

The renderer calls `throwDiagnosticProblems`, which aborts execution and returns a structured list of errors rather than generating malformed SVG. Checks include unique IDs, minimum spacing, label fit, and collision detection between arrows, labels, and segment frames.