Embedding the engine
A host imports the engine’s TypeScript, serves its prebuilt workers and WebAssembly modules, and configures it once at startup.
Decoders are not among them: a host hands the engine the decoder packages it loads, at runtime.
Read the engine’s folders from tools/constants.json (sourceDirectory, scriptsDirectory, libraryOutputDirectory, buildInfoFile) rather than restating them.
You need
- The engine as a git submodule of the host.
- The toolchain of the engine’s modules from WebAssembly build, for the first build of each checkout: Emscripten and rustup.
Steps
-
Make the sources resolvable. Map
webgpu-player/*to the engine’ssrc/*in the host’s TypeScriptpathsand in its bundler alias. The sources import nothing through theimportsfield of the engine’spackage.json; only the decoder projects’ tests do. -
Install the engine’s dependencies. Add the engine to the host’s npm
workspaces, so npm installs them,esbuildfor the asset build included. Without a workspace, install the engine’sdependenciesandesbuildin the host. -
Build the engine’s modules once per checkout, and again after their sources change:
make -C <engine>/wasm all. The asset build reads them from<engine>/bin/wasm/and stops when that folder is missing. -
Build the served assets before bundling:
node <engine>/scripts/build.mjs, with--productionfor a production build. It bundles the four engine workers with esbuild as classic workers. It copies the engine’s modules, their licenses, and the native range extension probes’ streams into<engine>/bin/libraries/. It also writes<engine>/bin/build-info.json, whoseassetKeyhashes every served file. -
Serve
<engine>/bin/libraries/unmodified, and do not minify it again. The default asset base islibraries/beside the page. Workers live in itswebgpu-player/folder and resolve the other assets relative to their own URL, so keep the layout intact. -
Configure the engine at startup:
import { configureEngineAssets } from 'webgpu-player/EngineAssets'; import { configureEngineFeatureFlags } from 'webgpu-player/EngineConfiguration'; import 'webgpu-player/style.css'; // assetKey comes from <engine>/bin/build-info.json, written by the asset build configureEngineAssets({ cacheKey: assetKey }); configureEngineFeatureFlags({ isHDRToneMappingEnabled: () => Promise.resolve(true) });Pass
baseURLto serve the assets from somewhere other thanlibraries/.
How assets are named and served
src/EngineAssets.tsnames every runtime asset,scripts/library-assets.mjsmaps each served file to its source, and theEngineAssetstest keeps the two in agreement.scripts/build.mjsfails when an asset it maps is missing, which also stops a host build that runs it.- Asset URLs are stable.
The cache key is appended as
?v=, so a browser never mixes files from two builds. - Engine workers are served at
libraries/webgpu-player/<Name>.worker.js, not as host bundler chunks. - The range extension probes’ streams are served with a
.binextension, because some static file servers reject unknown extensions. They come frombin/codec_vector_assets/. - The served folder holds each module’s license beside it. The engine serves no LGPL code; whoever distributes the LGPL decoder packages offers their source (see WebAssembly build).
Gotchas
- Reload the page fully after deploying a new build.
Open pages keep their old bundles; engine workers and modules change their
?v=key with every build. - A bundler dev server that runs the asset build at configuration load must be restarted after an engine worker changes.
- WebGPU needs a secure context. Over plain HTTP on a LAN address, a host that still selects the WebGPU player plays HTML pass-through only, which proves nothing.
- The presenter places its canvas over the surface’s video element and fits frames by that element’s CSS
object-fit, so the element needs a layout box. Hide it withvisibility: hiddenor cover it, but never withdisplay: noneor thehiddenattribute, which leave every frame undrawn assurface-empty. - While the black bar crop is on (
WebGPUPresenter.setBlackBarCropEnabled), the presenter fits the picture the bars leave rather than the whole frame, so the picture’s rectangle on the surface differs from the frame’s. Layers a host draws over the video, such as subtitles, place themselves over the element’s box and know nothing of the crop; the presenter’s telemetry gives it asblackBarCrop.