Reference

Player reference

Every attribute, property, method and event of the player element, the play() and createPlayer options, and the React props.

Taken from js/player/src/element.js, play.js and player.js, the types in js/player/types and js/react on commit 79b96c7 of the SDK repository. If a value here disagrees with the package you installed, the package wins; this page is checked against the source of the release it ships with.

How to use the player is in Embed the player. The full generated API of every module, including internals this page leaves out, is docs/api/JS-API.md in the SDK repository (generated by tools/jsapi.mjs).

There are three layers, each a thin shell over the next:

LayerUse it when
<nearglass-player>you want the whole player in one tag, with its own bar
play()you want your own page around the video: admission, seats, relay choice and the LL-HLS fallback, without the bar
createPlayer()you already have a descriptor and want only the playback engine

The @nearglass/react component wraps the element (React props).

The nearglass-player element

Load it once with import '@nearglass/player/element.js'; or a <script type="module">. Importing it defines the tag. The element needs one of: token-url, a descriptor property, or api with stream and publishable-key. With none of them the bar reads "Set token-url, or api, stream and publishable-key." and nothing starts.

Attributes

Changing any attribute restarts the player (once per task, however many you set), except background-audio, which changes on the running player.

AttributeTypeDefaultWhat
apiURLnonethe control API's base address. With stream and publishable-key: public admission
streamstringnonethe stream id (st_...)
publishable-keystringnoneyour pk_... key
token-urlURLnoneserver admission: your backend's route, POSTed { seat, join } for each viewer token (Viewer tokens)
hls-urlURLthe descriptor's hlsthe LL-HLS playlist, when the descriptor names none
auto-joinboolean (presence)offtake an offered seat without pressing Join
modelatency, smooth, pacedlatencyhow frames are presented (Modes)
background-audioboolean (presence)offkeep the sound playing while the page is hidden

Properties

PropertyTypeDefaultWhat
controllerPlayController or nullnullthe running play() controller. Read only
hlsHls class or nullglobalThis.Hls, else nullhls.js, for browsers without native HLS. Not bundled
descriptorobject, function or nullnullserver admission without a route: the descriptor JSON, or a function returning it (or a promise of it), called again for each fresh token. Setting it restarts the player
backgroundAudiobooleanfalsethe background-audio attribute, as a property

Methods

MethodReturnsWhat
join()nothingtake the seat on offer. Does nothing outside the offer state
setMuted(muted)Promise<boolean>mute or unmute. Call it from a click: a user gesture is what lets the browser start sound. Resolves true when the change took

Events

Events fire on the element and carry their data in detail.

EventdetailWhen
statechange{ state, ...info }the state changed (States)
modechange{ mode, label, engine?, reason? }the path changed between moq, ll-hls and none
notetexta status line for the page to show
audiochange{ state, reason }the sound changed (Sound states)

Styling

The shadow DOM has these parts, for ::part():

PartWhat
videothe box holding the picture
canvasthe MoQ picture
fallbackthe LL-HLS <video>
barthe status bar under the picture
modethe mode label in the bar
statusthe status text in the bar
jointhe Join button
soundthe sound button
Custom propertyDefaultWhat
--nearglass-player-bar-bgrgba(127,127,127,.12)the bar's background
--nearglass-player-bar-fgcurrentColorthe bar's text
--nearglass-player-accent#2f6fdethe live dot, the Join button, button outlines
--nearglass-player-radius6pxcorner radius

The element is display: block (and hidden by the hidden attribute), and inherits the page's font. The picture is 16:9. Nothing is drawn over it.

What the bar says

StateBar text
admittingGetting a seat...
waiting, lineIn line, number N... (without the number when the API gives none)
waiting, fullThe stream is full. Waiting for a seat...
offerA seat is free. Join within N s. (the Join button shows)
connectingConnecting...
seat-lostYour seat was given up. Reload to watch again.
unsupportedthe reason, or "This browser cannot play this stream."
errorCannot play: and the message, or "Cannot play."
live, fallback, closedempty
joining, stalledunchanged: the text from note stays

The mode label reads "Low latency" for moq and a "Compatibility mode" label for ll-hls. The sound button reads "Sound on" while muted and "Sound off" while playing.

play()

import { play } from '@nearglass/player/play.js';

