A transparent clip cannot be trusted to a video codec's alpha plane: Chrome
and Firefox render a VP9 yuva420p WebM transparent, but Safari and every
WKWebView (the exported kiosk app) decode it and DROP the alpha, so the clip
lands on a black square. Instead the clip is authored as one OPAQUE frame
twice as tall — the colour on top, the alpha channel as a grey matte of the
same size below (Scene.add_video(..., alpha_matte="stacked")) — and this
module recombines the halves on a <canvas> with a two-tap fragment shader:
rgb from the top half, a from the bottom half's red channel, output
premultiplied so the canvas composites correctly over the scene.
One compositor per stacked video overlay. Its WebGL resources are allocated
lazily on the first start(), so clips never shown do not consume contexts.
It draws only while start()ed (the overlay manager ties that to the
overlay's visibility, like playback) and only when the video has a frame;
frames are pulled with
requestVideoFrameCallback where available (one draw per decoded frame),
requestAnimationFrame otherwise. Without WebGL (jsdom, a blocked context)
the first start reports failure and the manager shows the plain video —
colour over matte, but visible rather than broken.
Stacked-alpha-matte video compositor.
A transparent clip cannot be trusted to a video codec's alpha plane: Chrome and Firefox render a VP9
yuva420pWebM transparent, but Safari and every WKWebView (the exported kiosk app) decode it and DROP the alpha, so the clip lands on a black square. Instead the clip is authored as one OPAQUE frame twice as tall — the colour on top, the alpha channel as a grey matte of the same size below (Scene.add_video(..., alpha_matte="stacked")) — and this module recombines the halves on a<canvas>with a two-tap fragment shader:rgbfrom the top half,afrom the bottom half's red channel, output premultiplied so the canvas composites correctly over the scene.One compositor per stacked video overlay. Its WebGL resources are allocated lazily on the first
start(), so clips never shown do not consume contexts. It draws only whilestart()ed (the overlay manager ties that to the overlay's visibility, like playback) and only when the video has a frame; frames are pulled withrequestVideoFrameCallbackwhere available (one draw per decoded frame),requestAnimationFrameotherwise. Without WebGL (jsdom, a blocked context) the first start reports failure and the manager shows the plain video — colour over matte, but visible rather than broken.