React
@alpha2-sh/react puts the browser player on a canvas in a React tree. After reading this page you can show a project at any time from React state, recover from a lost GPU without remaking the player, and do the same wiring without React.
The package draws nothing itself. You make an Alpha2Player (from alpha2/player); the component gives it a canvas and presents the time you pass.
Install
Section titled “Install”npm install @alpha2-sh/react alpha2pnpm add @alpha2-sh/react alpha2yarn add @alpha2-sh/react alpha2Show a project with Alpha2Canvas
Section titled “Show a project with Alpha2Canvas”- Write a render worker that runs the engine, as in Preview in a worker.
- Write a
createPlayercallback that makes anAlpha2Playerover aWorkerRenderBackend, and pass it thereplaceSurfaceargument the component gives you. - Render
<Alpha2Canvas>with the project’s size and the time to show, in ticks.
import { useCallback, useState } from "react";import { Alpha2Canvas } from "@alpha2-sh/react";import { Alpha2Player } from "alpha2/player";import { WorkerRenderBackend } from "alpha2/worker";
function Preview({ workerUrl, refill }) { const [time, setTime] = useState(0n);
// A new function makes a new player: keep it stable. const createPlayer = useCallback( (replaceSurface) => new Alpha2Player(new WorkerRenderBackend(new Worker(workerUrl, { type: "module" })), { onRefill: refill, replaceSurface, }), [workerUrl, refill], );
return ( <> <Alpha2Canvas createPlayer={createPlayer} timeTicks={time} width={1280} height={720} onError={(error) => console.error(error)} /> <input type="range" min={0} max={120_000 * 10} onChange={(event) => setTime(BigInt(event.target.value))} /> </> );}refill receives the DecoderRefillRequests for the frames a presentation lacks. It decodes them and uploads each with player.uploadVideoFrame; see Decode with WebCodecs.
| Prop | Type | |
|---|---|---|
createPlayer |
(replaceSurface) => player |
Makes the player for a canvas. Called each time the component makes a canvas. Keep it stable with useCallback. |
timeTicks |
bigint |
The timeline time to show, in ticks. |
width, height |
number |
The project’s size: what the player draws at, and the element’s aspect ratio. |
className, style |
Styles the element. | |
onReady |
() => void |
Called once the player has its canvas. |
onPresented |
(frame) => void |
Called with each presentation that was not superseded. |
onError |
(error) => void |
Called when initialization or a presentation fails. Without it, the error goes to the page’s reportError. |
How it behaves
Section titled “How it behaves”- The element keeps the project’s aspect ratio and fills its container’s width.
- It presents
timeTickswhenever it changes. Times given while a presentation runs coalesce into one presentation of the latest. - While the page is hidden it presents nothing. When the page is visible again, it shows the latest time.
- A player belongs to one canvas. Each mount, each new
createPlayerand each new size makes a new canvas and a new player, and the old player is disposed when its canvas goes. React may set the component up more than once (<StrictMode>,<Activity>); each time gets its own canvas and player.
Recover from a lost GPU
Section titled “Recover from a lost GPU”When the GPU is lost, Alpha2Player recovers on the next presentation. Some recoveries need a fresh canvas: a lost WebGL2 context, or the CPU renderer after the browser withdrew GPU access. The replaceSurface function the component passes to createPlayer handles this. It swaps a new canvas element in for the old one, and the same player draws on it.
Without React: bindCanvas
Section titled “Without React: bindCanvas”bindCanvas(player, canvas, options?) is the same wiring for any framework. It transfers the canvas to the player and presents the times you show. It neither makes nor disposes the player.
import { bindCanvas } from "@alpha2-sh/react";import { Alpha2Player } from "alpha2/player";import { WorkerRenderBackend } from "alpha2/worker";
const canvas = document.querySelector("canvas");canvas.width = 1280; // the drawing-buffer size cannot change after transfercanvas.height = 720;
let binding;let current = canvas;const player = new Alpha2Player(new WorkerRenderBackend(worker), { onRefill: refill, replaceSurface: () => binding.replaceSurface(),});binding = bindCanvas(player, canvas, { // Put a same-size canvas where the bound one is. replaceCanvas: async () => { const fresh = document.createElement("canvas"); fresh.width = current.width; fresh.height = current.height; current.replaceWith(fresh); current = fresh; return fresh; }, onPresented: (frame) => console.log(frame.presentedTimeTicks), onError: (error) => console.error(error),});
await binding.ready;binding.show(60_000n);
// Later:binding.dispose();player.dispose();binding.readyresolves once the player has its canvas, and rejects if it cannot have it.bindCanvasitself never throws.binding.show(ticks)shows a time now, once initialized, or when the page is next visible. Showing the same time again presents it again.binding.replaceSurface()returns a new canvas fromreplaceCanvas, already transferred to anOffscreenCanvas, which is what the player’sreplaceSurfaceoption needs. WithoutreplaceCanvasit rejects.options.visibilityreplacesdocumentas the source of the page’s visibility.
- Browser: the player, the render worker, frame supply and export.
- Editor:
EditorPreviewbinds an editor session’s preview to a canvas. - Project model: what the player renders.
