Skip to main content

Bridge

The bridge is a typed message bus between a running game (@zylem/game-lib) and an editor overlay (@zylem/editor). Both sides depend on @zylem/bridge only—no cross-imports—so zylem-game and editor custom elements stay decoupled while sharing one contract (packages/bridge/src/protocol.ts).

Game-lib exposes:

  • getZylemBridge() — shared channel singleton
  • GameBridge — game-side adapter wired inside ZylemGame (publish snapshots, handle commands)
  • announceBridgeReady, applySwatchesToSelection, BRIDGE_READY_EVENT
  • Re-exported payload types from @zylem/bridge

External hosts subscribe or send on getZylemBridge().channel after the zylem-game element fires zylem:bridge:ready.

import { getZylemBridge, BRIDGE_READY_EVENT } from '@zylem/game-lib/bridge';

gameElement.addEventListener(BRIDGE_READY_EVENT, () => {
const { channel } = getZylemBridge();
channel.on('stage:snapshot', (snap) => { /* … */ });
channel.send('playback:set', { paused: false });
});

High-frequency publishes use the channel queue so at most one message per animation frame coalesces (entity upserts, thumbnails).

Game → editor (GameToEditorMessages)​

MessagePayloadPurpose
game:configGameConfigPayloadGame id, aspect, fullscreen, resolution, debug flag
game:loadingGameLoadingPayloadLoad lifecycle: start / progress / complete, optional stage index
game:status{ paused?, debug? }Runtime flags
game:variableGameVariablePayloadGlobal variable change (path, value, previousValue)
game:noticeGameNoticePayloadConsole notice: info / warn / error + message
stage:snapshotStageSnapshotPayloadStage config + full entity list
entity:upsertEntitySummaryPayload[]Create or update entity summaries
entity:removed{ uuids: string[] }Entities left the stage
entity:thumbnailEntityThumbnailPayload[]Preview URL + bounds per entity
entity:selectionEntitySelectionPayloadSelected/hovered uuids (multi-select via selectedUuids)
catalog:snapshot{ entities: EntityTypeDescriptor[] }Add palette types
scene:operationSceneOperationPayloadCommitted undoable edit (transform, create, delete, swatch)
entity:pick:resultEntityPickResultPayloadRaycast answer for entity:pick
entity:swatch-appliedEntitySwatchAppliedPayloadOutcome of entity:apply-swatch
cutscene:statusCutsceneStatusPayloadCutscene id, playback state, time, scene/shot ids
cutscene:viewCutsceneViewPayloadView/projection matrices + viewport for overlays
camera:poseCameraPosePayloadAnswer to camera:pose:get

Editor → game (EditorToGameMessages)​

MessagePayloadPurpose
debug:set{ enabled: boolean }Toggle debug overlay
tool:set{ tool: BridgeDebugTool }select, translate, rotate, scale, delete, add, none
playback:set{ paused: boolean }Pause or resume simulation
entity:selectEntitySelectPayloadSelect one uuid, many uuids, or clear; mode: replace, add, subtract, toggle
entity:focus{ uuid: string }Frame entity in view
entity:transformuuid + optional position, rotation, quaternion, scaleApply transform (quaternion authoritative when set)
entity:create{ typeId, props?, pose? }Spawn catalog type
scene:operation:apply{ op: SceneOperationPayload, direction: 'undo' | 'redo' }Replay undo stack entry
add:type:set{ typeId: string | null, props? }Arm Add tool with catalog type or disarm
snap:setSnapSettingsPayloadTranslation / rotation / scale snap increments
grid:set{ visible: boolean }Construction grid visibility
stage:variable:set{ key, value }Write stage variable
pick:mode:set{ enabled: boolean }Hover picking while dragging swatches
entity:pickEntityPickPayloadRaycast at NDC; answered by entity:pick:result
entity:apply-swatchEntityApplySwatchPayloadApply shader/behavior swatches to uuids (batch cartesian product)
cutscene:loadCutsceneLoadPayloadLoad preview cutscene; optional autoplay, time
cutscene:play{ from?: number }Play cutscene
cutscene:pause{}Pause cutscene
cutscene:stop{}Stop cutscene
cutscene:seek{ time: number }Scrub time (seconds)
cutscene:unload{}Unload cutscene; restore gameplay camera
camera:pose:get{ requestId: string }Request live camera pose

BridgeMessages is the intersection of both maps; BridgeMessageType is keyof that union.

Shared payload notes​

  • Poses — BridgePose carries optional position, Euler rotation (radians), authoritative quaternion, and scale.
  • Swatches — SwatchSpec: { kind: 'shader' \| 'behavior', source, props } where source is an export name registered in the swatch registry.
  • Scene operations — Game publishes operations; editor stores undo/redo and replays via scene:operation:apply because it cannot reconstruct entities from summaries alone.

GameBridge host​

GameBridge.connect(host) binds an object implementing GameBridgeHost (stage access, entity lookup, thumbnails, cutscene preview, catalog publish). Most games use the internal wiring in ZylemGame; custom embeds can instantiate GameBridge for partial integration.

Pitfalls​

  • Ready event — commands sent before handlers connect are dropped; always listen for zylem:bridge:ready.
  • Partial swatch success — batch apply returns per-target results; failures do not roll back successful targets.
  • Cutscene JSON — bridge carries CutsceneLoadPayload.definition as untyped JSON to avoid circular deps; validate with cutscene schema.

API reference​