const p = play({ api: 'https://api.nearglass.com', stream: 'st_...', publishableKey: 'pk_...', canvas, video });
p.on('state', (state, info) => {});
p.join();
p.close();

play() returns a controller at once and starts on the next microtask, so listeners added straight after the call miss nothing.

Options

OptionTypeDefaultWhat
apistringnonethe control API's base address. Trailing slashes are dropped
streamstringnonethe stream id
publishableKeystringnonepublic admission: sent as the bearer key to POST /v1/play/{id}/admit
tokenUrlstringnoneserver admission through your route. Ignored when descriptor is set
descriptorobject or functionnoneserver admission with a descriptor in hand. A function is called for each fresh token
autoJoinbooleanfalsetake an offered seat without join(). A tab nobody watches should not hold a seat
canvasHTMLCanvasElementrequiredwhere MoQ frames are drawn
videoHTMLVideoElementnonewhere the LL-HLS fallback plays. Without it there is no fallback
hlsHls classnonehls.js, for browsers without native HLS
hlsUrlstringthe descriptor's hlsthe LL-HLS playlist
trackstringvideothe video track
backgroundAudiobooleanthe descriptor's background_audio, else falsekeep the sound on while the page is hidden. The fallback's sound follows the same rule
playerobject{}more createPlayer options (mode, audio, and so on)
fetchfunctionglobalThis.fetchfor tests
relayProbefunctionbuilt inprobe(url, certHash), for tests
createPlayerfunctionthe real onefor tests

Give exactly one way to be admitted: api with stream and publishableKey, or tokenUrl, or descriptor.

The controller

MemberTypeWhat
statePlayStatethe current state
modemoq, ll-hls, nonethe current path
seatstring or nullthe seat id once seated
enginePlayer or nullthe playback engine, once a descriptor is in hand
on(event, fn)controlleradd a listener. Returns the controller
join()nothingtake the seat on offer
setMuted(muted)Promise<boolean>as the element's
setBackgroundAudio(on)nothingchange the background-audio choice while playing
stats()object{ state, mode, seat, hls, engine, relay, region, rtt }: hls is native, hls.js or null; engine is the engine's stats() or null; relay and region name the relay in use; rtt is the measured round trip per relay
close()nothingstop, give the seat up at once, and go to closed

States

StateinfoMeaning
idlebefore anything has happened (only the state property reads this)
admittingasking for a seat
waiting{ position }, or { full, edge, waitMs }in line; or every seat or relay is full and the player asks again. full: true means a full stream or account; edge: true means every relay is full
offer{ seconds }a seat is free. join() takes it; the offer lapses after seconds and the player asks again
connectingopening a session on a relay
joiningsession open, waiting for the first picture
liveplaying over MoQ
stalledno frames; reconnecting
fallback{ reason, permanent }playing the LL-HLS stream (Fallback reasons)
seat-lostthe API no longer knows the seat (HTTP 410). The player stops
unsupported{ reason, message }this browser cannot play the stream, and there is no fallback either
error{ message }admission or the first descriptor failed. The player stops
closedclose() was called

Other events

EventPayloadWhen
mode{ mode, label, engine?, reason? }the path changed. engine is native or hls.js for ll-hls
relay{ url, region, rttMs, failover, full }the relay in use changed: first connect and each failover
notetextstatus text for the page
audio-availablebooleanthe catalog says whether the stream has sound. Show your sound control when true
audio{ state, reason }the sound changed (Sound states)

Timing constants

ExportValueWhat
CONNECT_DEADLINE_MS3000no relay session within this long: fall back to LL-HLS while MoQ keeps trying
KEYFRAME_DEADLINE_MS3000a session but no picture within this much longer: the same
ACCOUNT_FULL_RETRY_MS10000how often to ask again when every seat of the account is taken
MODE_LABELSobjectthe label for each path

The deadlines apply to the start only. A stall after MoQ has played is the engine's to recover; it is not a reason to fall back.

A seat is kept by a heartbeat to its play_url every heartbeat_ms of the descriptor (5000 when it names none). A hidden tab says so in the heartbeat, and a tab that comes back beats at once. When the page closes, a beacon gives the seat up.

Fallback reasons

reasonMeaningPermanent
no-webtransportthe browser has no WebTransportyes
no-webcodecsno WebCodecs video decoderyes
no-h264the decoder does not play H.264yes
webkit-flow-controlWebKit's WebTransport stops within minutes (Safari, and every browser on iPhone and iPad)yes
connect-timeoutno relay session in timeno
no-keyframea session but no picture in timeno

A permanent reason goes to LL-HLS at once and stays. For the others the player keeps trying MoQ and switches back at the first live frame. The engine itself also reports network after a stretch without frames; play() does not act on it.

Sound states

The audio event (and the element's audiochange) carries state:

stateMeaning
mutedthe sound is off. reason is hidden when a hidden page turned it off (show your sound control off), no-output when a start never played, otherwise null
startingunmuted, the output is starting
playingsound is playing
unavailablethe browser would not make a sound output

A hidden page without backgroundAudio turns the sound off, and it stays off until the viewer taps the sound control. That tap is also the gesture iOS needs to play.

createPlayer

createPlayer(opts) is the playback engine: one MoQ session at a time, with decode on arrival, the live-edge guard, token renewal, renditions and sound. It draws on a canvas and has no other UI. Use it when you do your own admission; play() calls it for you.

import { createPlayer } from '@nearglass/player/player.js';

createPlayer options

Options that play() fills in itself (relay, relayFull, certHash, tokens, namespace, track, canvas, backgroundAudio) are listed first. Everything else can be given to play() through its player option.

OptionTypeDefaultWhat
relaystring or functionrequiredthe relay's WebTransport URL; or a function asked before every connect with { failed, full }
relayFullfunctionnonerelayFull(retryMs): a relay refused the viewer as full; returns the ms until some relay may take it (0: another may now)
certHashUint8Array or nullnullSHA-256 of a test (self-signed) relay certificate
tokens() => Promise<Token[]>nonerole tokens, fetched on every (re)connect. Token is { value, receivedAt? }; receivedAt is performance.now() when it arrived
namespacestring arrayrequiredthe stream's namespace as segments, from the descriptor
trackstringvideothe video track
canvasHTMLCanvasElementrequiredthe windowed present target
videoHTMLVideoElementnonethe <video> used for fullscreen scanout
now() => msDate.nowthe publisher's clock
modelatency, smooth, pacedlatencypresentation mode
pumpClockraf, nowrafsmooth and paced: time frames against the display refresh's timestamp, or the wall clock
slewbooleanfalsefollow a clock re-sync gradually instead of jumping
presentLevelsbooleantruesmooth and paced: hold in whole display refreshes, with hysteresis
holdRuleadaptive, p90adaptivesmooth and paced: how the hold is chosen
presentworkermain threaddecode and draw in a worker
videoWindowedbooleanfalsepresent through the <video> windowed too (for comparison runs)
layoutfunctionthe exact scanout fit(streamW, streamH) => { left, top, width, height, fills, pure, note? } in CSS px, for fullscreen
scanTargetClassstringscan-targetclass put on the canvas when it is the fullscreen target
tracefunctionnone(kind, row): per-frame rows
renditionsauto or a track nameautoauto moves between the catalog's renditions by itself; a name pins one
rotateauto, off, or { bytes, groups, ms }automove to a fresh session before a limit is reached. auto rotates only on WebKit's WebTransport
renewauto, offautomove to a fresh session with fresh tokens before the token expires. off lets the relay end the session at expiry plus its grace, and the engine reconnects (tests)
webkitoff, onoffoff treats WebKit's WebTransport as unusable; on tries it (testing)
fallback{ hls, video }nonewhen MoQ cannot play here, play this LL-HLS playlist in that <video> if the browser plays HLS natively
audioboolean or track nametruethe catalog's audio track, a named track, or none. Always starts muted
audioWorkletUrlstringbuilt inthe audio worklet module's URL, for pages whose CSP blocks blob: worklets
volumenumber 0 to 11initial volume
audioLatencyHintstring or numberinteractivethe AudioContext's latencyHint
audioRingobject{}audio ring options, for tests
backgroundAudiobooleanfalsekeep sound on while the page is hidden. false: a hidden page turns it off until setMuted(false)
fecauto, offautoplay a track's FEC twin when the catalog names one; off: never
workerbooleanon, except on WebKitrun the receive path in a dedicated worker

Other options exist for tests and measurements (audioRenderWatchMs); they are not part of the supported surface.

Presentation modes

ModeWhat
latencydraw each frame as it arrives; a frame older than the next queued is skipped
smoothhold frames by recent lateness and draw the newest due one at each display refresh
paced"Every frame": the same hold, one frame per refresh in capture order

Player methods

MemberWhat
mode, setMode(m)read and change the presentation mode
setMuted(muted)Promise<boolean>. Call from a click
setVolume(v)0 to 1
backgroundAudio, setBackgroundAudio(on)read and change the background-audio choice
muted, hasAudiohasAudio is null until the catalog is read, then whether the stream has sound
setRendition(name)auto, or a rendition's track name to pin it
renditions()the catalog's renditions, highest bitrate first
stats()counters and current values (path, size, codec, mode, rendition, audio, FEC, adaptive-bitrate state)
sessionInfo()the current and ended sessions
jumpToLive()drop to the live edge now
close()end the session; the relay releases the viewer
donea promise that settles when the engine has stopped, or null

Player events

Listeners receive one argument.

EventPayloadWhen
stateconnecting, joining, live, stalled, waiting, fallbackthe engine's state
notetextstatus text
unsupportedtextthis browser cannot play MoQ
fallback{ reason, message, playing }MoQ cannot play here. playing is true when the engine played opts.fallback.hls itself
edge-full{ relay, waitMs, everywhere, round }a relay refused the viewer as full. everywhere is true when every relay is
frame{ captureMs, lagMs, path, count }a frame was shown
renditionslistthe catalog was read
rendition{ from, to, why, auto }the rendition changed. why is late, stale, stalled, join-rate, bunching, headroom, pinned, unavailable, underdelivered, not-live or abandoned
rotation{ n, bytes, groups, ms, gapMs, why }a session was handed over to a fresh one; why is token or limits
renew{ n, kind }a fresh session because requests were refused (expired or session), or the tab came back from hidden past the session's age (aged) or token time (token)
audio{ state, reason }as Sound states
audio-availablebooleanwhether the stream has sound
audio-statsobjectthe audio ring's numbers, four times a second
handoff, bytes, skip, jump, resubscribe, sessionengine internals, for measurements

Other exports

ExportWhat
supported()true when the browser has WebTransport and a video decoder
blocker(){ reason, message } when MoQ cannot play here, else null
onPageVisibility(onAway, onBack)call back when the page is hidden or put away, and when it is shown. Returns the remover
FALLBACK_REASONS, FALLBACK_MESSAGES, SUPPORTED_BROWSERSthe sentences shown to viewers
ELEMENT_TAGthe tag name

The descriptor

play() and createPlayer read the control API's descriptor JSON (the Descriptor schema). The fields the player uses:

FieldRequiredWhat
tokenyesthe viewer token
relaysyesrelay URLs, or { url } objects
namespaceyesthe stream's namespace, a string or segments
catalognothe catalog track's name
relay_cert_sha256nopins a self-signed relay certificate
hlsnothe LL-HLS playlist for the fallback
seat, statewhen seatedthe seat id, and in
heartbeat_ms, play_urlwhen seatedwhere and how often the seat is kept
background_audionothe stream's choice for background sound. The page's backgroundAudio wins

React props

@nearglass/react exports NearglassPlayer, useNearglassPlayer and ELEMENT_TAG. React 18 or later is a peer dependency. NearglassPlayer renders the element and forwards a ref to it.

PropTypeDefaultMaps to
apistringnonethe api attribute
streamstringnonestream
publishableKeystringnonepublishable-key
tokenUrlstringnonetoken-url
hlsUrlstringnonehls-url
modelatency, smooth, pacedlatencymode
autoJoinbooleanfalseauto-join
backgroundAudiobooleanfalsebackground-audio
descriptorobject or functionnonethe descriptor property
hlsHls classnonethe hls property
onStateChange(detail) => voidnonethe statechange event
onModeChange(detail) => voidnonethe modechange event
onNote(text) => voidnonethe note event
id, className, stylepassed through to the element

There is no prop for audiochange; add a listener to the element through the ref.

useNearglassPlayer(ref) follows an element and returns { state, info, mode, label, note }: state starts as idle and is the last statechange state, info is that event's detail, mode and label come from modechange (none and an empty string at first), and note is the last status text.

On this page