Helios

Architecture

Layers, scheduling, persistence boundary, and extension points.

Scope

Stable domain of this package:

  • universe/priority output addresses
  • 512-slot frames and sparse one-based channel writes
  • linear fades (shared or per-channel)
  • active-rate sends and idle keepalives
  • selected-universe packet viewing

Out of scope: fixtures, personalities, attributes, scenes, cues, show control, patching, licensing. A higher-level library can resolve those into frames and call a source.

Layers

Application / fixture layer
            |
     SacnSource + Universe
        /            \
 output engine    output store
       |            /      \
 transport     memory     Redis
       |
  Node sACN transport

Node receiver → ViewerService → callbacks / streams
                      |
                 viewer store

Host Elysia app → HTTP adapter → source + viewer

The root module does not open sockets or read environment variables at import time. Adapters are replaceable behind small contracts.

Scheduling

Each (universe, priority) pair is an independent output. State includes the current interpolated frame, target frame, last sent frame, transitions, sequence, identity, and next due time. Universe handles are a cached view — they do not own separate engine state.

Dirty or fading outputs run at activeFps (default 44). Stable outputs send keepalives at idleFps (default 2). Fades use a monotonic clock; persisted updatedAt is not the interpolation clock.

Mutations are ordered through the source. Transport sends are serialized per output so a timeout cannot overlap another send, and a clear cannot be followed by a stale frame.

Persistence boundary

Durable intent only: address, CID, source suffix, idle rate, target frame, update time. Runtime-only: interpolation, sequence, dirty flags, last-send telemetry.

On restore, the engine resumes toward the persisted target. Stores are invoked in mutation order. Redis (and other trust-boundary adapters) validate records on load.

Viewer backpressure

The receiver normalizes packets; the viewer filters by selection and pads to 512 slots. Callbacks are synchronous and isolated. Streams coalesce by universe and drop oldest-at-capacity. Reception never waits on slow consumers.

HTTP boundary

The Elysia plugin is routes only. Binding, TLS, CORS, auth, rate limits, and process signals stay in the host. Compatibility /outputs… routes are migration aids with deprecation metadata.

Extension points

Implement root contracts:

  • OutputTransport — another sender, recorder, or gateway
  • OutputStore / ViewerStore — another backend
  • Receiver — another packet source
  • Clock — deterministic tests
  • Logger — host observability

Custom HTTP should depend on SacnSourceContract, not engine internals. Future fixture packages should generate frames without expanding this domain.

subscribe on the source exposes lifecycle and output events without leaking mutable scheduler state.

On this page