The ligr.gfx.v1 protocol
Your graphic and the LIGR overlay page talk over postMessage. The overlay is the parent window.
Your graphic runs in an iframe. Every message in both directions uses one envelope and the
protocol name ligr.gfx.v1.
The envelope
Section titled “The envelope”{ "protocol": "ligr.gfx.v1", "sessionId": "cg_…", "seq": 12, "type": "GRAPHIC_UPDATE", "payload": {} }| Field | Value |
|---|---|
protocol | Always ligr.gfx.v1 |
sessionId | The session the overlay opened for this load of your frame. Copy it from HELLO |
seq | A counter. Each side keeps its own and increases it by 1 per message |
type | The message type. See the two tables below |
payload | The body of the message. Its shape depends on type |
Two rules apply to every message you send.
- Echo the
sessionId. Copy the exact string from theHELLOmessage into every message you send. The overlay drops a message with a differentsessionId. - Increase
seq. Start your counter at 1 and add 1 on every message. The overlay does not refuse a gap, but it logs a warning for one.
The overlay refuses a message over 1 MB in either direction. It reports the oversize message as a
GRAPHIC_ERROR in its own log. Keep externalData and your own payloads small.
The handshake
Section titled “The handshake”The overlay sends HELLO when your frame fires its load event. It repeats HELLO every 250 ms
until you answer READY, up to 20 times. A graphic that answers no HELLO within 5 seconds is
logged as an error and never loads. Answer the first HELLO you receive. You can also answer every
HELLO. The overlay ignores every READY after the first one of a session.
| Step | Direction | Type | Payload |
|---|---|---|---|
| 1 | Overlay → graphic | HELLO | { width, height }, the frame size |
| 2 | Graphic → overlay | READY | { version }, the version of your graphic |
| 3 | Overlay → graphic | LOAD_GRAPHIC | The full data payload |
From then on, every change arrives as a GRAPHIC_UPDATE.
Overlay to graphic
Section titled “Overlay to graphic”| Type | When | Payload |
|---|---|---|
HELLO | On frame load, repeated until READY | { width, height } |
LOAD_GRAPHIC | After READY | Every surface. See Data binding |
GRAPHIC_UPDATE | When any surface changes | graphicId, show, sport, plus only the surfaces that changed |
GRAPHIC_HIDE | An operator, an automation or a REST command hides the graphic | { graphicId } |
PING | Every 10 seconds | {} |
A GRAPHIC_UPDATE carries only the surfaces whose JSON changed since the previous message. Merge
it over the state you hold. A surface that is absent did not change.
If the overlay fails to deliver a LOAD_GRAPHIC, it sends the full load again on the next data
change. If it fails to deliver a GRAPHIC_UPDATE, it drops its diff baseline and sends a full
LOAD_GRAPHIC on the next data change. Your graphic must render correctly from a LOAD_GRAPHIC at
any time, not only the first one.
Graphic to overlay
Section titled “Graphic to overlay”| Type | When | Payload |
|---|---|---|
READY | You answer HELLO | { version } |
HIDDEN | Your out-animation ends | { graphicId } |
PONG | You answer PING | {} |
ACK | Optional, after any command | { ackSeq }, the seq of the message you acknowledge |
GRAPHIC_ERROR | Your graphic hits a runtime error | { message, stack?, graphicId? } |
METRICS | Optional | { graphicId, renderTimeMs, frameCount? } |
Answer every PING with a PONG. The overlay logs a warning when no PONG arrives within 5
seconds.
Show and hide
Section titled “Show and hide”- The overlay sends
GRAPHIC_HIDE. - Your graphic plays its out-animation and sends
HIDDEN. - The overlay hides the frame when
HIDDENarrives, or afterexitDurationMsfrom the manifest. The default is 1500 ms.
Two rules govern show and hide.
GRAPHIC_HIDEis the only hide trigger. Hide your graphic only when you receive it. Do not hide on any other message.showinsideGRAPHIC_UPDATEis informational. There is no separate show message. Treatshow: truein aGRAPHIC_UPDATEthat arrives after a hide as the signal to show your graphic again.
Always send HIDDEN when your out-animation ends. If you never send it, the overlay hides the
frame at the deadline and your animation looks cut off. If your animation is longer than the
default deadline, raise exitDurationMs in the manifest.
Origin
Section titled “Origin”Post messages to window.parent. Capture event.origin from the first HELLO you receive and
use it as the target origin for every later message. Before you know the origin, '*' is
accepted.
Your graphic runs in a sandboxed frame with an opaque origin. It cannot read the overlay page. The overlay accepts messages only from your frame. See Bundle rules.
A complete listener
Section titled “A complete listener”createGraphic from the @ligrsystems/graphics-sdk package implements this listener, with the origin check,
the merge of every GRAPHIC_UPDATE, and HIDDEN after your out-animation. Use the listener below when you
cannot add a dependency.
const PROTOCOL = 'ligr.gfx.v1'let sessionId = nulllet parentOrigin = '*'let seq = 0
export function send(type, payload) { window.parent.postMessage({ protocol: PROTOCOL, sessionId, seq: ++seq, type, payload }, parentOrigin)}
export function listen(handlers) { window.addEventListener('message', (event) => { const msg = event.data if (!msg || msg.protocol !== PROTOCOL) return
if (msg.type === 'HELLO') { sessionId = msg.sessionId parentOrigin = event.origin || '*' send('READY', { version: '1.0.0' }) return } if (msg.sessionId !== sessionId) return
if (msg.type === 'PING') { send('PONG', {}) return } handlers[msg.type]?.(msg.payload) })}handlers maps LOAD_GRAPHIC, GRAPHIC_UPDATE and GRAPHIC_HIDE to your own functions. The
Quick start has a full graphic that uses this shape.