scrml.dev v0.7.1
Reference › Elements

<channel>

Real-time multi-client state. Declared cells auto-sync across connected clients over WebSocket. V5-strict body; the compiler generates the endpoint, the wire protocol, and the reconnect logic.

Syntax

<channel name="chat" topic="lobby">
  <cell> = initialValue
  ...
</channel>

name= is the channel identifier (drives the WS endpoint /_scrml_ws/<name>). topic= optionally partitions clients (defaults to name). Inside the body, V5-strict declarations (<cell> = init) create channel-scoped reactive cells that auto-sync across subscribed clients.

Worked example

A minimal chat room. One channel, one synced list, one publisher function. The cell write runs on the CLIENT and syncs to every subscriber automatically (§38.4) — it needs no server keyword, and a server-escalated function may not read a channel cell at all.

<program>

  <channel name="chat" topic="lobby">
    <messages> = []

    ${ function postMessage(author, body) {
        @messages = [...@messages, { author, body, ts: Date.now() }]
    } }
  </channel>

  ${ const count = @messages.length }

</program>

<messages> is declared inside the channel body, so it auto-syncs. Anywhere else in the <program>, @messages reads it — program-scope visibility for channel cells is the canonical pattern (per §38.4 + Insight 30).

Semantics

  • Children of <program>, not file-level siblings. Channels live INSIDE the entry-file <program> (sibling of <page>). Pure channel-module files (no <program> in the file) MAY declare channels at file top — the cross-file inline-expansion pattern per §38.12.6. Per Insight 30 (S87 placement reversal).
  • No @shared modifier. Auto-sync is a consequence of being declared inside the channel body, NOT a per-cell modifier. E-CHANNEL-SHARED-MODIFIER fires on the legacy form. Per §38.4.
  • V5-strict body. <cell> = init declares; @cell reads / writes. Same access discipline as everywhere else in scrml.
  • Auto-injected helpers in server functions. Inside a channel body, server functions get broadcast(data) and disconnect() injected into scope. Use broadcast for one-shot messages that don't fit a reactive cell; use cell writes for state every client should track.
  • Event handlers. onserver:message=handler(msg) and onclient:connect=handler() handle the underlying WS events. Handler params (msg) are function-local locals, accessed bare. V5-strict local semantics; not state.
  • Compiler generates the wire. WebSocket endpoint at /_scrml_ws/<name> ; reconnect logic (channel-reconnect=N on <program> sets the project-level default); payload serialization; per-topic broadcast graph. The developer writes the declared cells; the rest is emitted.

Errors this feature can fire

Edge cases

  • Pure channel-module files. A .scrml file with no top-level <program> may declare channels at file top. Other entry files import the channel as a unit; the compiler inlines the channel's cells into the importer's <program> (the "channel-module sharing pattern", §38.12.6).
  • topic= is the partition key. Multiple clients on the same channel name= but different topic= are isolated. The default is topic=name — one global room. For per-document rooms, derive topic= from the current route.
  • Channel writes from clients are server- mediated. A client can only write a channel cell via a server function declared inside the channel body. The wire protocol passes through the compiler-generated server-fn endpoint, which writes the cell and broadcasts to peers. Direct client-side @cell = ... writes are scoped to the local client.
  • Channel-reconnect default. Project- level reconnect window via <program channel-reconnect=N> (S81 §38.3.1). Set to zero to disable auto-reconnect on transient WS drops.

Related features

  • <program> — channels live as children of the entry-file <program>. channel-reconnect=N on <program> sets the default reconnect window.
  • server function — inside a channel body, server fns get broadcast and disconnect injected. Reference page queued.
  • server function* (SSE generators, §37) — the one-way server-to-client equivalent; lighter than a channel when clients don't write back.

Availability

Surface Since Notes
V5-strict body / no @shared D3 rewrite (2026-05-04) Major SPEC §38 rewrite — @shared removed; auto-sync from placement
Channels as <program> children Insight 30 (S87) Placement reversal — channels inside <program>, not file-level
channel-reconnect=N on <program> S81 (§38.3.1) Project-level reconnect window default
Cross-file channel-module inline expansion §38.12.6 Pure-channel files import as a unit

Specification

This page summarizes SPEC §38 (WebSocket Channels). The normative text lives at compiler/SPEC.md in the scrmlTS repository.

Quick-lookup anchors: SPEC-INDEX.md entries — channel file-level placement, channel V5-strict body, v1→v0.next channel migration.