// ambient.js — YouTube-style ambient backlight (upgrade in v1 UI). // // A tiny 32x18 offscreen sample of the video, drawn to a canvas BEHIND the // bezel and blurred huge by CSS, so the screen's edge colors bleed outward as a // phosphor halo. Cheap: ~20fps, sampled every 3rd rAF. Works only because the // relay makes video same-origin (canvas never tainted). Respects reduced-motion. const W = 32, H = 18; export class Ambient { /** @param {HTMLVideoElement} video @param {HTMLCanvasElement} canvas */ constructor(video, canvas) { this.video = video; this.canvas = canvas; this.canvas.width = W; this.canvas.height = H; this.ctx = canvas.getContext('2d', { willReadFrequently: false }); this.raf = 0; this.frame = 0; this.active = false; this._reduced = matchMedia('(prefers-reduced-motion: reduce)').matches; } start() { if (this.active || this._reduced) return; this.active = true; const loop = () => { if (!this.active) return; this.raf = requestAnimationFrame(loop); if ((this.frame++ % 3) !== 0) return; // ~20fps if (document.hidden) return; const v = this.video; if (v.readyState < 2 || v.videoWidth === 0) return; try { this.ctx.drawImage(v, 0, 0, W, H); } catch { /* tainted (cross-origin) — bail quietly */ this.stop(); } }; this.raf = requestAnimationFrame(loop); } stop() { this.active = false; if (this.raf) cancelAnimationFrame(this.raf); this.raf = 0; } toggle() { this.active ? this.stop() : this.start(); return this.active; } }