Automatic Port Spread in Archify: How the Routing Algorithm Distributes Connection Points

Archify's automatic port spread is a renderer-level algorithm that distributes multiple relationship connection points across a component's side to prevent visual overlap, applying calculated offsets only when no explicit routing controls are specified.

Automatic port spread is one of Archify's core layout primitives. When multiple edges originate from or terminate at the same side of a component, the algorithm groups, sorts, and offsets each port to create clean, deterministic fan-out and fan-in patterns—without requiring manual placement of via points, channelX/Y constraints, or labelAt overrides.

How Automatic Port Spread Works

The implementation lives in archify/renderers/shared/geometry.mjs, specifically the automaticPortSpread function (lines 1020–1055). This utility operates as a preprocessing step before the main routing pipeline generates final SVG or HTML paths.

The Five-Step Algorithm

1. Group relationships by side

The function first collects relationships that share the same component side into distinct groups. Each group represents all edges entering or exiting a specific face of a rectangle.

2. Sort by counterpart coordinate

Within each group, relationships are sorted by the opposite endpoint's coordinate. This sorting ensures deterministic output—the same diagram structure always produces identical port placements.

3. Compute usable space

The algorithm calculates available space as extent – gutter * 2, where extent is the length of the component side and gutter is a configurable minimum margin (default 16px). A safe maxSpacing is derived from this usable space.

4. Calculate per-port offsets

Each relationship receives an offset from the canonical anchor point (computed via anchor(item.rect, item.side)). The offset magnitude depends on the relationship's index in the sorted group: middle items get minimal displacement, while outer items spread toward the edges.

5. Return adjusted endpoints

The function returns a map of relationship IDs to their adjusted endpoint coordinates, which the renderer later incorporates into polyline paths.

Anchor Calculation and Side Selection

The anchor utility (lines 1010–1025 in geometry.mjs) determines the base position for any (rect, side) pair. For automatic port spread, this anchor becomes the zero-point from which offsets are applied:

// Canonical anchor for a right-side exit
const basePoint = anchor(hubRect, 'right'); // → { x: 220, y: 310 }
// Spread-adjusted port for third relationship in group
const spreadPoint = { x: 220, y: 324 }; // base.y + 14px offset

Conditional Application: Respecting Explicit Routes

Automatic port spread applies only to "plain" relationships—those without explicit routing controls. The algorithm checks for these overrides before activating:

  • via — explicit path waypoints
  • channelX or channelY — forced outside-channel routing
  • labelAt — label positioning that affects geometry
  • Custom route functions

When any of these are present, Archify preserves the author-specified geometry and skips automatic spreading. This behavior ensures that manual fine-tuning always takes precedence over algorithmic defaults.

Integration with the Routing Pipeline

After port spread calculation, results feed into Archify's standard routing pipeline. The final path generation may apply additional transformations:

Rhythm-Bridge Fallback

If a calculated route would violate Archify's 8px/16px rhythm constraints, the renderer invokes automaticPortRhythmBridge (lines 1027–1038). This fallback constructs an outside-channel route that maintains visual alignment with the diagram's grid system.

The rhythm bridge operates as a safety net: it guarantees that even aggressive port spreads or unusual component placements produce valid, rhythm-compliant output.

Practical Example: Hub-and-Spoke Layout

Consider a backend hub connecting to three external services:

const doc = {
  schema_version: 1,
  diagram_type: 'architecture',
  meta: { title: 'Automatic fan-out' },
  components: [
    { id: 'hub', type: 'backend', label: 'Hub', pos: [100, 280], size: [120, 60] },
    { id: 'svcA', type: 'external', label: 'A', pos: [500, 100], size: [120, 60] },
    { id: 'svcB', type: 'external', label: 'B', pos: [500, 280], size: [120, 60] },
    { id: 'svcC', type: 'external', label: 'C', pos: [500, 460], size: [120, 60] },
  ],
  connections: [
    { id: 'to-A', from: 'hub', to: 'svcA' },
    { id: 'to-B', from: 'hub', to: 'svcB' },
    { id: 'to-C', from: 'hub', to: 'svcC' },
  ],
};

Rendering this diagram triggers automatic port spread on the hub's right side. The three connections receive distinct ports:

Connection Port Coordinate Offset from Center
to-A (top) (220, 296) -14px
to-B (middle) (220, 310) 0px
to-C (bottom) (220, 324) +14px

These coordinates derive from the spread algorithm's sorting (by destination Y-coordinate) and spacing calculations.

Direct API Access

While primarily used internally, the spread function can be accessed for custom renderers or testing:

import { automaticPortSpread } from './archify/renderers/shared/geometry.mjs';

// boxes: Map<componentId, { x, y, width, height, cx, cy }>
const spreadMap = automaticPortSpread(relations, boxes, {
  gutter: 16,      // minimum margin from corner
  maxSpacing: 14   // maximum distance between adjacent ports
});

// spreadMap: Map<relationId, { x, y }> — adjusted endpoints

Options control the trade-off between edge density and margin preservation. Lower maxSpacing values create tighter groupings; higher values spread connections more aggressively toward component corners.

Renderer Coverage

Automatic port spread is implemented across all Archify diagram types:

Renderer File Application
Architecture render-architecture.mjs Component dependency diagrams
Workflow render-workflow.mjs Lane-based process flows
Dataflow render-dataflow.mjs Stage-to-stage data pipelines
Lifecycle render-lifecycle.mjs State transition diagrams

Each renderer invokes the shared automaticPortSpread function with diagram-specific parameters, ensuring consistent behavior across visual conventions.

Summary

  • Automatic port spread distributes connection points along component sides to prevent edge overlap in dense diagrams.
  • The algorithm lives in archify/renderers/shared/geometry.mjs and operates through grouping, sorting, spacing calculation, and offset application.
  • Spread only activates for relationships without explicit via, channelX/Y, labelAt, or custom route controls.
  • Results feed into standard routing with possible automaticPortRhythmBridge fallback for rhythm compliance.
  • The function is used across all Archify renderers (architecture, workflow, dataflow, lifecycle) for consistent automatic layout.

Frequently Asked Questions

How does Archify determine which relationships to group for port spreading?

Archify groups relationships that share the same component side (top, right, bottom, or left) and the same connection direction (incoming or outgoing). Within each group, relationships are sorted by the coordinate of their opposite endpoint—Y-coordinate for left/right sides, X-coordinate for top/bottom sides. This sorting produces deterministic, visually logical ordering where higher connections attach to higher counterpart components.

What happens if automatic port spread violates Archify's rhythm constraints?

When calculated offsets or resulting paths would break the 8px/16px rhythm floor, the renderer invokes automaticPortRhythmBridge (lines 1027–1038 in geometry.mjs). This fallback constructs an outside-channel route that maintains grid alignment. The rhythm bridge preserves logical connectivity while ensuring the final output satisfies Archify's visual consistency requirements.

Can I disable automatic port spread for specific connections?

Yes. Automatic port spread is automatically disabled for any relationship that specifies explicit routing controls: via waypoints, channelX or channelY constraints, labelAt positioning, or a custom route function. To prevent spreading without adding geometry, you can specify an empty via: [] array or set explicit but neutral channel values.

Where can I find test coverage for the port spread algorithm?

Comprehensive tests reside in archify/test/automatic-port-spread.test.mjs. This suite validates spread behavior across all diagram types, verifies deterministic output for identical inputs, checks boundary conditions at component edges, and confirms that explicit routing controls correctly suppress automatic spreading.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →