Appearance
Page API
approvedHow a hosting page creates, controls, and observes player instances.
Mounting and unmounting
- Creating a player takes a host element and a configuration and returns an instance handle; destroying it releases everything.
- Updating an instance swaps the video in place: the media element is reused, which preserves playback permissions such as the audio activation grant. This operation is the primitive the shorts feed is built on.
- Mount and update validate before applying anything. A hard-invalid configuration (the config contract's validation section defines exactly three conditions, with the legacy pre-3
bodyshape called out) makes the call fail with a console error naming the violation, and the instance renders the config error state; a rejected update applies no partial changes and does not continue the previous video. Soft irregularities never fail a call: they degrade to defaults with one consolidated console warning. - A hard-invalid initial call still mounts the configuration-error state before it reports and throws, and it returns no handle. When the supplied layout is valid, the error state uses that layout; landscape is the fallback only when the layout value is absent or invalid. The failed tree stays associated with its host internally so a later valid
createPlayercall can replace and dispose it. - An update rejects a configuration whose layout differs from the instance's: layout is immutable per instance, in both directions, and the error says so plainly.
- One live player owns one host. A second
createPlayercall on an already-owned host throws an error naming the ownership conflict and leaves the existing instance untouched. Destroying the owner releases the host for a later player.
Instance handles
Creating a player is one call: createPlayer(host, config) takes a host element and a complete configuration and returns the instance handle. The handle is the entire surface a page ever holds; there is no other object and no engine access. It is a stable plain object whose method identities do not change.
ts
handle.update(config); // apply a complete configuration; throws on hard-invalid
handle.destroy(); // release everything
handle.getState(); // one snapshot object, see below
handle.play();
handle.pause();
handle.seek(seconds);
handle.setVolume(volume); // 0 to 1
handle.setMuted(muted);
handle.setRate(rate); // within the offered rate list
handle.setCaptions({ enabled, language });
handle.openShare(); // the external share trigger
handle.openPanel(view); // open a specific context panel view
handle.closePanel();
handle.on(event, handler); // returns an unsubscribe functiondestroy() is idempotent. Once it has released the instance, mutating commands, update(), and new subscription attempts through that stale handle are silent no-ops; on() returns an inert unsubscribe function. getState() remains readable and returns the final detached snapshot. The unsubscribe returned by a live subscription is also idempotent.
State
getState() returns one snapshot, deliberately instead of many getters:
json
{
"playback": "playing",
"currentTime": 12.4,
"duration": 609.9,
"volume": 0.8,
"muted": false,
"rate": 1.5,
"captions": { "enabled": true, "language": "de" },
"pip": false,
"layout": "portrait"
}playback is one of idle, buffering, playing, paused, ended, error (the core player's six states; their derivation and transitions live in the core player document). The snapshot is a plain serializable object, computed at call time; it never updates itself. Pages that want continuous values subscribe to events.
Setter semantics
- Setters express user-equivalent actions: they update the user state exactly as the player's own controls would (
setRateremembers intent,setCaptionssets the preference), and the same bounds apply: a rate outside the offered list snaps to the closest offered value. - Setters on removed functions are silent no-ops: calling
setCaptionson an instance withcapabilities.captions: falsedoes nothing, matching the removal-beats-preference rule. openPanel(view)with a view whose metadata section is absent is a silent no-op (the view is dormant);openShare()without share data likewise.openShare()opens the share dialog in its page placement, centered in the viewport; the player's own trigger uses the in-player placement (the share capability's Placement section defines both).openPanel(view)on an instance whose layout owns no panel surface is a silent no-op, even when that view's metadata section is present.- The public context-panel view values are
knowledge,underlyingPublication, andrelatedVideos.
Events
handle.on(event, handler) subscribes; the returned function unsubscribes. Handlers receive one payload object, and every payload carries currentTime, read when the event is dispatched. Commands do not emit optimistically: an event follows the observed engine or LT-module state change, regardless of whether the trigger was the page API, player UI, lock screen, or browser. The list is deliberately small and grows in ordinary releases when a real need appears:
| Event | Additional payload fields | Fires |
|---|---|---|
ready | duration | Metadata and a first frame are available and the instance can play |
play | none | Playback starts or resumes |
pause | none | Playback pauses |
ended | none | Playback reaches the end (standalone; the feed loops instead) |
seeked | none | A seek completes |
buffering | buffering: boolean | Published buffering starts or recovers |
error | kind: "configuration" | "startup" | "mid-playback" | The corresponding error state is entered |
timeUpdate | none | Playback position updates at the media element's native cadence |
chapterChange | chapter: PlayerChapter | null | The current chapter changes, including the unlabeled intro |
volumeChange | volume, muted | Volume or mute changes |
rateChange | rate | Playback rate changes |
captionsChange | captions: { enabled: boolean; language?: string } | Captions toggle or the selected track changes |
pipChange | pip: boolean | Picture-in-picture is entered or left |
shareOpen | source: "player" | "page" | The share dialog opens; source matches the trigger |
panelOpen | view: "knowledge" | "underlyingPublication" | "relatedVideos" | The context panel opens or switches to that view |
panelClose | none | The context panel closes |
videoChange | none | A successful update is live |
Every error also reaching the console is a rule from the core player document; events are for pages, the console is for developers.
Multiple players on one page
- Multiple independent instances are supported. Cross-instance coordination exists only where a module defines it; the shorts feed's single-instance model keeps the common case trivial.