@clayzo/webgl-player
The lightweight Clayzo WebGL2 player. It embeds the engine core and built-in effects, so playback does not need an authentication request, a staged WASM file, or a Clayzo account.
For iOS and macOS apps, the same documents play natively through the Swift package at Clayzo/clayzo-swift.
npm install @clayzo/webgl-player
import { createPlayer, checkCoverage } from "@clayzo/webgl-player";
// `loaders` is optional: a bundle's, or your own font and image loaders.
const coverage = await checkCoverage(document, loaders);
if (!coverage.supported) throw new Error(coverage.reasons.join("; "));
const player = await createPlayer({ canvas, document, loaders, interactive: true });
player.play();
The stable public surface is createPlayer(), checkCoverage(), registerFont(), CreatePlayerOptions, and PlayerHandle. Engine handles, shader sources, renderer classes, the WASM ABI, and context-pool internals are intentionally private.
React and Next clients can use the optional client entrypoint:
import { ClayzoPlayer } from "@clayzo/webgl-player/react";
export function Hero() {
return (
<ClayzoPlayer
document={document}
poster="/hero.poster.webp"
posterTick={240}
label="A cube folding into the logo"
/>
);
}
The component shows poster at once and loads the player only as it comes near the viewport, then fades it in on posterTick and plays. clayzo package hero.json --poster 240 writes a matching hero.poster.webp. When the reader asks for reduced motion it holds posterTick; reducedMotion="pause" stops where it is instead, and "ignore" plays anyway. autoplay={false} keeps it still, decorative hides it from screen readers instead of label, and fallback is shown if the player cannot be created. document can be null while it loads.
Pages without React can use the <clayzo-player> element, with the same behaviour:
<script type="module">import "@clayzo/webgl-player/element";</script>
<clayzo-player src="/hero.clayzo" poster="/hero.poster.webp" label="A cube folding into the logo"></clayzo-player>
src is an engine JSON document or a .clayzo bundle. The attributes are poster, poster-tick (defaulting to the tick the bundle's poster was rendered at), autoplay, loop, interactive, reduced-motion, label and decorative; set autoplay="false" to turn one off. The element has play(), pause(), seek(), setInput() and fire(), and fires ready and error. Content inside it is shown if the player cannot be created.
States
A document with a state machine plays one state at a time, such as idle, hover, pressed, loading and done. Pointer events, inputs and setState move it between states, and the player calls back after the frame that shows each change.
const player = await createPlayer({
canvas,
document,
onStateChange: ({ from, to }) => console.log(from.name, "→", to.name),
});
player.play();
player.setInput("loading", true);
player.setState("done", { instant: true });
player.currentState; // { id, name }
initialState starts in another state, onComplete reports a state that plays once reaching its end, and onPointer reports pointer events on the canvas and on hit areas. setReducedMotion(true) shows each state at its last frame instead of animating it, which the components do when the reader asks for reduced motion. stateMachine: false plays the plain timeline.
Many players on one page
Players share GPU contexts, but each still needs memory for its pixels, and a phone closes a page that uses too much. The player keeps memory bounded by the players near the screen, not by how many are on the page:
- Off-screen players stop drawing (
pauseWhenOffscreen, default on). - Players more than a viewport away free their GPU memory and their canvas's pixels (
releaseWhenOffscreen, default on). The canvas keeps its layout box. Within half a viewport of the screen the player draws the same frame again before it can be seen. The clock doesn't run while it's away, sotickand playing state are exactly as it left them. resolution: "display"caps the device pixel ratio atmaxPixelRatio(default2), which on a 3x phone holds less than half the pixels.- A lost GPU context keeps the last frame on the canvas while the player moves to a new context. Failures go to
onError(oronPlayerErrorin React); nothing is thrown.
// A player that must reappear instantly, and a crisper one on 3x screens.
await createPlayer({ canvas, document, releaseWhenOffscreen: false, maxPixelRatio: 3 });
Provide font or image loaders when document asset URLs are not directly fetchable. Use a .clayzo bundle when you want assets packaged with the document. Choose @clayzo/canvaskit-player when checkCoverage() reports an unsupported construct or full reference-renderer fidelity matters more than transfer size.