@tensordoc/prism 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"prism.mjs","sources":["../src/backends/milkdrop.ts","../src/backends/shadertoy.ts","../src/image-feed.ts","../src/types.ts","../src/registry.ts","../src/runtime.ts","../src/synth.ts","../src/player.ts","../src/image-overlay.ts"],"sourcesContent":["// milkdrop.ts — boot a butterchurn (Milkdrop) visualizer onto a caller-\n// supplied canvas. Picks a random preset on each load. Starts on a silent\n// source so the visual runs without audio; the caller can call connectAudio()\n// later when real audio is shared.\n\nimport butterchurnRaw from \"butterchurn\";\nimport butterchurnPresetsRaw from \"butterchurn-presets\";\nimport * as milkdropConverterRaw from \"milkdrop-preset-converter\";\nconst milkdropConverter = milkdropConverterRaw as unknown as {\n convertPreset: (text: string) => Promise<unknown>;\n};\n\ninterface VisualizerHandle {\n connectAudio(node: AudioNode): void;\n loadPreset(preset: unknown, blendTime?: number): void;\n setRendererSize(w: number, h: number): void;\n render(): void;\n}\ninterface ButterchurnAPI {\n createVisualizer(\n ctx: AudioContext,\n canvas: HTMLCanvasElement,\n opts: { width: number; height: number; pixelRatio?: number; textureRatio?: number },\n ): VisualizerHandle;\n}\ninterface PresetsAPI {\n getPresets(): Record<string, unknown>;\n}\n\nfunction unwrap<T>(mod: unknown): T {\n const m = mod as { default?: T };\n return (m && typeof m === \"object\" && \"default\" in m ? (m.default as T) : (mod as T));\n}\nconst butterchurn = unwrap<ButterchurnAPI>(butterchurnRaw);\nconst butterchurnPresets = unwrap<PresetsAPI>(butterchurnPresetsRaw);\n\nexport interface MilkdropBg {\n /** Pretty name of the currently-loaded preset (live; updates on rotation). */\n readonly presetName: string;\n /** Raw preset key (matches catalog `preset_id`). Stable for share / API use. */\n readonly currentPresetId: string;\n /** Swap the audio source — call when real tab-audio comes in. */\n connectAudio: (node: AudioNode) => void;\n /** Load a new random preset (with a blend transition). Returns the\n * pretty name of the newly-loaded preset. */\n loadRandom: (blendSeconds?: number) => string;\n /** Load a specific preset by its raw key (matches catalog `preset_id`).\n * Returns the pretty name on success; `null` if no preset with that key\n * exists in the bundled library. */\n loadByName: (name: string, blendSeconds?: number) => string | null;\n /** Load a .milk preset from a URL: fetch text → convertPreset → loadPreset.\n * Used for the 526 favorites and future contributor uploads which live\n * at public/presets/milkdrop/<slug>.milk. Returns the pretty name on\n * success; throws on fetch / parse error so callers can surface it. */\n loadFromUrl: (url: string, blendSeconds?: number) => Promise<string>;\n destroy: () => void;\n}\n\nexport interface MilkdropBgOptions {\n /** Preset key to load on cold open. Falls back to a random pick if the\n * name doesn't exist in the bundle. Lets the landing pin a curated\n * \"atelier\" default instead of getting random_$$$ Royal Mashup. */\n initialPresetName?: string;\n}\n\nexport function createMilkdropBackground(\n audioCtx: AudioContext,\n canvas: HTMLCanvasElement,\n silentSource: AudioNode,\n onReady?: () => void,\n options: MilkdropBgOptions = {},\n): MilkdropBg {\n // Size canvas to viewport (raw pixels — butterchurn does its own DPR math via pixelRatio).\n const sizeTo = (w: number, h: number) => {\n canvas.width = w;\n canvas.height = h;\n };\n sizeTo(window.innerWidth, window.innerHeight);\n\n const visualizer = butterchurn.createVisualizer(audioCtx, canvas, {\n width: canvas.width,\n height: canvas.height,\n pixelRatio: 1,\n textureRatio: 1,\n });\n visualizer.connectAudio(silentSource);\n\n // Pick the curated default if it exists in the bundle; otherwise random.\n const presetMap = butterchurnPresets.getPresets();\n const names = Object.keys(presetMap);\n let currentRaw: string;\n const requested = options.initialPresetName;\n const requestedKey = requested ? findPresetKey(names, requested) : null;\n if (requestedKey) {\n currentRaw = requestedKey;\n } else {\n currentRaw = names[Math.floor(Math.random() * names.length)];\n }\n visualizer.loadPreset(presetMap[currentRaw], 0);\n\n const onResize = (): void => {\n const w = window.innerWidth;\n const h = window.innerHeight;\n sizeTo(w, h);\n visualizer.setRendererSize(w, h);\n };\n window.addEventListener(\"resize\", onResize, { passive: true });\n\n let running = true;\n let firstFrame = true;\n const loop = (): void => {\n if (!running) return;\n visualizer.render();\n if (firstFrame) {\n firstFrame = false;\n // Defer slightly so the canvas actually has paint before consumers\n // remove their \"loading\" UI — gives a smoother transition.\n window.setTimeout(() => onReady?.(), 0);\n }\n requestAnimationFrame(loop);\n };\n requestAnimationFrame(loop);\n\n const pickNew = (): string => {\n // avoid immediately re-picking the same preset\n if (names.length < 2) return currentRaw;\n let next = currentRaw;\n while (next === currentRaw) next = names[Math.floor(Math.random() * names.length)];\n return next;\n };\n\n return {\n get presetName(): string { return prettyPresetName(currentRaw); },\n get currentPresetId(): string { return currentRaw; },\n connectAudio: (node) => visualizer.connectAudio(node),\n loadRandom: (blendSeconds = 2.5) => {\n const next = pickNew();\n visualizer.loadPreset(presetMap[next], blendSeconds);\n currentRaw = next;\n return prettyPresetName(next);\n },\n loadByName: (name, blendSeconds = 2.5) => {\n const key = findPresetKey(names, name);\n if (!key) return null;\n visualizer.loadPreset(presetMap[key], blendSeconds);\n currentRaw = key;\n return prettyPresetName(key);\n },\n loadFromUrl: async (url, blendSeconds = 2.5) => {\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`fetch ${url} → ${res.status} ${res.statusText}`);\n }\n const milkText = await res.text();\n const converted = await milkdropConverter.convertPreset(milkText);\n visualizer.loadPreset(converted, blendSeconds);\n // Display name is derived from the URL's last path segment.\n const stem = url.split(\"/\").pop()?.replace(/\\.milk$/, \"\") ?? url;\n currentRaw = stem;\n return prettyPresetName(stem);\n },\n destroy: () => {\n running = false;\n window.removeEventListener(\"resize\", onResize);\n },\n };\n}\n\n/** Match a preset key tolerantly: exact, case-insensitive, then\n * punctuation-stripped substring. Returns the raw key from the library\n * so loadPreset gets the exact object it expects. */\nfunction findPresetKey(names: string[], requested: string): string | null {\n if (names.includes(requested)) return requested;\n const lower = requested.toLowerCase();\n const ci = names.find((n) => n.toLowerCase() === lower);\n if (ci) return ci;\n const norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n const target = norm(requested);\n return names.find((n) => norm(n) === target) ?? null;\n}\n\n/** Strip leading punctuation, kebab-ish format, lowercase. */\nfunction prettyPresetName(raw: string): string {\n return raw\n .replace(/^[\\W\\d]+/, \"\")\n .replace(/[_]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .toLowerCase();\n}\n","// shadertoy.ts — render a Shadertoy-style GLSL fragment shader to a\n// caller-supplied canvas. Mirrors backends/milkdrop.ts's role: takes audio\n// in, produces a live visual. GraphRuntime swaps between the two based on\n// whether the graph has a lf.milkdrop or lf.shadertoy node.\n//\n// Shadertoy convention: the user shader defines\n// void mainImage(out vec4 fragColor, in vec2 fragCoord)\n// We wrap that with our standard uniforms:\n// iTime, iResolution, iMouse, iChannel0 (audio FFT 256x1)\n\nconst VERTEX_SHADER = `#version 300 es\nprecision highp float;\nout vec2 v_uv;\nvoid main() {\n // Fullscreen triangle (no buffer needed; gl_VertexID drives positions).\n vec2 p = vec2((gl_VertexID & 1) * 4 - 1, (gl_VertexID & 2) * 2 - 1);\n v_uv = p * 0.5 + 0.5;\n gl_Position = vec4(p, 0.0, 1.0);\n}\n`;\n\nconst FRAGMENT_PREAMBLE = `#version 300 es\nprecision highp float;\nprecision highp int;\n\nuniform float iTime;\nuniform float iTimeDelta;\nuniform int iFrame;\nuniform vec3 iResolution;\nuniform vec4 iMouse;\nuniform sampler2D iChannel0; // audio FFT (256x1, R8)\nuniform sampler2D iChannel1; // image input (default_image or bound source)\nin vec2 v_uv;\nout vec4 outColor;\n\n`;\n\nconst FRAGMENT_EPILOGUE = `\n\nvoid main() {\n vec4 c;\n mainImage(c, gl_FragCoord.xy);\n outColor = vec4(c.rgb, 1.0);\n}\n`;\n\nexport interface ShadertoyBg {\n /** Pretty name from the source URL, useful for SKILL readout. */\n readonly presetName: string;\n /** URL of the currently-loaded shader. */\n readonly currentUrl: string | null;\n /** Connect a Web Audio source — Shadertoy's iChannel0 sees its FFT. */\n connectAudio: (node: AudioNode) => void;\n /** Fetch + compile + render a new shader. Resolves once first frame\n * paints. Throws on compile error. */\n loadFromUrl: (url: string) => Promise<string>;\n /** Bind an image URL as iChannel1. Resolves once decoded + uploaded.\n * Pass null to clear (resets to the 1x1 placeholder). */\n bindImage: (url: string | null) => Promise<void>;\n /** Pipe a live source (e.g. a slideshow's canvas) into iChannel1 — its\n * contents are uploaded to the GPU every frame, so when the slideshow\n * advances to the next image, the shader sees it immediately. Pass\n * null to disable + revert to whatever was last bound via bindImage. */\n setLiveSource: (source: HTMLCanvasElement | HTMLVideoElement | null) => void;\n /** Pause the render loop + free GL resources. */\n destroy: () => void;\n}\n\nexport function createShadertoyBackground(\n audioCtx: AudioContext,\n canvas: HTMLCanvasElement,\n silentSource: AudioNode,\n): ShadertoyBg {\n const glOrNull = canvas.getContext(\"webgl2\", {\n alpha: false,\n antialias: false,\n preserveDrawingBuffer: false,\n powerPreference: \"high-performance\",\n });\n if (!glOrNull) throw new Error(\"WebGL2 not available\");\n const gl: WebGL2RenderingContext = glOrNull;\n\n const sizeTo = (w: number, h: number): void => {\n canvas.width = w;\n canvas.height = h;\n gl.viewport(0, 0, w, h);\n };\n sizeTo(window.innerWidth, window.innerHeight);\n\n // Audio analyser → 256-bin FFT texture for iChannel0.\n let audioSource: AudioNode = silentSource;\n const analyser = audioCtx.createAnalyser();\n analyser.fftSize = 512; // 256 frequency bins\n audioSource.connect(analyser);\n const fftBytes = new Uint8Array(256);\n const audioTex = gl.createTexture()!;\n gl.bindTexture(gl.TEXTURE_2D, audioTex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n // WebGL2 single-channel texture: R8 internal + RED format. LUMINANCE\n // is deprecated and unreliable in WebGL2 / GLSL 300 es shaders.\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, 256, 1, 0, gl.RED, gl.UNSIGNED_BYTE, null);\n\n // iChannel1: image texture. Starts as 1x1 dim-gray placeholder so the\n // shader has something to sample before bindImage resolves.\n const imageTex = gl.createTexture()!;\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([32, 32, 38, 255]));\n\n const dummyVao = gl.createVertexArray()!; // needed for vertexAttrib-less rendering\n\n let currentProgram: WebGLProgram | null = null;\n let currentUrl: string | null = null;\n let currentName = \"—\";\n let liveSource: HTMLCanvasElement | HTMLVideoElement | null = null;\n const startTime = performance.now();\n let lastFrameTime = startTime;\n let frame = 0;\n let mouseX = 0;\n let mouseY = 0;\n let mouseDown = false;\n let running = true;\n\n function compile(type: GLenum, src: string): WebGLShader {\n const sh = gl.createShader(type)!;\n gl.shaderSource(sh, src);\n gl.compileShader(sh);\n if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(sh) ?? \"(no log)\";\n gl.deleteShader(sh);\n throw new Error(`GLSL compile error:\\n${log}\\n--- source ---\\n${numberLines(src)}`);\n }\n return sh;\n }\n\n function numberLines(src: string): string {\n return src.split(\"\\n\").map((l, i) => `${String(i + 1).padStart(3)}: ${l}`).join(\"\\n\");\n }\n\n function link(userSource: string): WebGLProgram {\n const vs = compile(gl.VERTEX_SHADER, VERTEX_SHADER);\n const fs = compile(gl.FRAGMENT_SHADER, FRAGMENT_PREAMBLE + userSource + FRAGMENT_EPILOGUE);\n const prog = gl.createProgram()!;\n gl.attachShader(prog, vs);\n gl.attachShader(prog, fs);\n gl.linkProgram(prog);\n if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(prog) ?? \"(no log)\";\n gl.deleteShader(vs); gl.deleteShader(fs); gl.deleteProgram(prog);\n throw new Error(`Shader link error: ${log}`);\n }\n gl.deleteShader(vs); gl.deleteShader(fs);\n return prog;\n }\n\n function frameLoop(): void {\n if (!running) return;\n const now = performance.now();\n const iTime = (now - startTime) * 0.001;\n const iTimeDelta = (now - lastFrameTime) * 0.001;\n lastFrameTime = now;\n frame++;\n\n analyser.getByteFrequencyData(fftBytes);\n gl.bindTexture(gl.TEXTURE_2D, audioTex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, 256, 1, 0, gl.RED, gl.UNSIGNED_BYTE, fftBytes);\n\n // If a live source (slideshow canvas, video element, etc.) is bound,\n // upload its current contents to iChannel1 every frame. When the\n // upstream advances to a new image, the shader sees it next frame.\n if (liveSource &&\n (liveSource instanceof HTMLCanvasElement\n ? liveSource.width > 0 && liveSource.height > 0\n : liveSource.readyState >= 2)) {\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n try {\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, liveSource);\n } catch {\n // tainted canvas or zero-size — skip this frame\n }\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n }\n\n if (currentProgram) {\n gl.useProgram(currentProgram);\n const loc = (name: string): WebGLUniformLocation | null =>\n gl.getUniformLocation(currentProgram!, name);\n gl.uniform1f(loc(\"iTime\"), iTime);\n gl.uniform1f(loc(\"iTimeDelta\"), iTimeDelta);\n gl.uniform1i(loc(\"iFrame\"), frame);\n gl.uniform3f(loc(\"iResolution\"), canvas.width, canvas.height, 1.0);\n gl.uniform4f(loc(\"iMouse\"), mouseX, canvas.height - mouseY, mouseDown ? 1 : 0, 0);\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, audioTex);\n gl.uniform1i(loc(\"iChannel0\"), 0);\n gl.activeTexture(gl.TEXTURE1);\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.uniform1i(loc(\"iChannel1\"), 1);\n gl.bindVertexArray(dummyVao);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n } else {\n gl.clearColor(0, 0, 0, 1);\n gl.clear(gl.COLOR_BUFFER_BIT);\n }\n\n requestAnimationFrame(frameLoop);\n }\n requestAnimationFrame(frameLoop);\n\n const onResize = (): void => sizeTo(window.innerWidth, window.innerHeight);\n const onPointerMove = (e: PointerEvent): void => { mouseX = e.clientX; mouseY = e.clientY; };\n const onPointerDown = (): void => { mouseDown = true; };\n const onPointerUp = (): void => { mouseDown = false; };\n window.addEventListener(\"resize\", onResize, { passive: true });\n window.addEventListener(\"pointermove\", onPointerMove, { passive: true });\n window.addEventListener(\"pointerdown\", onPointerDown, { passive: true });\n window.addEventListener(\"pointerup\", onPointerUp, { passive: true });\n\n return {\n get presetName(): string { return currentName; },\n get currentUrl(): string | null { return currentUrl; },\n connectAudio: (node) => {\n audioSource.disconnect();\n audioSource = node;\n audioSource.connect(analyser);\n },\n loadFromUrl: async (url) => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`fetch ${url} → ${res.status}`);\n const src = await res.text();\n const prog = link(src);\n if (currentProgram) gl.deleteProgram(currentProgram);\n currentProgram = prog;\n currentUrl = url;\n currentName = url.split(\"/\").pop()?.replace(/\\.glsl$/, \"\") ?? url;\n return currentName;\n },\n setLiveSource: (source) => {\n liveSource = source;\n },\n bindImage: async (url) => {\n if (!url) {\n // Reset to placeholder\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([32, 32, 38, 255]));\n return;\n }\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error(`image load failed: ${url}`));\n img.src = url;\n });\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n // Use mipmaps if power-of-2; otherwise just LINEAR.\n const pow2 = (img.naturalWidth & (img.naturalWidth - 1)) === 0\n && (img.naturalHeight & (img.naturalHeight - 1)) === 0;\n if (pow2) {\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n } else {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n }\n },\n destroy: () => {\n running = false;\n window.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerdown\", onPointerDown);\n window.removeEventListener(\"pointerup\", onPointerUp);\n if (currentProgram) gl.deleteProgram(currentProgram);\n gl.deleteTexture(audioTex);\n gl.deleteTexture(imageTex);\n gl.deleteVertexArray(dummyVao);\n },\n };\n}\n","// image-feed.ts — headless slideshow.\n//\n// Owns a single offscreen canvas, preloads a list of image URLs, and\n// crossfades between them on a timer. Shaders read the canvas via the\n// player's setLiveSource() — same plumbing as a live video element,\n// just sourced from a slideshow instead of a camera.\n//\n// The site's src/landing/slideshow.ts is a richer surface (drag-resize\n// card, progress bar, melt transitions); this is the minimum the npm\n// package needs so an OSS consumer can pass image: [\"a.jpg\", \"b.jpg\"]\n// and get a feed for the shader without writing canvas plumbing.\n\nconst DEFAULT_HOLD_SECONDS = 6;\nconst CROSSFADE_SECONDS = 1.0;\nconst CANVAS_W = 1280;\nconst CANVAS_H = 720;\n\nexport interface SlideshowOptions {\n /** How long (seconds) each image is held on-screen before the\n * crossfade to the next begins. Defaults to 6. */\n holdSeconds?: number;\n}\n\nexport class HeadlessSlideshow {\n /** The output canvas — bind this to the shader via setLiveSource. */\n readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly urls: string[];\n private readonly holdMs: number;\n private readonly images: Array<HTMLImageElement | null>;\n private currentIndex = 0;\n private nextIndex = 1;\n /** Wall-clock time the current crossfade started, or null if we're\n * in the steady-hold portion of the cycle. */\n private crossfadeStartMs: number | null = null;\n private holdTimer: number | null = null;\n private rafHandle: number | null = null;\n private destroyed = false;\n\n constructor(urls: string[], opts: SlideshowOptions = {}) {\n if (urls.length === 0) {\n throw new Error(\"HeadlessSlideshow: urls list cannot be empty\");\n }\n this.urls = urls;\n this.holdMs = Math.max(0.5, opts.holdSeconds ?? DEFAULT_HOLD_SECONDS) * 1000;\n this.canvas = document.createElement(\"canvas\");\n this.canvas.width = CANVAS_W;\n this.canvas.height = CANVAS_H;\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"HeadlessSlideshow: 2D canvas context unavailable\");\n this.ctx = ctx;\n this.images = urls.map(() => null);\n void this.preload();\n }\n\n destroy(): void {\n this.destroyed = true;\n if (this.holdTimer != null) clearTimeout(this.holdTimer);\n if (this.rafHandle != null) cancelAnimationFrame(this.rafHandle);\n }\n\n private async preload(): Promise<void> {\n // Load all images in parallel, but start rendering + cycling as\n // soon as the first one's ready so the shader isn't stuck on a\n // black frame waiting on slow tails.\n const promises = this.urls.map((url, i) =>\n loadImage(url).then(\n (img) => {\n if (this.destroyed) return;\n this.images[i] = img;\n if (i === 0) this.start();\n },\n (err: Error) => {\n // Failure to load one image shouldn't kill the whole feed —\n // leave that slot as null; the render loop skips it.\n console.warn(`[/prism] image-feed: ${url} → ${err.message}`);\n },\n ),\n );\n await Promise.allSettled(promises);\n }\n\n private start(): void {\n if (this.destroyed) return;\n this.drawFrame();\n this.scheduleNext();\n }\n\n private scheduleNext(): void {\n if (this.destroyed) return;\n this.holdTimer = window.setTimeout(() => this.advance(), this.holdMs);\n }\n\n private advance(): void {\n if (this.destroyed) return;\n // Pick the next loaded image, skipping any that failed to load.\n let candidate = (this.currentIndex + 1) % this.urls.length;\n for (let i = 0; i < this.urls.length; i++) {\n if (this.images[candidate]) break;\n candidate = (candidate + 1) % this.urls.length;\n }\n if (candidate === this.currentIndex) {\n // Only one loaded image — nothing to cross to, just re-arm.\n this.scheduleNext();\n return;\n }\n this.nextIndex = candidate;\n this.crossfadeStartMs = performance.now();\n this.rafHandle = requestAnimationFrame(() => this.tickCrossfade());\n }\n\n private tickCrossfade(): void {\n if (this.destroyed) return;\n if (this.crossfadeStartMs == null) return;\n const t = (performance.now() - this.crossfadeStartMs) / 1000 / CROSSFADE_SECONDS;\n if (t >= 1) {\n // Crossfade finished — commit and queue the next hold.\n this.currentIndex = this.nextIndex;\n this.crossfadeStartMs = null;\n this.drawFrame();\n this.scheduleNext();\n return;\n }\n this.drawCrossfade(t);\n this.rafHandle = requestAnimationFrame(() => this.tickCrossfade());\n }\n\n /** Draw the current image fully opaque. */\n private drawFrame(): void {\n const img = this.images[this.currentIndex];\n if (!img) return;\n this.ctx.fillStyle = \"#000\";\n this.ctx.fillRect(0, 0, CANVAS_W, CANVAS_H);\n drawCovered(this.ctx, img, CANVAS_W, CANVAS_H);\n }\n\n /** Draw the current image, then the next one at alpha=t on top. */\n private drawCrossfade(t: number): void {\n const cur = this.images[this.currentIndex];\n const nxt = this.images[this.nextIndex];\n this.ctx.fillStyle = \"#000\";\n this.ctx.fillRect(0, 0, CANVAS_W, CANVAS_H);\n if (cur) drawCovered(this.ctx, cur, CANVAS_W, CANVAS_H);\n if (nxt) {\n this.ctx.save();\n this.ctx.globalAlpha = t;\n drawCovered(this.ctx, nxt, CANVAS_W, CANVAS_H);\n this.ctx.restore();\n }\n }\n}\n\nfunction loadImage(url: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`image load failed: ${url}`));\n img.src = url;\n });\n}\n\n/** Draw `img` covering the (w,h) box — like CSS object-fit: cover.\n * Cropping rather than letterboxing keeps the slideshow visually full\n * in the shader's iChannel1, which is usually being sampled by uv\n * coordinates that assume the whole texture is content. */\nfunction drawCovered(\n ctx: CanvasRenderingContext2D,\n img: HTMLImageElement,\n w: number,\n h: number,\n): void {\n const srcAR = img.naturalWidth / img.naturalHeight;\n const dstAR = w / h;\n let sx = 0, sy = 0, sw = img.naturalWidth, sh = img.naturalHeight;\n if (srcAR > dstAR) {\n // image is wider — crop horizontally\n sw = img.naturalHeight * dstAR;\n sx = (img.naturalWidth - sw) / 2;\n } else {\n sh = img.naturalWidth / dstAR;\n sy = (img.naturalHeight - sh) / 2;\n }\n ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h);\n}\n","// prism.graph/0.1 — node-graph schema for visualizations.\n//\n// Every Prism visualization is a JSON document of this shape. Nodes fall\n// into five role-tagged categories that mirror the fundamental abstraction:\n//\n// signal source → signal xform → light field generator → lf operator → sink\n//\n// In English: Prism computes light fields from signals.\n//\n// M1 only exercises the minimal graph (audio → milkdrop → display); the\n// schema is intentionally roomy so M8/M9 can add more generators and\n// compositor ops without rewriting it.\n\nexport const SCHEMA_VERSION = \"prism.graph/0.1\" as const;\n\nexport type NodeRole = \"signal\" | \"xform\" | \"lf\" | \"op\" | \"sink\";\n\nexport type NodeType =\n // signal sources — produce a streaming signal\n | \"signal.audio\"\n | \"signal.cursor\"\n | \"signal.heartbeat\"\n | \"signal.synth\"\n // signal transformers — signal → signal\n | \"xform.gain\"\n | \"xform.beat\"\n // light field generators — produce a frame from signals\n | \"lf.milkdrop\"\n | \"lf.shadertoy\"\n | \"lf.isf\"\n // light field operators — frame(s) → frame\n | \"op.blend\"\n | \"op.displace\"\n | \"op.feedback\"\n // sinks — terminate the graph\n | \"sink.display\"\n | \"sink.recorder\";\n\nexport type NodeParam =\n | string\n | number\n | boolean\n | string[]\n | number[]\n | Record<string, string | number | boolean>;\n\n/** A single node in the graph. Inputs reference upstream node outputs by\n * the string \"<node_id>.<output_name>\". The runtime resolves these. */\nexport interface NodeDef {\n type: NodeType;\n params?: Record<string, NodeParam>;\n inputs?: Record<string, string>;\n}\n\nexport interface PrismGraph {\n schema: typeof SCHEMA_VERSION;\n /** Stable id — enables share-by-URL in M6. */\n id: string;\n /** Human-readable summary of what the graph does. Shown in the SKILL\n * readout; lets the AI explain its choice. */\n intent: string;\n nodes: Record<string, NodeDef>;\n /** Node id of the terminal sink. */\n output: string;\n}\n\n/** Extract the role prefix from a node type. */\nexport function roleOf(type: NodeType): NodeRole {\n return type.split(\".\")[0] as NodeRole;\n}\n\n/** Iterate nodes of a given role. */\nexport function nodesByRole(graph: PrismGraph, role: NodeRole): Array<[string, NodeDef]> {\n return Object.entries(graph.nodes).filter(([, n]) => roleOf(n.type) === role);\n}\n\n/** Build the M1 minimal graph: audio_in → lf.milkdrop(preset) → sink.display. */\nexport function makeMilkdropGraph(\n presetName: string,\n intent: string,\n blendSeconds = 2.5,\n): PrismGraph {\n return {\n schema: SCHEMA_VERSION,\n id: `g_${Math.random().toString(36).slice(2, 10)}`,\n intent,\n nodes: {\n audio_in: { type: \"signal.audio\", params: {} },\n main: {\n type: \"lf.milkdrop\",\n params: { preset_name: presetName, blend_seconds: blendSeconds },\n inputs: { audio: \"audio_in.signal\" },\n },\n out: { type: \"sink.display\", inputs: { frame: \"main.frame\" } },\n },\n output: \"out\",\n };\n}\n","// registry.ts — share-token lookup for catalog content.\n//\n// Every catalog entry on prism.scott.ai has a 6-char base62 token (the\n// short_id). Consumers pass one to `player.load(...)` or via the URL\n// param `?g=<short_id>` and the player resolves it to a PrismGraph\n// offline — no network call. The map is generated from the catalog at\n// build-index time; see scripts/prism/commands/build-index.ts.\n\nimport registryData from \"./registry.generated.json\";\nimport { SCHEMA_VERSION, type NodeDef, type NodeType, type PrismGraph } from \"./types\";\n\nexport interface RegistryEntry {\n name: string;\n source_type: \"milkdrop\" | \"shadertoy\" | \"isf\" | \"wgsl\";\n source_loader: \"url\" | \"npm-butterchurn-presets\";\n source_url?: string;\n source_ref?: string;\n default_image?: string;\n}\n\nconst registry = registryData as Record<string, RegistryEntry>;\n\n/** Resolve a 6-char short_id to a registry entry. Returns null when\n * the id isn't known — callers decide whether to fall back, error,\n * or just keep the synthetic cold-open running. */\nexport function lookup(shortId: string): RegistryEntry | null {\n return registry[shortId] ?? null;\n}\n\n/** All known short_ids. Mostly useful for tests + the rotation pool. */\nexport function shortIds(): string[] {\n return Object.keys(registry);\n}\n\n/** Build a minimal PrismGraph that plays the entry behind `shortId`.\n * Returns null when the id isn't in the registry. */\nexport function shortIdToGraph(shortId: string): PrismGraph | null {\n const entry = lookup(shortId);\n if (!entry) return null;\n return entryToGraph(shortId, entry);\n}\n\nfunction entryToGraph(shortId: string, entry: RegistryEntry): PrismGraph {\n const mainParams: Record<string, string> = {};\n let mainType: NodeType;\n if (entry.source_type === \"shadertoy\") {\n mainType = \"lf.shadertoy\";\n if (entry.source_url) mainParams.shader_url = entry.source_url;\n if (entry.default_image) mainParams.image_url = entry.default_image;\n } else {\n mainType = \"lf.milkdrop\";\n if (entry.source_loader === \"url\" && entry.source_url) {\n mainParams.preset_url = entry.source_url;\n } else if (entry.source_ref) {\n mainParams.preset_name = entry.source_ref;\n }\n }\n const nodes: Record<string, NodeDef> = {\n audio: { type: \"signal.audio\" },\n main: { type: mainType, params: mainParams, inputs: { audio: \"audio.signal\" } },\n screen: { type: \"sink.display\", inputs: { frame: \"main.frame\" } },\n };\n return {\n schema: SCHEMA_VERSION,\n id: `g:${shortId}`,\n intent: entry.name,\n nodes,\n output: \"screen\",\n };\n}\n","// runtime.ts — graph executor for prism.graph/0.1.\n//\n// Walks a PrismGraph, finds the light-field generator node, dispatches\n// to the appropriate backend (milkdrop / shadertoy / ...). Future M9\n// will add op.* nodes (blend / displace) and a real compositor; today\n// it's single-backend-at-a-time and swaps as needed.\n//\n// The runtime owns visibility of the two background canvases — only the\n// active backend's canvas is shown, the other is hidden. URL-loaded\n// presets (favorites + shadertoys) load asynchronously; runtime.apply\n// returns synchronously after kicking off the load and updating state.\n\nimport type { MilkdropBg } from \"./backends/milkdrop\";\nimport type { ShadertoyBg } from \"./backends/shadertoy\";\nimport { nodesByRole, type PrismGraph } from \"./types\";\n\nexport interface RuntimeContext {\n milkdrop: MilkdropBg;\n shadertoy: ShadertoyBg;\n /** Called to toggle which backend's canvas is visible.\n * Implementations set CSS opacity/display on the two canvases. */\n setActiveBackend: (which: \"milkdrop\" | \"shadertoy\") => void;\n}\n\nexport interface ApplyResult {\n ok: boolean;\n error?: string;\n /** Display name of the preset that was loaded, if any. */\n presetName?: string;\n /** Which backend handled this graph. */\n backend?: \"milkdrop\" | \"shadertoy\";\n}\n\nexport class GraphRuntime {\n private active: PrismGraph | null = null;\n\n constructor(private readonly ctx: RuntimeContext) {}\n\n apply(graph: PrismGraph, blendSecondsOverride?: number): ApplyResult {\n const lfNodes = nodesByRole(graph, \"lf\");\n if (lfNodes.length === 0) {\n return { ok: false, error: \"graph has no light-field generator\" };\n }\n const [, node] = lfNodes[0];\n\n if (node.type === \"lf.milkdrop\") {\n const presetName = node.params?.preset_name;\n const presetUrl = node.params?.preset_url;\n const blendSeconds =\n blendSecondsOverride ??\n (typeof node.params?.blend_seconds === \"number\" ? node.params.blend_seconds : 2.5);\n this.ctx.setActiveBackend(\"milkdrop\");\n // URL takes precedence (favorites). Fall back to name (npm bundle / legacy).\n if (typeof presetUrl === \"string\") {\n void this.ctx.milkdrop.loadFromUrl(presetUrl, blendSeconds).catch((err: Error) => {\n console.warn(\"[runtime] milkdrop loadFromUrl failed:\", err.message);\n });\n this.active = graph;\n const name = presetUrl.split(\"/\").pop()?.replace(/\\.milk$/, \"\") ?? presetUrl;\n return { ok: true, presetName: name, backend: \"milkdrop\" };\n }\n if (typeof presetName !== \"string\") {\n return { ok: false, error: \"lf.milkdrop missing preset_name or preset_url\" };\n }\n const loaded = this.ctx.milkdrop.loadByName(presetName, blendSeconds);\n if (loaded === null) {\n return { ok: false, error: `preset not found: ${presetName}` };\n }\n this.active = graph;\n return { ok: true, presetName: loaded, backend: \"milkdrop\" };\n }\n\n if (node.type === \"lf.shadertoy\") {\n const url = node.params?.shader_url;\n if (typeof url !== \"string\") {\n return { ok: false, error: \"lf.shadertoy missing shader_url\" };\n }\n const imageUrl = node.params?.image_url;\n this.ctx.setActiveBackend(\"shadertoy\");\n void this.ctx.shadertoy.loadFromUrl(url).catch((err: Error) => {\n console.warn(\"[runtime] shadertoy loadFromUrl failed:\", err.message);\n });\n // Bind the entry's default image to iChannel1 if specified. The\n // shader has a 1x1 grey placeholder until this resolves.\n void this.ctx.shadertoy.bindImage(typeof imageUrl === \"string\" ? imageUrl : null)\n .catch((err: Error) => {\n console.warn(\"[runtime] shadertoy bindImage failed:\", err.message);\n });\n this.active = graph;\n const name = url.split(\"/\").pop()?.replace(/\\.glsl$/, \"\") ?? url;\n return { ok: true, presetName: name, backend: \"shadertoy\" };\n }\n\n return { ok: false, error: `unsupported lf type: ${node.type}` };\n }\n\n get current(): PrismGraph | null {\n return this.active;\n }\n}\n","// synthetic-signal.ts — silent \"fractal\" audio driver.\n// Generates a pink-noise-like spectrum (octave-spaced oscillators, amplitude\n// ∝ 1/√f) plus periodic beat envelopes and slow frequency mutations. The\n// signal is read by butterchurn's analyser to keep the Milkdrop preset\n// animated while no real audio is shared. Output never reaches ctx.destination,\n// so nothing is audible.\n\ntype Band = \"bass\" | \"mid\" | \"treble\";\ntype Voice = {\n base: number;\n baseGain: number;\n band: Band;\n osc: OscillatorNode;\n lfo: OscillatorNode;\n lfoGain: GainNode;\n gain: GainNode;\n};\n\nexport class SyntheticSignal {\n private readonly ctx: AudioContext;\n private readonly output: GainNode;\n private readonly voices: Voice[] = [];\n private readonly analyser: AnalyserNode;\n private readonly fft: Uint8Array;\n private beatTimer: number | null = null;\n private mutationTimer: number | null = null;\n private stopped = false;\n private lastCursorBeatAt = 0;\n\n constructor(ctx: AudioContext) {\n this.ctx = ctx;\n this.output = ctx.createGain();\n this.output.gain.value = 0.65;\n\n // Octave-spaced voices producing a 1/√f spectrum — convincingly \"musical\"\n // to the analyser without being recognisable as anything in particular.\n // freqHz, type, baseGain, lfoHz, lfoDepth\n const seeds: Array<[number, OscillatorType, number, number, number]> = [\n [ 50, \"sine\", 0.55, 0.06, 18 ],\n [ 110, \"sine\", 0.40, 0.09, 24 ],\n [ 230, \"triangle\", 0.28, 0.12, 60 ],\n [ 460, \"sawtooth\", 0.18, 0.17, 140 ],\n [ 950, \"triangle\", 0.12, 0.21, 280 ],\n [ 1900, \"sine\", 0.08, 0.27, 520 ],\n [ 3900, \"triangle\", 0.05, 0.33, 1100 ],\n ];\n for (const [f, type, g, lfoHz, lfoD] of seeds) this.addVoice(f, type, g, lfoHz, lfoD);\n\n // Internal analyser so callers can read our energy without colliding with\n // butterchurn's own analyser.\n this.analyser = ctx.createAnalyser();\n this.analyser.fftSize = 256;\n this.analyser.smoothingTimeConstant = 0.78;\n this.output.connect(this.analyser);\n this.fft = new Uint8Array(this.analyser.frequencyBinCount);\n\n this.scheduleBeats();\n this.scheduleMutations();\n }\n\n /** The node to feed into a consumer (e.g. butterchurn.connectAudio). */\n getOutput(): AudioNode {\n return this.output;\n }\n\n /** Returns a smoothed 0..1 energy reading from our own analyser. */\n readEnergy(): number {\n if (this.stopped) return 0;\n this.analyser.getByteFrequencyData(this.fft as unknown as Uint8Array<ArrayBuffer>);\n let s = 0;\n for (let i = 0; i < this.fft.length; i++) s += this.fft[i];\n return s / 255 / this.fft.length;\n }\n\n /** Returns smoothed bass/mid/treble bands (0..1 each). */\n readBands(): { bass: number; mid: number; treble: number } {\n if (this.stopped) return { bass: 0, mid: 0, treble: 0 };\n this.analyser.getByteFrequencyData(this.fft as unknown as Uint8Array<ArrayBuffer>);\n const n = this.fft.length;\n const bassEnd = Math.floor(n * 0.10);\n const midEnd = Math.floor(n * 0.45);\n let b = 0, m = 0, t = 0;\n for (let i = 0; i < n; i++) {\n const v = this.fft[i] / 255;\n if (i < bassEnd) b += v;\n else if (i < midEnd) m += v;\n else t += v;\n }\n return {\n bass: b / Math.max(1, bassEnd),\n mid: m / Math.max(1, midEnd - bassEnd),\n treble: t / Math.max(1, n - midEnd),\n };\n }\n\n stop(): void {\n if (this.stopped) return;\n this.stopped = true;\n if (this.beatTimer != null) clearTimeout(this.beatTimer);\n if (this.mutationTimer != null) clearTimeout(this.mutationTimer);\n for (const v of this.voices) {\n try { v.osc.stop(); v.lfo.stop(); } catch { /* ignore */ }\n try { v.osc.disconnect(); v.lfo.disconnect(); v.lfoGain.disconnect(); v.gain.disconnect(); }\n catch { /* ignore */ }\n }\n try { this.output.disconnect(); } catch { /* ignore */ }\n try { this.analyser.disconnect(); } catch { /* ignore */ }\n }\n\n private addVoice(\n freq: number,\n type: OscillatorType,\n gain: number,\n lfoHz: number,\n lfoDepth: number,\n ): void {\n const ctx = this.ctx;\n const osc = ctx.createOscillator();\n osc.frequency.value = freq;\n osc.type = type;\n const g = ctx.createGain();\n g.gain.value = gain;\n osc.connect(g).connect(this.output);\n\n const lfo = ctx.createOscillator();\n lfo.frequency.value = lfoHz;\n lfo.type = \"sine\";\n const lfoGain = ctx.createGain();\n lfoGain.gain.value = lfoDepth;\n lfo.connect(lfoGain).connect(osc.frequency);\n\n lfo.start();\n osc.start();\n const band: Band = freq < 200 ? \"bass\" : freq < 1100 ? \"mid\" : \"treble\";\n this.voices.push({ base: freq, baseGain: gain, band, osc, lfo, lfoGain, gain: g });\n }\n\n /**\n * Modulate the synth from the cursor. Position controls the bass/treble\n * balance (top-right = bright/airy; bottom-left = bassy/warm); velocity\n * triggers a beat envelope on the master gain. The cursor becomes the\n * instrument driving the milkdrop preset.\n *\n * @param x01 cursor X normalised to 0..1 (left .. right)\n * @param y01 cursor Y normalised to 0..1 (top .. bottom)\n * @param vel01 cursor speed normalised to 0..1\n */\n setCursorModulation(x01: number, y01: number, vel01: number): void {\n if (this.stopped) return;\n const now = this.ctx.currentTime;\n const ramp = 0.12;\n const x = Math.max(0, Math.min(1, x01));\n const y = Math.max(0, Math.min(1, y01));\n const v = Math.max(0, Math.min(1, vel01));\n\n // bass: bottom of screen = louder\n const bassMul = 0.55 + (1 - y) * 0.90 + v * 0.15;\n // treble: right of screen = brighter\n const trebleMul = 0.55 + x * 0.90 + v * 0.20;\n // mid: rises with velocity (gives \"presence\" to motion)\n const midMul = 0.7 + v * 0.7;\n\n for (const voice of this.voices) {\n const target =\n voice.band === \"bass\" ? voice.baseGain * bassMul :\n voice.band === \"treble\" ? voice.baseGain * trebleMul :\n voice.baseGain * midMul;\n voice.gain.gain.cancelScheduledValues(now);\n voice.gain.gain.setValueAtTime(voice.gain.gain.value, now);\n voice.gain.gain.linearRampToValueAtTime(target, now + ramp);\n }\n\n // Velocity-burst → master gain envelope (rate-limited so a steady fast\n // glide doesn't constantly hammer the analyser).\n if (v > 0.45 && now - this.lastCursorBeatAt > 0.22) {\n const peak = 0.95 + v * 0.45;\n this.output.gain.cancelScheduledValues(now);\n this.output.gain.setValueAtTime(this.output.gain.value, now);\n this.output.gain.linearRampToValueAtTime(peak, now + 0.035);\n this.output.gain.exponentialRampToValueAtTime(0.52, now + 0.22);\n this.lastCursorBeatAt = now;\n }\n }\n\n /**\n * Fire a bass-kick envelope — for hooking up heartbeats, MIDI clicks, or\n * any external rhythmic trigger. Sharp attack + bass voice swell so the\n * spectrum butterchurn sees registers as a clean bass beat.\n *\n * @param intensity 0..1, scales the peak\n */\n pulseBeat(intensity = 0.8): void {\n if (this.stopped) return;\n const now = this.ctx.currentTime;\n const i = Math.max(0, Math.min(1, intensity));\n\n // Master gain — sharp attack, exponential decay (kick-drum shape)\n const masterPeak = 0.95 + i * 0.55;\n this.output.gain.cancelScheduledValues(now);\n this.output.gain.setValueAtTime(this.output.gain.value, now);\n this.output.gain.linearRampToValueAtTime(masterPeak, now + 0.035);\n this.output.gain.exponentialRampToValueAtTime(0.42, now + 0.26);\n\n // Bass voices get an extra swell so the kick lands in the low band\n // (which is where butterchurn's preset reactivity usually lives).\n for (const voice of this.voices) {\n if (voice.band !== \"bass\") continue;\n const peak = voice.baseGain * (1.6 + i * 1.4);\n voice.gain.gain.cancelScheduledValues(now);\n voice.gain.gain.setValueAtTime(voice.gain.gain.value, now);\n voice.gain.gain.linearRampToValueAtTime(peak, now + 0.025);\n voice.gain.gain.exponentialRampToValueAtTime(\n Math.max(0.01, voice.baseGain), now + 0.34,\n );\n }\n }\n\n /** Periodic \"beat\" — short amplitude burst on the master gain. */\n private scheduleBeats(): void {\n const tick = (): void => {\n if (this.stopped) return;\n const now = this.ctx.currentTime;\n const peak = 0.95 + Math.random() * 0.35;\n this.output.gain.cancelScheduledValues(now);\n this.output.gain.setValueAtTime(this.output.gain.value, now);\n this.output.gain.linearRampToValueAtTime(peak, now + 0.04);\n this.output.gain.exponentialRampToValueAtTime(0.45, now + 0.28);\n // BPM-ish range 60–110, jittered\n const nextMs = 600 + Math.random() * 1000;\n this.beatTimer = window.setTimeout(tick, nextMs);\n };\n this.beatTimer = window.setTimeout(tick, 900);\n }\n\n /** Every ~20–35 s, smoothly mutate one voice's base frequency over 8 s. */\n private scheduleMutations(): void {\n const tick = (): void => {\n if (this.stopped) return;\n const v = this.voices[Math.floor(Math.random() * this.voices.length)];\n const now = this.ctx.currentTime;\n const ratio = 0.65 + Math.random() * 0.7; // 0.65× .. 1.35×\n const target = Math.max(20, Math.min(6000, v.base * ratio));\n v.osc.frequency.cancelScheduledValues(now);\n v.osc.frequency.setValueAtTime(v.osc.frequency.value, now);\n v.osc.frequency.linearRampToValueAtTime(target, now + 7.5);\n v.base = target;\n this.mutationTimer = window.setTimeout(tick, 18000 + Math.random() * 16000);\n };\n this.mutationTimer = window.setTimeout(tick, 6000);\n }\n}\n","// player.ts — public PrismPlayer class.\n//\n// Owns a small DOM tree (two stacked <canvas> elements inside `container`),\n// an AudioContext, a SyntheticSignal (the default audio driver), the two\n// rendering backends, and a GraphRuntime that swaps between them.\n//\n// Minimal usage:\n//\n// const player = new PrismPlayer({ container: el, graph });\n//\n// The caller can override the audio source any time via connectAudio(),\n// swap graphs via load(), or pipe a live texture into iChannel1 via\n// setLiveSource(). Backends + runtime + synth + audioCtx are exposed\n// readonly so embedders can drive them directly (read presetName, fire\n// pulseBeat from an external trigger, etc.) without re-creating the API.\n\nimport { createMilkdropBackground, type MilkdropBg } from \"./backends/milkdrop\";\nimport { createShadertoyBackground, type ShadertoyBg } from \"./backends/shadertoy\";\nimport { HeadlessSlideshow } from \"./image-feed\";\nimport { shortIdToGraph } from \"./registry\";\nimport { GraphRuntime, type ApplyResult } from \"./runtime\";\nimport { SyntheticSignal } from \"./synth\";\nimport type { PrismGraph } from \"./types\";\n\n/** Anything `load()` and the `graph` option accept. A string is treated\n * as a catalog short_id (6-char base62) and resolved against the\n * bundled registry. */\nexport type GraphInput = PrismGraph | string;\n\n/** Anything the `image` option / `connectImage()` accepts. Strings\n * beyond the \"webcam\" / \"tab\" sentinels are treated as static URLs. */\nexport type ImageSource =\n | \"webcam\"\n | \"tab\"\n | string\n | string[]\n | HTMLCanvasElement\n | HTMLVideoElement\n | MediaStream;\n\nexport interface PrismPlayerOptions {\n /** Where to mount the visualization canvases. Pass either an element\n * (preferred from frameworks — React refs, Vue template refs, etc.)\n * or a DOM id string for hand-written HTML embeds. The player\n * creates its own canvases inside; it does not touch any children\n * that already exist there. */\n container: HTMLElement | string;\n /** Optional initial graph. Pass a PrismGraph object directly, or a\n * 6-char short_id string (e.g. \"7Hq3pK\") to look up an entry from\n * the bundled catalog. Without it, cold-opens on a curated milkdrop\n * preset; you can call load() later. */\n graph?: GraphInput;\n /** Audio source. An AudioNode is connected directly; \"mic\" and \"tab\"\n * request the matching media stream via getUserMedia /\n * getDisplayMedia and connect that. Undefined → the built-in\n * SyntheticSignal drives both backends. */\n audio?: \"mic\" | \"tab\" | MediaStream | AudioNode;\n /** Bring your own AudioContext (useful when the embedder already has\n * one; web pages can only have a small number of them). */\n audioCtx?: AudioContext;\n /** iChannel1 fallback used by shader backends until something more\n * specific is bound by the graph or setLiveSource(). */\n defaultImage?: string;\n /** Image feed for shader backends (bound to iChannel1). Accepts:\n * - A single URL → static image\n * - An array of URLs → built-in crossfading slideshow\n * - \"webcam\" / \"tab\" → getUserMedia / getDisplayMedia\n * - a MediaStream / HTMLVideoElement → live video\n * - an HTMLCanvasElement → live canvas (e.g. your own\n * renderer; the player just\n * re-uploads its contents every\n * frame)\n * Use this for \"I want pictures showing through the shader\" without\n * thinking about iChannel1 or setLiveSource. */\n image?: ImageSource;\n /** Seconds each image is held on-screen before the crossfade to the\n * next begins. Only used when `image` is a URL array. Defaults to 6. */\n holdSeconds?: number;\n /** Milkdrop preset to load on cold-open. Falls back to a random preset\n * from the bundled library if the name isn't found. */\n initialPresetName?: string;\n /** Called once the first frame has painted. */\n onReady?: () => void;\n /** Async failures (audio capture rejection, graph load errors that\n * cannot be surfaced from a synchronous call) land here. */\n onError?: (err: Error) => void;\n}\n\nexport class PrismPlayer {\n /** AudioContext driving both backends + the default synthetic signal.\n * Created on construction unless one was passed in `opts.audioCtx`. */\n readonly audioCtx: AudioContext;\n /** Default audio driver: a silent \"pink-noise-ish\" pad that keeps the\n * visualization animated when no real audio is connected. Kept alive\n * for the lifetime of the player — call `player.synth.stop()` if you\n * want to free it after switching to a real audio source. */\n readonly synth: SyntheticSignal;\n /** Milkdrop (butterchurn) backend handle. Cold-opens on a random\n * preset (or `initialPresetName` if you passed one). */\n readonly milkdrop: MilkdropBg;\n /** Shadertoy backend handle. Idle until a graph with `lf.shadertoy` is\n * loaded (or you call `shadertoy.loadFromUrl` directly). */\n readonly shadertoy: ShadertoyBg;\n /** Graph executor — dispatches to the right backend based on the\n * graph's light-field generator node. */\n readonly runtime: GraphRuntime;\n /** Tracks which backend's canvas is currently visible. */\n activeBackend: \"milkdrop\" | \"shadertoy\" = \"milkdrop\";\n\n private readonly milkdropCanvas: HTMLCanvasElement;\n private readonly shadertoyCanvas: HTMLCanvasElement;\n private readonly ownsAudioCtx: boolean;\n /** Default hold per image when `image` is a URL list; set in ctor. */\n private readonly defaultHoldSeconds: number;\n /** Owned media resources (MediaStreams we started, <video> elements\n * we created internally, headless slideshow). Cleaned up by\n * connectImage() before re-binding and by destroy() on teardown. */\n private currentSlideshow: HeadlessSlideshow | null = null;\n private currentVideo: HTMLVideoElement | null = null;\n private currentStream: MediaStream | null = null;\n /** Audio MediaStream the player started itself (\"mic\" / \"tab\"). Held\n * so disconnectAudio() can stop the tracks. Null if the caller\n * passed in their own stream/node (we never stop tracks we don't own). */\n private currentAudioOwnedStream: MediaStream | null = null;\n\n constructor(opts: PrismPlayerOptions) {\n const container = resolveContainer(opts.container);\n\n this.audioCtx = opts.audioCtx ?? new AudioContext();\n this.ownsAudioCtx = opts.audioCtx === undefined;\n this.defaultHoldSeconds = opts.holdSeconds ?? 6;\n this.synth = new SyntheticSignal(this.audioCtx);\n\n // Two stacked canvases — opacity-crossfaded by setActiveBackend.\n // Match the legacy id/class pair so existing CSS (cold-open animation,\n // bg-canvas--hidden/--active modifiers) still applies on prism.scott.ai.\n this.milkdropCanvas = createCanvas(\"milkdrop\");\n this.shadertoyCanvas = createCanvas(\"shadertoy\");\n this.shadertoyCanvas.classList.add(\"bg-canvas--hidden\");\n container.appendChild(this.milkdropCanvas);\n container.appendChild(this.shadertoyCanvas);\n\n this.milkdrop = createMilkdropBackground(\n this.audioCtx,\n this.milkdropCanvas,\n this.synth.getOutput(),\n opts.onReady,\n { initialPresetName: opts.initialPresetName },\n );\n this.shadertoy = createShadertoyBackground(\n this.audioCtx,\n this.shadertoyCanvas,\n this.synth.getOutput(),\n );\n\n if (opts.defaultImage) {\n void this.shadertoy.bindImage(opts.defaultImage).catch((err: Error) => {\n opts.onError?.(err);\n });\n }\n\n this.runtime = new GraphRuntime({\n milkdrop: this.milkdrop,\n shadertoy: this.shadertoy,\n setActiveBackend: (which) => this.setActiveBackend(which),\n });\n\n if (opts.graph !== undefined) {\n const result = this.load(opts.graph);\n if (!result.ok && opts.onError) {\n opts.onError(new Error(result.error ?? \"graph apply failed\"));\n }\n }\n\n if (opts.audio !== undefined) {\n void this.connectAudio(opts.audio).catch((err: Error) => opts.onError?.(err));\n }\n\n if (opts.image !== undefined) {\n void this.connectImage(opts.image).catch((err: Error) => opts.onError?.(err));\n }\n }\n\n /** Swap to a new graph. Accepts either a full PrismGraph object or a\n * 6-char short_id string from the bundled registry. Returns the\n * ApplyResult the runtime emits so the caller can react to backend\n * switches / errors. An unknown short_id returns\n * `{ ok: false, error: \"unknown short_id ...\" }` without touching\n * the running visualization. */\n load(graph: GraphInput, blendSeconds?: number): ApplyResult {\n if (typeof graph === \"string\") {\n const resolved = shortIdToGraph(graph);\n if (!resolved) {\n return { ok: false, error: `unknown short_id: ${graph}` };\n }\n return this.runtime.apply(resolved, blendSeconds);\n }\n return this.runtime.apply(graph, blendSeconds);\n }\n\n /** Connect a new audio source. Accepts:\n * - \"mic\" — getUserMedia({ audio: true })\n * - \"tab\" — getDisplayMedia({ audio: true })\n * - MediaStream — wrapped in a MediaStreamAudioSourceNode\n * - AudioNode — connected directly\n * Routes the source to both backends so whichever one is active sees\n * reactivity. Tracks any MediaStream the player started itself so\n * disconnectAudio() can stop the tracks (mic indicator off, etc.). */\n async connectAudio(source: \"mic\" | \"tab\" | MediaStream | AudioNode): Promise<AudioNode> {\n const { node, owned } = await this.resolveAudioNode(source);\n this.milkdrop.connectAudio(node);\n this.shadertoy.connectAudio(node);\n this.currentAudioOwnedStream = owned;\n return node;\n }\n\n /** Disconnect the current audio source and revert both backends to\n * the built-in SyntheticSignal. Stops any MediaStream tracks the\n * player started itself (mic / tab capture indicator goes off).\n * Streams or AudioNodes the caller passed in are left untouched —\n * the caller owns their lifetime. Idempotent. */\n disconnectAudio(): void {\n if (this.currentAudioOwnedStream) {\n for (const track of this.currentAudioOwnedStream.getTracks()) track.stop();\n this.currentAudioOwnedStream = null;\n }\n const fallback = this.synth.getOutput();\n this.milkdrop.connectAudio(fallback);\n this.shadertoy.connectAudio(fallback);\n }\n\n /** Pipe a live source (e.g. a slideshow's canvas) into iChannel1 — its\n * contents are re-uploaded each frame. Pass null to disable.\n * Most embedders should prefer connectImage(), which handles\n * webcam/tab/slideshow plumbing for you. */\n setLiveSource(source: HTMLCanvasElement | HTMLVideoElement | null): void {\n this.shadertoy.setLiveSource(source);\n }\n\n /** Connect an image source for the shader's iChannel1. Symmetric to\n * connectAudio. Replaces any prior image feed; old MediaStream\n * tracks are stopped and internal slideshow timers are cleared.\n * See ImageSource for the accepted shapes. */\n async connectImage(source: ImageSource): Promise<void> {\n this.teardownImageFeed();\n if (typeof source === \"string\") {\n if (source === \"webcam\") {\n const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });\n this.bindStream(stream);\n return;\n }\n if (source === \"tab\") {\n const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });\n this.bindStream(stream);\n return;\n }\n // Treat any other string as a static image URL.\n this.setLiveSource(null);\n await this.shadertoy.bindImage(source);\n return;\n }\n if (Array.isArray(source)) {\n const slideshow = new HeadlessSlideshow(source, { holdSeconds: this.defaultHoldSeconds });\n this.currentSlideshow = slideshow;\n this.setLiveSource(slideshow.canvas);\n return;\n }\n if (source instanceof MediaStream) {\n this.bindStream(source);\n return;\n }\n // Canvas or video element passed in directly — wire it as the\n // live source without owning it (caller manages its lifetime).\n this.setLiveSource(source);\n }\n\n /** Stop the current image feed and unbind from the shader. Releases\n * resources the player owned: slideshow timer cleared, MediaStream\n * tracks stopped (webcam light off), internal <video> detached.\n * Canvas/video elements the caller passed in are left untouched.\n * After this, the shader reverts to whatever was last bound via\n * `defaultImage` or `shadertoy.bindImage()` (or the 1×1 placeholder).\n * Idempotent. */\n disconnectImage(): void {\n this.teardownImageFeed();\n }\n\n /** Toggle which backend's canvas is visible. Called by GraphRuntime;\n * callers can flip it manually too (rare). */\n setActiveBackend(which: \"milkdrop\" | \"shadertoy\"): void {\n this.activeBackend = which;\n if (which === \"milkdrop\") {\n this.milkdropCanvas.classList.remove(\"bg-canvas--hidden\");\n this.milkdropCanvas.classList.add(\"bg-canvas--active\");\n this.shadertoyCanvas.classList.add(\"bg-canvas--hidden\");\n this.shadertoyCanvas.classList.remove(\"bg-canvas--active\");\n } else {\n this.shadertoyCanvas.classList.remove(\"bg-canvas--hidden\");\n this.shadertoyCanvas.classList.add(\"bg-canvas--active\");\n this.milkdropCanvas.classList.add(\"bg-canvas--hidden\");\n this.milkdropCanvas.classList.remove(\"bg-canvas--active\");\n }\n }\n\n /** Stop animation, free GL resources, detach canvases, close the\n * AudioContext (only if the player created it itself). */\n destroy(): void {\n this.teardownImageFeed();\n if (this.currentAudioOwnedStream) {\n for (const track of this.currentAudioOwnedStream.getTracks()) track.stop();\n this.currentAudioOwnedStream = null;\n }\n this.milkdrop.destroy();\n this.shadertoy.destroy();\n this.synth.stop();\n this.milkdropCanvas.remove();\n this.shadertoyCanvas.remove();\n if (this.ownsAudioCtx) {\n void this.audioCtx.close();\n }\n }\n\n /** Wrap a MediaStream in an autoplaying <video> and bind that as the\n * live source. The video element is held internally; teardown is\n * handled by teardownImageFeed(). */\n private bindStream(stream: MediaStream): void {\n const video = document.createElement(\"video\");\n video.srcObject = stream;\n video.autoplay = true;\n video.muted = true;\n video.playsInline = true;\n // Auto-play may reject (Safari, autoplay policy); we ignore — the\n // user gesture that opened the stream usually satisfies the policy.\n void video.play().catch(() => undefined);\n this.currentVideo = video;\n this.currentStream = stream;\n this.setLiveSource(video);\n }\n\n /** Stop the current image feed (slideshow timer, MediaStream tracks,\n * internal <video>) and unbind from the shader. Idempotent. */\n private teardownImageFeed(): void {\n if (this.currentSlideshow) {\n this.currentSlideshow.destroy();\n this.currentSlideshow = null;\n }\n if (this.currentStream) {\n for (const track of this.currentStream.getTracks()) track.stop();\n this.currentStream = null;\n }\n if (this.currentVideo) {\n this.currentVideo.srcObject = null;\n this.currentVideo = null;\n }\n this.setLiveSource(null);\n }\n\n private async resolveAudioNode(\n source: \"mic\" | \"tab\" | MediaStream | AudioNode,\n ): Promise<{ node: AudioNode; owned: MediaStream | null }> {\n if (source instanceof AudioNode) return { node: source, owned: null };\n let stream: MediaStream;\n let owned = false;\n if (source instanceof MediaStream) {\n stream = source;\n } else if (source === \"mic\") {\n stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });\n owned = true;\n } else {\n stream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: false });\n owned = true;\n }\n return {\n node: this.audioCtx.createMediaStreamSource(stream),\n owned: owned ? stream : null,\n };\n }\n}\n\nfunction resolveContainer(container: HTMLElement | string): HTMLElement {\n if (typeof container !== \"string\") return container;\n const el = document.getElementById(container);\n if (!el) {\n throw new Error(`PrismPlayer: no element found with id \"${container}\"`);\n }\n return el;\n}\n\nfunction createCanvas(id: string): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.id = id;\n canvas.className = \"bg-canvas\";\n canvas.setAttribute(\"aria-hidden\", \"true\");\n return canvas;\n}\n","// image-overlay.ts — draggable, resizable, collapsible PiP card.\n//\n// A pure-UI companion to PrismPlayer. The card has no opinion about\n// what's inside it — it just provides a positioned rectangle (the\n// `rect` property) that an embedder can sync to a slideshow's render\n// region, a video element, or anything else that wants to live in a\n// PiP. Drag from the body to move; drag from the 4 corner handles to\n// resize; click the × button to collapse to a thumbnail in the upper\n// right; click the thumbnail to expand again.\n//\n// Styling is class-based with a customisable prefix so the host page\n// can theme it. Defaults to `prism-overlay` with BEM-style modifiers\n// (`__handle`, `__collapse`); prism.scott.ai passes `slideshow-card` to\n// inherit its existing styles unchanged.\n\nexport interface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface OverlayState {\n rect: Rect;\n collapsed: boolean;\n}\n\nexport interface ImageOverlayOptions {\n /** Element to mount the card div in. Defaults to document.body. */\n mount?: HTMLElement;\n /** Initial card rect in CSS pixels. Defaults to a centered card sized\n * to ~58% of viewport width. */\n initialRect?: Rect;\n /** BEM class prefix for the card and its parts. Defaults to\n * \"prism-overlay\" — produces classes `.prism-overlay`,\n * `.prism-overlay__handle`, `.prism-overlay__collapse`. */\n className?: string;\n /** Thumbnail (collapsed) width in CSS pixels. Defaults to 200. */\n thumbWidth?: number;\n /** Min visible margin in pixels — drag is clamped so this much of\n * the card always stays in the viewport. Defaults to 60. */\n minVisible?: number;\n /** Min top edge (pixels from viewport top), e.g. to clear a status\n * bar. Defaults to 36. */\n topMargin?: number;\n /** Minimum card dimensions while resizing. Defaults to 80×45 (16:9). */\n minWidth?: number;\n minHeight?: number;\n /** Called whenever the rect or collapsed state changes (drag, resize,\n * collapse, expand, window resize re-anchor). */\n onChange?: (state: OverlayState) => void;\n}\n\ninterface DragState {\n startCard: Rect;\n startPointer: { x: number; y: number };\n handle: string | null;\n}\n\nexport class ImageOverlay {\n readonly element: HTMLElement;\n private readonly mount: HTMLElement;\n private readonly className: string;\n private readonly thumbW: number;\n private readonly thumbH: number;\n private readonly thumbMargin = 16;\n private readonly statusBarH: number;\n private readonly minVisible: number;\n private readonly minWidth: number;\n private readonly minHeight: number;\n private readonly onChange?: (state: OverlayState) => void;\n\n private _rect: Rect;\n private _collapsed = false;\n private uncollapsedRect: Rect | null = null;\n private drag: DragState | null = null;\n private destroyed = false;\n\n constructor(opts: ImageOverlayOptions = {}) {\n this.mount = opts.mount ?? document.body;\n this.className = opts.className ?? \"prism-overlay\";\n this.thumbW = opts.thumbWidth ?? 200;\n this.thumbH = this.thumbW * (9 / 16);\n this.minVisible = opts.minVisible ?? 60;\n this.statusBarH = opts.topMargin ?? 36;\n this.minWidth = opts.minWidth ?? 80;\n this.minHeight = opts.minHeight ?? 45;\n this.onChange = opts.onChange;\n this._rect = opts.initialRect ?? this.defaultRect();\n\n this.element = this.buildDom();\n this.mount.appendChild(this.element);\n this.applyRectToDom();\n\n window.addEventListener(\"resize\", this.onWindowResize, { passive: true });\n }\n\n /** Current card rect (live; do not mutate — call setRect instead). */\n get rect(): Rect {\n return { ...this._rect };\n }\n\n isCollapsed(): boolean {\n return this._collapsed;\n }\n\n /** Programmatically set the rect. Silent: does NOT call onChange.\n * Use this to push externally-managed state into the overlay\n * without echoing back into your own change handler. The internal\n * drag/resize handlers emit onChange themselves — that's the only\n * source of change notifications. */\n setRect(next: Rect): void {\n this._rect = { ...next };\n this.applyRectToDom();\n }\n\n /** Programmatically collapse. Silent — see setRect. */\n collapse(): void {\n if (this._collapsed) return;\n this.uncollapsedRect = { ...this._rect };\n this._rect = this.thumbRect();\n this._collapsed = true;\n this.element.setAttribute(\"data-collapsed\", \"\");\n this.applyRectToDom();\n }\n\n /** Programmatically expand. Silent — see setRect. */\n expand(): void {\n if (!this._collapsed) return;\n this._rect = this.uncollapsedRect ?? this.defaultRect();\n this._collapsed = false;\n this.element.removeAttribute(\"data-collapsed\");\n this.applyRectToDom();\n }\n\n show(): void {\n this.element.removeAttribute(\"data-hidden\");\n }\n\n hide(): void {\n this.element.setAttribute(\"data-hidden\", \"\");\n }\n\n /** Whether the card is currently visible (DOM attribute is the\n * source of truth, so this stays accurate if anyone toggles it\n * externally for testing). */\n isVisible(): boolean {\n return !this.element.hasAttribute(\"data-hidden\");\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n window.removeEventListener(\"resize\", this.onWindowResize);\n this.element.remove();\n }\n\n // ── internals ──────────────────────────────────────────────\n\n private buildDom(): HTMLElement {\n const card = document.createElement(\"div\");\n card.className = this.className;\n card.setAttribute(\"data-hidden\", \"\");\n\n const collapseBtn = document.createElement(\"button\");\n collapseBtn.type = \"button\";\n collapseBtn.className = `${this.className}__collapse`;\n collapseBtn.title = \"Collapse to thumbnail\";\n collapseBtn.setAttribute(\"aria-label\", \"Collapse\");\n const collapseIcon = document.createElement(\"span\");\n collapseIcon.setAttribute(\"aria-hidden\", \"true\");\n collapseIcon.textContent = \"−\";\n collapseBtn.appendChild(collapseIcon);\n collapseBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.collapse();\n this.emit();\n });\n card.appendChild(collapseBtn);\n\n for (const corner of [\"tl\", \"tr\", \"bl\", \"br\"] as const) {\n const handle = document.createElement(\"span\");\n handle.className = `${this.className}__handle ${this.className}__handle--${corner}`;\n handle.dataset.handle = corner;\n handle.setAttribute(\"aria-hidden\", \"true\");\n card.appendChild(handle);\n }\n\n card.addEventListener(\"pointerdown\", this.onPointerDown);\n card.addEventListener(\"pointermove\", this.onPointerMove);\n card.addEventListener(\"pointerup\", this.onPointerEnd);\n card.addEventListener(\"pointercancel\", this.onPointerEnd);\n\n return card;\n }\n\n private applyRectToDom(): void {\n const r = this._rect;\n this.element.style.left = `${r.x}px`;\n this.element.style.top = `${r.y}px`;\n this.element.style.width = `${r.w}px`;\n this.element.style.height = `${r.h}px`;\n }\n\n private emit(): void {\n this.onChange?.({ rect: { ...this._rect }, collapsed: this._collapsed });\n }\n\n private defaultRect(): Rect {\n // Centered, ~58% viewport width, 16:9 unless taller fits.\n const W = window.innerWidth;\n const H = window.innerHeight;\n const rw = W * 0.58;\n const rh = Math.min(rw * (9 / 16), H * 0.54);\n return { x: (W - rw) / 2, y: H * 0.16, w: rw, h: rh };\n }\n\n private thumbRect(): Rect {\n return {\n x: window.innerWidth - this.thumbW - this.thumbMargin,\n y: this.statusBarH + this.thumbMargin,\n w: this.thumbW,\n h: this.thumbH,\n };\n }\n\n private clampRect(r: Rect): Rect {\n return {\n x: Math.max(-(r.w - this.minVisible), Math.min(window.innerWidth - this.minVisible, r.x)),\n y: Math.max(this.statusBarH, Math.min(window.innerHeight - this.minVisible, r.y)),\n w: r.w,\n h: r.h,\n };\n }\n\n private readonly onWindowResize = (): void => {\n if (this._collapsed) {\n // Re-anchor the thumb to the new upper-right corner.\n this._rect = this.thumbRect();\n this.applyRectToDom();\n this.emit();\n }\n };\n\n private readonly onPointerDown = (e: PointerEvent): void => {\n const target = e.target as HTMLElement;\n if (target.closest(`.${this.className}__collapse`)) return; // collapse btn handles itself\n\n if (this._collapsed) {\n // Clicking the thumbnail expands.\n e.preventDefault();\n this.expand();\n this.emit();\n return;\n }\n\n e.preventDefault();\n this.element.setPointerCapture(e.pointerId);\n this.drag = {\n startCard: { ...this._rect },\n startPointer: { x: e.clientX, y: e.clientY },\n handle: target.classList.contains(`${this.className}__handle`)\n ? (target.dataset.handle ?? null)\n : null,\n };\n };\n\n private readonly onPointerMove = (e: PointerEvent): void => {\n if (!this.drag) return;\n e.preventDefault();\n const dx = e.clientX - this.drag.startPointer.x;\n const dy = e.clientY - this.drag.startPointer.y;\n const s = this.drag.startCard;\n const h = this.drag.handle;\n\n if (h === null) {\n // Whole-card drag.\n this._rect = this.clampRect({ x: s.x + dx, y: s.y + dy, w: s.w, h: s.h });\n } else {\n // Corner resize. l/r/t/b combinations resize from the matching edge.\n let nx = s.x, ny = s.y, nw = s.w, nh = s.h;\n if (h.includes(\"r\")) nw = Math.max(this.minWidth, s.w + dx);\n if (h.includes(\"l\")) { nw = Math.max(this.minWidth, s.w - dx); nx = s.x + (s.w - nw); }\n if (h.includes(\"b\")) nh = Math.max(this.minHeight, s.h + dy);\n if (h.includes(\"t\")) { nh = Math.max(this.minHeight, s.h - dy); ny = s.y + (s.h - nh); }\n this._rect = { x: nx, y: ny, w: nw, h: nh };\n }\n this.applyRectToDom();\n this.emit();\n };\n\n private readonly onPointerEnd = (e: PointerEvent): void => {\n if (!this.drag) return;\n try {\n this.element.releasePointerCapture(e.pointerId);\n } catch {\n /* ignore */\n }\n this.drag = null;\n };\n}\n\n"],"names":["milkdropConverter","milkdropConverterRaw","unwrap","mod","m","butterchurn","butterchurnRaw","butterchurnPresets","butterchurnPresetsRaw","createMilkdropBackground","audioCtx","canvas","silentSource","onReady","options","sizeTo","w","h","visualizer","presetMap","names","currentRaw","requested","requestedKey","findPresetKey","onResize","running","firstFrame","loop","pickNew","next","prettyPresetName","node","blendSeconds","name","key","url","res","milkText","converted","stem","_a","lower","ci","n","norm","s","target","raw","VERTEX_SHADER","FRAGMENT_PREAMBLE","FRAGMENT_EPILOGUE","createShadertoyBackground","glOrNull","gl","audioSource","analyser","fftBytes","audioTex","imageTex","dummyVao","currentProgram","currentUrl","currentName","liveSource","startTime","lastFrameTime","frame","mouseX","mouseY","mouseDown","compile","type","src","sh","log","numberLines","l","i","link","userSource","vs","fs","prog","frameLoop","now","iTime","iTimeDelta","loc","onPointerMove","e","onPointerDown","onPointerUp","source","img","resolve","reject","DEFAULT_HOLD_SECONDS","CROSSFADE_SECONDS","CANVAS_W","CANVAS_H","HeadlessSlideshow","urls","opts","__publicField","ctx","promises","loadImage","err","candidate","t","drawCovered","cur","nxt","srcAR","dstAR","sx","sy","sw","SCHEMA_VERSION","roleOf","nodesByRole","graph","role","makeMilkdropGraph","presetName","intent","registry","registryData","lookup","shortId","shortIds","shortIdToGraph","entry","entryToGraph","mainParams","mainType","nodes","GraphRuntime","blendSecondsOverride","lfNodes","presetUrl","_b","_c","_d","loaded","_e","imageUrl","_f","_g","SyntheticSignal","seeds","f","g","lfoHz","lfoD","bassEnd","midEnd","b","v","freq","gain","lfoDepth","osc","lfo","lfoGain","band","x01","y01","vel01","ramp","x","y","bassMul","trebleMul","midMul","voice","peak","intensity","masterPeak","tick","nextMs","ratio","PrismPlayer","container","resolveContainer","createCanvas","which","result","resolved","owned","track","fallback","stream","slideshow","video","el","id","ImageOverlay","dx","dy","nx","ny","nw","nh","card","collapseBtn","collapseIcon","corner","handle","r","W","H","rw","rh"],"mappings":"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"}
1
+ {"version":3,"file":"prism.mjs","sources":["../src/backends/milkdrop.ts","../src/backends/shadertoy.ts","../src/image-feed.ts","../src/types.ts","../src/registry.ts","../src/runtime.ts","../src/synth.ts","../src/player.ts","../src/image-overlay.ts"],"sourcesContent":["// milkdrop.ts — boot a butterchurn (Milkdrop) visualizer onto a caller-\n// supplied canvas. Picks a random preset on each load. Starts on a silent\n// source so the visual runs without audio; the caller can call connectAudio()\n// later when real audio is shared.\n\nimport butterchurnRaw from \"butterchurn\";\nimport butterchurnPresetsRaw from \"butterchurn-presets\";\nimport * as milkdropConverterRaw from \"milkdrop-preset-converter\";\nconst milkdropConverter = milkdropConverterRaw as unknown as {\n convertPreset: (text: string) => Promise<unknown>;\n};\n\ninterface VisualizerHandle {\n connectAudio(node: AudioNode): void;\n loadPreset(preset: unknown, blendTime?: number): void;\n setRendererSize(w: number, h: number): void;\n render(): void;\n}\ninterface ButterchurnAPI {\n createVisualizer(\n ctx: AudioContext,\n canvas: HTMLCanvasElement,\n opts: { width: number; height: number; pixelRatio?: number; textureRatio?: number },\n ): VisualizerHandle;\n}\ninterface PresetsAPI {\n getPresets(): Record<string, unknown>;\n}\n\nfunction unwrap<T>(mod: unknown): T {\n const m = mod as { default?: T };\n return (m && typeof m === \"object\" && \"default\" in m ? (m.default as T) : (mod as T));\n}\nconst butterchurn = unwrap<ButterchurnAPI>(butterchurnRaw);\nconst butterchurnPresets = unwrap<PresetsAPI>(butterchurnPresetsRaw);\n\nexport interface MilkdropBg {\n /** Pretty name of the currently-loaded preset (live; updates on rotation). */\n readonly presetName: string;\n /** Raw preset key (matches catalog `preset_id`). Stable for share / API use. */\n readonly currentPresetId: string;\n /** Swap the audio source — call when real tab-audio comes in. */\n connectAudio: (node: AudioNode) => void;\n /** Load a new random preset (with a blend transition). Returns the\n * pretty name of the newly-loaded preset. */\n loadRandom: (blendSeconds?: number) => string;\n /** Load a specific preset by its raw key (matches catalog `preset_id`).\n * Returns the pretty name on success; `null` if no preset with that key\n * exists in the bundled library. */\n loadByName: (name: string, blendSeconds?: number) => string | null;\n /** Load a .milk preset from a URL: fetch text → convertPreset → loadPreset.\n * Used for the 526 favorites and future contributor uploads which live\n * at public/presets/milkdrop/<slug>.milk. Returns the pretty name on\n * success; throws on fetch / parse error so callers can surface it. */\n loadFromUrl: (url: string, blendSeconds?: number) => Promise<string>;\n destroy: () => void;\n}\n\nexport interface MilkdropBgOptions {\n /** Preset key to load on cold open. Falls back to a random pick if the\n * name doesn't exist in the bundle. Lets the landing pin a curated\n * \"atelier\" default instead of getting random_$$$ Royal Mashup. */\n initialPresetName?: string;\n}\n\nexport function createMilkdropBackground(\n audioCtx: AudioContext,\n canvas: HTMLCanvasElement,\n silentSource: AudioNode,\n onReady?: () => void,\n options: MilkdropBgOptions = {},\n): MilkdropBg {\n // Size canvas to viewport (raw pixels — butterchurn does its own DPR math via pixelRatio).\n const sizeTo = (w: number, h: number) => {\n canvas.width = w;\n canvas.height = h;\n };\n sizeTo(window.innerWidth, window.innerHeight);\n\n const visualizer = butterchurn.createVisualizer(audioCtx, canvas, {\n width: canvas.width,\n height: canvas.height,\n pixelRatio: 1,\n textureRatio: 1,\n });\n visualizer.connectAudio(silentSource);\n\n // Pick the curated default if it exists in the bundle; otherwise random.\n const presetMap = butterchurnPresets.getPresets();\n const names = Object.keys(presetMap);\n let currentRaw: string;\n const requested = options.initialPresetName;\n const requestedKey = requested ? findPresetKey(names, requested) : null;\n if (requestedKey) {\n currentRaw = requestedKey;\n } else {\n currentRaw = names[Math.floor(Math.random() * names.length)];\n }\n visualizer.loadPreset(presetMap[currentRaw], 0);\n\n const onResize = (): void => {\n const w = window.innerWidth;\n const h = window.innerHeight;\n sizeTo(w, h);\n visualizer.setRendererSize(w, h);\n };\n window.addEventListener(\"resize\", onResize, { passive: true });\n\n let running = true;\n let firstFrame = true;\n const loop = (): void => {\n if (!running) return;\n visualizer.render();\n if (firstFrame) {\n firstFrame = false;\n // Defer slightly so the canvas actually has paint before consumers\n // remove their \"loading\" UI — gives a smoother transition.\n window.setTimeout(() => onReady?.(), 0);\n }\n requestAnimationFrame(loop);\n };\n requestAnimationFrame(loop);\n\n const pickNew = (): string => {\n // avoid immediately re-picking the same preset\n if (names.length < 2) return currentRaw;\n let next = currentRaw;\n while (next === currentRaw) next = names[Math.floor(Math.random() * names.length)];\n return next;\n };\n\n return {\n get presetName(): string { return prettyPresetName(currentRaw); },\n get currentPresetId(): string { return currentRaw; },\n connectAudio: (node) => visualizer.connectAudio(node),\n loadRandom: (blendSeconds = 2.5) => {\n const next = pickNew();\n visualizer.loadPreset(presetMap[next], blendSeconds);\n currentRaw = next;\n return prettyPresetName(next);\n },\n loadByName: (name, blendSeconds = 2.5) => {\n const key = findPresetKey(names, name);\n if (!key) return null;\n visualizer.loadPreset(presetMap[key], blendSeconds);\n currentRaw = key;\n return prettyPresetName(key);\n },\n loadFromUrl: async (url, blendSeconds = 2.5) => {\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`fetch ${url} → ${res.status} ${res.statusText}`);\n }\n const milkText = await res.text();\n const converted = await milkdropConverter.convertPreset(milkText);\n visualizer.loadPreset(converted, blendSeconds);\n // Display name is derived from the URL's last path segment.\n const stem = url.split(\"/\").pop()?.replace(/\\.milk$/, \"\") ?? url;\n currentRaw = stem;\n return prettyPresetName(stem);\n },\n destroy: () => {\n running = false;\n window.removeEventListener(\"resize\", onResize);\n },\n };\n}\n\n/** Match a preset key tolerantly: exact, case-insensitive, then\n * punctuation-stripped substring. Returns the raw key from the library\n * so loadPreset gets the exact object it expects. */\nfunction findPresetKey(names: string[], requested: string): string | null {\n if (names.includes(requested)) return requested;\n const lower = requested.toLowerCase();\n const ci = names.find((n) => n.toLowerCase() === lower);\n if (ci) return ci;\n const norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n const target = norm(requested);\n return names.find((n) => norm(n) === target) ?? null;\n}\n\n/** Strip leading punctuation, kebab-ish format, lowercase. */\nfunction prettyPresetName(raw: string): string {\n return raw\n .replace(/^[\\W\\d]+/, \"\")\n .replace(/[_]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .toLowerCase();\n}\n","// shadertoy.ts — render a Shadertoy-style GLSL fragment shader to a\n// caller-supplied canvas. Mirrors backends/milkdrop.ts's role: takes audio\n// in, produces a live visual. GraphRuntime swaps between the two based on\n// whether the graph has a lf.milkdrop or lf.shadertoy node.\n//\n// Shadertoy convention: the user shader defines\n// void mainImage(out vec4 fragColor, in vec2 fragCoord)\n// We wrap that with our standard uniforms:\n// iTime, iResolution, iMouse, iChannel0 (audio FFT 256x1)\n\nconst VERTEX_SHADER = `#version 300 es\nprecision highp float;\nout vec2 v_uv;\nvoid main() {\n // Fullscreen triangle (no buffer needed; gl_VertexID drives positions).\n vec2 p = vec2((gl_VertexID & 1) * 4 - 1, (gl_VertexID & 2) * 2 - 1);\n v_uv = p * 0.5 + 0.5;\n gl_Position = vec4(p, 0.0, 1.0);\n}\n`;\n\nconst FRAGMENT_PREAMBLE = `#version 300 es\nprecision highp float;\nprecision highp int;\n\nuniform float iTime;\nuniform float iTimeDelta;\nuniform int iFrame;\nuniform vec3 iResolution;\nuniform vec4 iMouse;\nuniform sampler2D iChannel0; // audio FFT (256x1, R8)\nuniform sampler2D iChannel1; // image input (default_image or bound source)\nin vec2 v_uv;\nout vec4 outColor;\n\n`;\n\nconst FRAGMENT_EPILOGUE = `\n\nvoid main() {\n vec4 c;\n mainImage(c, gl_FragCoord.xy);\n outColor = vec4(c.rgb, 1.0);\n}\n`;\n\nexport interface ShadertoyBg {\n /** Pretty name from the source URL, useful for SKILL readout. */\n readonly presetName: string;\n /** URL of the currently-loaded shader. */\n readonly currentUrl: string | null;\n /** Connect a Web Audio source — Shadertoy's iChannel0 sees its FFT. */\n connectAudio: (node: AudioNode) => void;\n /** Fetch + compile + render a new shader. Resolves once first frame\n * paints. Throws on compile error. */\n loadFromUrl: (url: string) => Promise<string>;\n /** Bind an image URL as iChannel1. Resolves once decoded + uploaded.\n * Pass null to clear (resets to the 1x1 placeholder). */\n bindImage: (url: string | null) => Promise<void>;\n /** Pipe a live source (e.g. a slideshow's canvas) into iChannel1 — its\n * contents are uploaded to the GPU every frame, so when the slideshow\n * advances to the next image, the shader sees it immediately. Pass\n * null to disable + revert to whatever was last bound via bindImage. */\n setLiveSource: (source: HTMLCanvasElement | HTMLVideoElement | null) => void;\n /** Pause the render loop + free GL resources. */\n destroy: () => void;\n}\n\nexport function createShadertoyBackground(\n audioCtx: AudioContext,\n canvas: HTMLCanvasElement,\n silentSource: AudioNode,\n): ShadertoyBg {\n const glOrNull = canvas.getContext(\"webgl2\", {\n alpha: false,\n antialias: false,\n preserveDrawingBuffer: false,\n powerPreference: \"high-performance\",\n });\n if (!glOrNull) throw new Error(\"WebGL2 not available\");\n const gl: WebGL2RenderingContext = glOrNull;\n\n const sizeTo = (w: number, h: number): void => {\n canvas.width = w;\n canvas.height = h;\n gl.viewport(0, 0, w, h);\n };\n sizeTo(window.innerWidth, window.innerHeight);\n\n // Audio analyser → 256-bin FFT texture for iChannel0.\n let audioSource: AudioNode = silentSource;\n const analyser = audioCtx.createAnalyser();\n analyser.fftSize = 512; // 256 frequency bins\n audioSource.connect(analyser);\n const fftBytes = new Uint8Array(256);\n const audioTex = gl.createTexture()!;\n gl.bindTexture(gl.TEXTURE_2D, audioTex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n // WebGL2 single-channel texture: R8 internal + RED format. LUMINANCE\n // is deprecated and unreliable in WebGL2 / GLSL 300 es shaders.\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, 256, 1, 0, gl.RED, gl.UNSIGNED_BYTE, null);\n\n // iChannel1: image texture. Starts as 1x1 dim-gray placeholder so the\n // shader has something to sample before bindImage resolves.\n const imageTex = gl.createTexture()!;\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([32, 32, 38, 255]));\n\n const dummyVao = gl.createVertexArray()!; // needed for vertexAttrib-less rendering\n\n let currentProgram: WebGLProgram | null = null;\n let currentUrl: string | null = null;\n let currentName = \"—\";\n let liveSource: HTMLCanvasElement | HTMLVideoElement | null = null;\n const startTime = performance.now();\n let lastFrameTime = startTime;\n let frame = 0;\n let mouseX = 0;\n let mouseY = 0;\n let mouseDown = false;\n let running = true;\n\n function compile(type: GLenum, src: string): WebGLShader {\n const sh = gl.createShader(type)!;\n gl.shaderSource(sh, src);\n gl.compileShader(sh);\n if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(sh) ?? \"(no log)\";\n gl.deleteShader(sh);\n throw new Error(`GLSL compile error:\\n${log}\\n--- source ---\\n${numberLines(src)}`);\n }\n return sh;\n }\n\n function numberLines(src: string): string {\n return src.split(\"\\n\").map((l, i) => `${String(i + 1).padStart(3)}: ${l}`).join(\"\\n\");\n }\n\n function link(userSource: string): WebGLProgram {\n const vs = compile(gl.VERTEX_SHADER, VERTEX_SHADER);\n const fs = compile(gl.FRAGMENT_SHADER, FRAGMENT_PREAMBLE + userSource + FRAGMENT_EPILOGUE);\n const prog = gl.createProgram()!;\n gl.attachShader(prog, vs);\n gl.attachShader(prog, fs);\n gl.linkProgram(prog);\n if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(prog) ?? \"(no log)\";\n gl.deleteShader(vs); gl.deleteShader(fs); gl.deleteProgram(prog);\n throw new Error(`Shader link error: ${log}`);\n }\n gl.deleteShader(vs); gl.deleteShader(fs);\n return prog;\n }\n\n function frameLoop(): void {\n if (!running) return;\n const now = performance.now();\n const iTime = (now - startTime) * 0.001;\n const iTimeDelta = (now - lastFrameTime) * 0.001;\n lastFrameTime = now;\n frame++;\n\n analyser.getByteFrequencyData(fftBytes);\n gl.bindTexture(gl.TEXTURE_2D, audioTex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, 256, 1, 0, gl.RED, gl.UNSIGNED_BYTE, fftBytes);\n\n // If a live source (slideshow canvas, video element, etc.) is bound,\n // upload its current contents to iChannel1 every frame. When the\n // upstream advances to a new image, the shader sees it next frame.\n if (liveSource &&\n (liveSource instanceof HTMLCanvasElement\n ? liveSource.width > 0 && liveSource.height > 0\n : liveSource.readyState >= 2)) {\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n try {\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, liveSource);\n } catch {\n // tainted canvas or zero-size — skip this frame\n }\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n }\n\n if (currentProgram) {\n gl.useProgram(currentProgram);\n const loc = (name: string): WebGLUniformLocation | null =>\n gl.getUniformLocation(currentProgram!, name);\n gl.uniform1f(loc(\"iTime\"), iTime);\n gl.uniform1f(loc(\"iTimeDelta\"), iTimeDelta);\n gl.uniform1i(loc(\"iFrame\"), frame);\n gl.uniform3f(loc(\"iResolution\"), canvas.width, canvas.height, 1.0);\n gl.uniform4f(loc(\"iMouse\"), mouseX, canvas.height - mouseY, mouseDown ? 1 : 0, 0);\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, audioTex);\n gl.uniform1i(loc(\"iChannel0\"), 0);\n gl.activeTexture(gl.TEXTURE1);\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.uniform1i(loc(\"iChannel1\"), 1);\n gl.bindVertexArray(dummyVao);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n } else {\n gl.clearColor(0, 0, 0, 1);\n gl.clear(gl.COLOR_BUFFER_BIT);\n }\n\n requestAnimationFrame(frameLoop);\n }\n requestAnimationFrame(frameLoop);\n\n const onResize = (): void => sizeTo(window.innerWidth, window.innerHeight);\n const onPointerMove = (e: PointerEvent): void => { mouseX = e.clientX; mouseY = e.clientY; };\n const onPointerDown = (): void => { mouseDown = true; };\n const onPointerUp = (): void => { mouseDown = false; };\n window.addEventListener(\"resize\", onResize, { passive: true });\n window.addEventListener(\"pointermove\", onPointerMove, { passive: true });\n window.addEventListener(\"pointerdown\", onPointerDown, { passive: true });\n window.addEventListener(\"pointerup\", onPointerUp, { passive: true });\n\n return {\n get presetName(): string { return currentName; },\n get currentUrl(): string | null { return currentUrl; },\n connectAudio: (node) => {\n audioSource.disconnect();\n audioSource = node;\n audioSource.connect(analyser);\n },\n loadFromUrl: async (url) => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`fetch ${url} → ${res.status}`);\n const src = await res.text();\n const prog = link(src);\n if (currentProgram) gl.deleteProgram(currentProgram);\n currentProgram = prog;\n currentUrl = url;\n currentName = url.split(\"/\").pop()?.replace(/\\.glsl$/, \"\") ?? url;\n return currentName;\n },\n setLiveSource: (source) => {\n liveSource = source;\n },\n bindImage: async (url) => {\n if (!url) {\n // Reset to placeholder\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([32, 32, 38, 255]));\n return;\n }\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error(`image load failed: ${url}`));\n img.src = url;\n });\n gl.bindTexture(gl.TEXTURE_2D, imageTex);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n // Use mipmaps if power-of-2; otherwise just LINEAR.\n const pow2 = (img.naturalWidth & (img.naturalWidth - 1)) === 0\n && (img.naturalHeight & (img.naturalHeight - 1)) === 0;\n if (pow2) {\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n } else {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n }\n },\n destroy: () => {\n running = false;\n window.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerdown\", onPointerDown);\n window.removeEventListener(\"pointerup\", onPointerUp);\n if (currentProgram) gl.deleteProgram(currentProgram);\n gl.deleteTexture(audioTex);\n gl.deleteTexture(imageTex);\n gl.deleteVertexArray(dummyVao);\n },\n };\n}\n","// image-feed.ts — headless slideshow.\n//\n// Owns a single offscreen canvas, preloads a list of image URLs, and\n// crossfades between them on a timer. Shaders read the canvas via the\n// player's setLiveSource() — same plumbing as a live video element,\n// just sourced from a slideshow instead of a camera.\n//\n// The site's src/landing/slideshow.ts is a richer surface (drag-resize\n// card, progress bar, melt transitions); this is the minimum the npm\n// package needs so an OSS consumer can pass image: [\"a.jpg\", \"b.jpg\"]\n// and get a feed for the shader without writing canvas plumbing.\n\nconst DEFAULT_HOLD_SECONDS = 6;\nconst CROSSFADE_SECONDS = 1.0;\nconst CANVAS_W = 1280;\nconst CANVAS_H = 720;\n\nexport interface SlideshowOptions {\n /** How long (seconds) each image is held on-screen before the\n * crossfade to the next begins. Defaults to 6. */\n holdSeconds?: number;\n}\n\nexport class HeadlessSlideshow {\n /** The output canvas — bind this to the shader via setLiveSource. */\n readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly urls: string[];\n private readonly holdMs: number;\n private readonly images: Array<HTMLImageElement | null>;\n private currentIndex = 0;\n private nextIndex = 1;\n /** Wall-clock time the current crossfade started, or null if we're\n * in the steady-hold portion of the cycle. */\n private crossfadeStartMs: number | null = null;\n private holdTimer: number | null = null;\n private rafHandle: number | null = null;\n private destroyed = false;\n\n constructor(urls: string[], opts: SlideshowOptions = {}) {\n if (urls.length === 0) {\n throw new Error(\"HeadlessSlideshow: urls list cannot be empty\");\n }\n this.urls = urls;\n this.holdMs = Math.max(0.5, opts.holdSeconds ?? DEFAULT_HOLD_SECONDS) * 1000;\n this.canvas = document.createElement(\"canvas\");\n this.canvas.width = CANVAS_W;\n this.canvas.height = CANVAS_H;\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"HeadlessSlideshow: 2D canvas context unavailable\");\n this.ctx = ctx;\n this.images = urls.map(() => null);\n void this.preload();\n }\n\n destroy(): void {\n this.destroyed = true;\n if (this.holdTimer != null) clearTimeout(this.holdTimer);\n if (this.rafHandle != null) cancelAnimationFrame(this.rafHandle);\n }\n\n private async preload(): Promise<void> {\n // Load all images in parallel, but start rendering + cycling as\n // soon as the first one's ready so the shader isn't stuck on a\n // black frame waiting on slow tails.\n const promises = this.urls.map((url, i) =>\n loadImage(url).then(\n (img) => {\n if (this.destroyed) return;\n this.images[i] = img;\n if (i === 0) this.start();\n },\n (err: Error) => {\n // Failure to load one image shouldn't kill the whole feed —\n // leave that slot as null; the render loop skips it.\n console.warn(`[/prism] image-feed: ${url} → ${err.message}`);\n },\n ),\n );\n await Promise.allSettled(promises);\n }\n\n private start(): void {\n if (this.destroyed) return;\n this.drawFrame();\n this.scheduleNext();\n }\n\n private scheduleNext(): void {\n if (this.destroyed) return;\n this.holdTimer = window.setTimeout(() => this.advance(), this.holdMs);\n }\n\n private advance(): void {\n if (this.destroyed) return;\n // Pick the next loaded image, skipping any that failed to load.\n let candidate = (this.currentIndex + 1) % this.urls.length;\n for (let i = 0; i < this.urls.length; i++) {\n if (this.images[candidate]) break;\n candidate = (candidate + 1) % this.urls.length;\n }\n if (candidate === this.currentIndex) {\n // Only one loaded image — nothing to cross to, just re-arm.\n this.scheduleNext();\n return;\n }\n this.nextIndex = candidate;\n this.crossfadeStartMs = performance.now();\n this.rafHandle = requestAnimationFrame(() => this.tickCrossfade());\n }\n\n private tickCrossfade(): void {\n if (this.destroyed) return;\n if (this.crossfadeStartMs == null) return;\n const t = (performance.now() - this.crossfadeStartMs) / 1000 / CROSSFADE_SECONDS;\n if (t >= 1) {\n // Crossfade finished — commit and queue the next hold.\n this.currentIndex = this.nextIndex;\n this.crossfadeStartMs = null;\n this.drawFrame();\n this.scheduleNext();\n return;\n }\n this.drawCrossfade(t);\n this.rafHandle = requestAnimationFrame(() => this.tickCrossfade());\n }\n\n /** Draw the current image fully opaque. */\n private drawFrame(): void {\n const img = this.images[this.currentIndex];\n if (!img) return;\n this.ctx.fillStyle = \"#000\";\n this.ctx.fillRect(0, 0, CANVAS_W, CANVAS_H);\n drawCovered(this.ctx, img, CANVAS_W, CANVAS_H);\n }\n\n /** Draw the current image, then the next one at alpha=t on top. */\n private drawCrossfade(t: number): void {\n const cur = this.images[this.currentIndex];\n const nxt = this.images[this.nextIndex];\n this.ctx.fillStyle = \"#000\";\n this.ctx.fillRect(0, 0, CANVAS_W, CANVAS_H);\n if (cur) drawCovered(this.ctx, cur, CANVAS_W, CANVAS_H);\n if (nxt) {\n this.ctx.save();\n this.ctx.globalAlpha = t;\n drawCovered(this.ctx, nxt, CANVAS_W, CANVAS_H);\n this.ctx.restore();\n }\n }\n}\n\nfunction loadImage(url: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`image load failed: ${url}`));\n img.src = url;\n });\n}\n\n/** Draw `img` covering the (w,h) box — like CSS object-fit: cover.\n * Cropping rather than letterboxing keeps the slideshow visually full\n * in the shader's iChannel1, which is usually being sampled by uv\n * coordinates that assume the whole texture is content. */\nfunction drawCovered(\n ctx: CanvasRenderingContext2D,\n img: HTMLImageElement,\n w: number,\n h: number,\n): void {\n const srcAR = img.naturalWidth / img.naturalHeight;\n const dstAR = w / h;\n let sx = 0, sy = 0, sw = img.naturalWidth, sh = img.naturalHeight;\n if (srcAR > dstAR) {\n // image is wider — crop horizontally\n sw = img.naturalHeight * dstAR;\n sx = (img.naturalWidth - sw) / 2;\n } else {\n sh = img.naturalWidth / dstAR;\n sy = (img.naturalHeight - sh) / 2;\n }\n ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h);\n}\n","// prism.graph/0.1 — node-graph schema for visualizations.\n//\n// Every Prism visualization is a JSON document of this shape. Nodes fall\n// into five role-tagged categories that mirror the fundamental abstraction:\n//\n// signal source → signal xform → light field generator → lf operator → sink\n//\n// In English: Prism computes light fields from signals.\n//\n// M1 only exercises the minimal graph (audio → milkdrop → display); the\n// schema is intentionally roomy so M8/M9 can add more generators and\n// compositor ops without rewriting it.\n\nexport const SCHEMA_VERSION = \"prism.graph/0.1\" as const;\n\nexport type NodeRole = \"signal\" | \"xform\" | \"lf\" | \"op\" | \"sink\";\n\nexport type NodeType =\n // signal sources — produce a streaming signal\n | \"signal.audio\"\n | \"signal.cursor\"\n | \"signal.heartbeat\"\n | \"signal.synth\"\n // signal transformers — signal → signal\n | \"xform.gain\"\n | \"xform.beat\"\n // light field generators — produce a frame from signals\n | \"lf.milkdrop\"\n | \"lf.shadertoy\"\n | \"lf.isf\"\n // light field operators — frame(s) → frame\n | \"op.blend\"\n | \"op.displace\"\n | \"op.feedback\"\n // sinks — terminate the graph\n | \"sink.display\"\n | \"sink.recorder\";\n\nexport type NodeParam =\n | string\n | number\n | boolean\n | string[]\n | number[]\n | Record<string, string | number | boolean>;\n\n/** A single node in the graph. Inputs reference upstream node outputs by\n * the string \"<node_id>.<output_name>\". The runtime resolves these. */\nexport interface NodeDef {\n type: NodeType;\n params?: Record<string, NodeParam>;\n inputs?: Record<string, string>;\n}\n\nexport interface PrismGraph {\n schema: typeof SCHEMA_VERSION;\n /** Stable id — enables share-by-URL in M6. */\n id: string;\n /** Human-readable summary of what the graph does. Shown in the SKILL\n * readout; lets the AI explain its choice. */\n intent: string;\n nodes: Record<string, NodeDef>;\n /** Node id of the terminal sink. */\n output: string;\n}\n\n/** Extract the role prefix from a node type. */\nexport function roleOf(type: NodeType): NodeRole {\n return type.split(\".\")[0] as NodeRole;\n}\n\n/** Iterate nodes of a given role. */\nexport function nodesByRole(graph: PrismGraph, role: NodeRole): Array<[string, NodeDef]> {\n return Object.entries(graph.nodes).filter(([, n]) => roleOf(n.type) === role);\n}\n\n/** Build the M1 minimal graph: audio_in → lf.milkdrop(preset) → sink.display. */\nexport function makeMilkdropGraph(\n presetName: string,\n intent: string,\n blendSeconds = 2.5,\n): PrismGraph {\n return {\n schema: SCHEMA_VERSION,\n id: `g_${Math.random().toString(36).slice(2, 10)}`,\n intent,\n nodes: {\n audio_in: { type: \"signal.audio\", params: {} },\n main: {\n type: \"lf.milkdrop\",\n params: { preset_name: presetName, blend_seconds: blendSeconds },\n inputs: { audio: \"audio_in.signal\" },\n },\n out: { type: \"sink.display\", inputs: { frame: \"main.frame\" } },\n },\n output: \"out\",\n };\n}\n","// registry.ts — share-token lookup for catalog content.\n//\n// Every catalog entry on prism.scott.ai has a 6-char base62 token (the\n// short_id). Consumers pass one to `player.load(...)` or via the URL\n// param `?g=<short_id>` and the player resolves it to a PrismGraph\n// offline — no network call. The map is generated from the catalog at\n// build-index time; see scripts/prism/commands/build-index.ts.\n\nimport registryData from \"./registry.generated.json\";\nimport { SCHEMA_VERSION, type NodeDef, type NodeType, type PrismGraph } from \"./types\";\n\nexport interface RegistryEntry {\n name: string;\n source_type: \"milkdrop\" | \"shadertoy\" | \"isf\" | \"wgsl\";\n source_loader: \"url\" | \"npm-butterchurn-presets\";\n source_url?: string;\n source_ref?: string;\n default_image?: string;\n}\n\nconst registry = registryData as Record<string, RegistryEntry>;\n\n/** Resolve a 6-char short_id to a registry entry. Returns null when\n * the id isn't known — callers decide whether to fall back, error,\n * or just keep the synthetic cold-open running. */\nexport function lookup(shortId: string): RegistryEntry | null {\n return registry[shortId] ?? null;\n}\n\n/** All known short_ids. Mostly useful for tests + the rotation pool. */\nexport function shortIds(): string[] {\n return Object.keys(registry);\n}\n\n/** Build a minimal PrismGraph that plays the entry behind `shortId`.\n * Returns null when the id isn't in the registry. */\nexport function shortIdToGraph(shortId: string): PrismGraph | null {\n const entry = lookup(shortId);\n if (!entry) return null;\n return entryToGraph(shortId, entry);\n}\n\nfunction entryToGraph(shortId: string, entry: RegistryEntry): PrismGraph {\n const mainParams: Record<string, string> = {};\n let mainType: NodeType;\n if (entry.source_type === \"shadertoy\") {\n mainType = \"lf.shadertoy\";\n if (entry.source_url) mainParams.shader_url = entry.source_url;\n if (entry.default_image) mainParams.image_url = entry.default_image;\n } else {\n mainType = \"lf.milkdrop\";\n if (entry.source_loader === \"url\" && entry.source_url) {\n mainParams.preset_url = entry.source_url;\n } else if (entry.source_ref) {\n mainParams.preset_name = entry.source_ref;\n }\n }\n const nodes: Record<string, NodeDef> = {\n audio: { type: \"signal.audio\" },\n main: { type: mainType, params: mainParams, inputs: { audio: \"audio.signal\" } },\n screen: { type: \"sink.display\", inputs: { frame: \"main.frame\" } },\n };\n return {\n schema: SCHEMA_VERSION,\n id: `g:${shortId}`,\n intent: entry.name,\n nodes,\n output: \"screen\",\n };\n}\n","// runtime.ts — graph executor for prism.graph/0.1.\n//\n// Walks a PrismGraph, finds the light-field generator node, dispatches\n// to the appropriate backend (milkdrop / shadertoy / ...). Future M9\n// will add op.* nodes (blend / displace) and a real compositor; today\n// it's single-backend-at-a-time and swaps as needed.\n//\n// The runtime owns visibility of the two background canvases — only the\n// active backend's canvas is shown, the other is hidden. URL-loaded\n// presets (favorites + shadertoys) load asynchronously; runtime.apply\n// returns synchronously after kicking off the load and updating state.\n\nimport type { MilkdropBg } from \"./backends/milkdrop\";\nimport type { ShadertoyBg } from \"./backends/shadertoy\";\nimport { nodesByRole, type PrismGraph } from \"./types\";\n\nexport interface RuntimeContext {\n milkdrop: MilkdropBg;\n shadertoy: ShadertoyBg;\n /** Called to toggle which backend's canvas is visible.\n * Implementations set CSS opacity/display on the two canvases. */\n setActiveBackend: (which: \"milkdrop\" | \"shadertoy\") => void;\n}\n\nexport interface ApplyResult {\n ok: boolean;\n error?: string;\n /** Display name of the preset that was loaded, if any. */\n presetName?: string;\n /** Which backend handled this graph. */\n backend?: \"milkdrop\" | \"shadertoy\";\n}\n\nexport class GraphRuntime {\n private active: PrismGraph | null = null;\n\n constructor(private readonly ctx: RuntimeContext) {}\n\n apply(graph: PrismGraph, blendSecondsOverride?: number): ApplyResult {\n const lfNodes = nodesByRole(graph, \"lf\");\n if (lfNodes.length === 0) {\n return { ok: false, error: \"graph has no light-field generator\" };\n }\n const [, node] = lfNodes[0];\n\n if (node.type === \"lf.milkdrop\") {\n const presetName = node.params?.preset_name;\n const presetUrl = node.params?.preset_url;\n const blendSeconds =\n blendSecondsOverride ??\n (typeof node.params?.blend_seconds === \"number\" ? node.params.blend_seconds : 2.5);\n this.ctx.setActiveBackend(\"milkdrop\");\n // URL takes precedence (favorites). Fall back to name (npm bundle / legacy).\n if (typeof presetUrl === \"string\") {\n void this.ctx.milkdrop.loadFromUrl(presetUrl, blendSeconds).catch((err: Error) => {\n console.warn(\"[runtime] milkdrop loadFromUrl failed:\", err.message);\n });\n this.active = graph;\n const name = presetUrl.split(\"/\").pop()?.replace(/\\.milk$/, \"\") ?? presetUrl;\n return { ok: true, presetName: name, backend: \"milkdrop\" };\n }\n if (typeof presetName !== \"string\") {\n return { ok: false, error: \"lf.milkdrop missing preset_name or preset_url\" };\n }\n const loaded = this.ctx.milkdrop.loadByName(presetName, blendSeconds);\n if (loaded === null) {\n return { ok: false, error: `preset not found: ${presetName}` };\n }\n this.active = graph;\n return { ok: true, presetName: loaded, backend: \"milkdrop\" };\n }\n\n if (node.type === \"lf.shadertoy\") {\n const url = node.params?.shader_url;\n if (typeof url !== \"string\") {\n return { ok: false, error: \"lf.shadertoy missing shader_url\" };\n }\n const imageUrl = node.params?.image_url;\n this.ctx.setActiveBackend(\"shadertoy\");\n void this.ctx.shadertoy.loadFromUrl(url).catch((err: Error) => {\n console.warn(\"[runtime] shadertoy loadFromUrl failed:\", err.message);\n });\n // Bind the entry's default image to iChannel1 if specified. The\n // shader has a 1x1 grey placeholder until this resolves.\n void this.ctx.shadertoy.bindImage(typeof imageUrl === \"string\" ? imageUrl : null)\n .catch((err: Error) => {\n console.warn(\"[runtime] shadertoy bindImage failed:\", err.message);\n });\n this.active = graph;\n const name = url.split(\"/\").pop()?.replace(/\\.glsl$/, \"\") ?? url;\n return { ok: true, presetName: name, backend: \"shadertoy\" };\n }\n\n return { ok: false, error: `unsupported lf type: ${node.type}` };\n }\n\n get current(): PrismGraph | null {\n return this.active;\n }\n}\n","// synthetic-signal.ts — silent \"fractal\" audio driver.\n// Generates a pink-noise-like spectrum (octave-spaced oscillators, amplitude\n// ∝ 1/√f) plus periodic beat envelopes and slow frequency mutations. The\n// signal is read by butterchurn's analyser to keep the Milkdrop preset\n// animated while no real audio is shared. Output never reaches ctx.destination,\n// so nothing is audible.\n\ntype Band = \"bass\" | \"mid\" | \"treble\";\ntype Voice = {\n base: number;\n baseGain: number;\n band: Band;\n osc: OscillatorNode;\n lfo: OscillatorNode;\n lfoGain: GainNode;\n gain: GainNode;\n};\n\nexport class SyntheticSignal {\n private readonly ctx: AudioContext;\n private readonly output: GainNode;\n private readonly voices: Voice[] = [];\n private readonly analyser: AnalyserNode;\n private readonly fft: Uint8Array;\n private beatTimer: number | null = null;\n private mutationTimer: number | null = null;\n private stopped = false;\n private lastCursorBeatAt = 0;\n\n constructor(ctx: AudioContext) {\n this.ctx = ctx;\n this.output = ctx.createGain();\n this.output.gain.value = 0.65;\n\n // Octave-spaced voices producing a 1/√f spectrum — convincingly \"musical\"\n // to the analyser without being recognisable as anything in particular.\n // freqHz, type, baseGain, lfoHz, lfoDepth\n const seeds: Array<[number, OscillatorType, number, number, number]> = [\n [ 50, \"sine\", 0.55, 0.06, 18 ],\n [ 110, \"sine\", 0.40, 0.09, 24 ],\n [ 230, \"triangle\", 0.28, 0.12, 60 ],\n [ 460, \"sawtooth\", 0.18, 0.17, 140 ],\n [ 950, \"triangle\", 0.12, 0.21, 280 ],\n [ 1900, \"sine\", 0.08, 0.27, 520 ],\n [ 3900, \"triangle\", 0.05, 0.33, 1100 ],\n ];\n for (const [f, type, g, lfoHz, lfoD] of seeds) this.addVoice(f, type, g, lfoHz, lfoD);\n\n // Internal analyser so callers can read our energy without colliding with\n // butterchurn's own analyser.\n this.analyser = ctx.createAnalyser();\n this.analyser.fftSize = 256;\n this.analyser.smoothingTimeConstant = 0.78;\n this.output.connect(this.analyser);\n this.fft = new Uint8Array(this.analyser.frequencyBinCount);\n\n this.scheduleBeats();\n this.scheduleMutations();\n }\n\n /** The node to feed into a consumer (e.g. butterchurn.connectAudio). */\n getOutput(): AudioNode {\n return this.output;\n }\n\n /** Returns a smoothed 0..1 energy reading from our own analyser. */\n readEnergy(): number {\n if (this.stopped) return 0;\n this.analyser.getByteFrequencyData(this.fft as unknown as Uint8Array<ArrayBuffer>);\n let s = 0;\n for (let i = 0; i < this.fft.length; i++) s += this.fft[i];\n return s / 255 / this.fft.length;\n }\n\n /** Returns smoothed bass/mid/treble bands (0..1 each). */\n readBands(): { bass: number; mid: number; treble: number } {\n if (this.stopped) return { bass: 0, mid: 0, treble: 0 };\n this.analyser.getByteFrequencyData(this.fft as unknown as Uint8Array<ArrayBuffer>);\n const n = this.fft.length;\n const bassEnd = Math.floor(n * 0.10);\n const midEnd = Math.floor(n * 0.45);\n let b = 0, m = 0, t = 0;\n for (let i = 0; i < n; i++) {\n const v = this.fft[i] / 255;\n if (i < bassEnd) b += v;\n else if (i < midEnd) m += v;\n else t += v;\n }\n return {\n bass: b / Math.max(1, bassEnd),\n mid: m / Math.max(1, midEnd - bassEnd),\n treble: t / Math.max(1, n - midEnd),\n };\n }\n\n stop(): void {\n if (this.stopped) return;\n this.stopped = true;\n if (this.beatTimer != null) clearTimeout(this.beatTimer);\n if (this.mutationTimer != null) clearTimeout(this.mutationTimer);\n for (const v of this.voices) {\n try { v.osc.stop(); v.lfo.stop(); } catch { /* ignore */ }\n try { v.osc.disconnect(); v.lfo.disconnect(); v.lfoGain.disconnect(); v.gain.disconnect(); }\n catch { /* ignore */ }\n }\n try { this.output.disconnect(); } catch { /* ignore */ }\n try { this.analyser.disconnect(); } catch { /* ignore */ }\n }\n\n private addVoice(\n freq: number,\n type: OscillatorType,\n gain: number,\n lfoHz: number,\n lfoDepth: number,\n ): void {\n const ctx = this.ctx;\n const osc = ctx.createOscillator();\n osc.frequency.value = freq;\n osc.type = type;\n const g = ctx.createGain();\n g.gain.value = gain;\n osc.connect(g).connect(this.output);\n\n const lfo = ctx.createOscillator();\n lfo.frequency.value = lfoHz;\n lfo.type = \"sine\";\n const lfoGain = ctx.createGain();\n lfoGain.gain.value = lfoDepth;\n lfo.connect(lfoGain).connect(osc.frequency);\n\n lfo.start();\n osc.start();\n const band: Band = freq < 200 ? \"bass\" : freq < 1100 ? \"mid\" : \"treble\";\n this.voices.push({ base: freq, baseGain: gain, band, osc, lfo, lfoGain, gain: g });\n }\n\n /**\n * Modulate the synth from the cursor. Position controls the bass/treble\n * balance (top-right = bright/airy; bottom-left = bassy/warm); velocity\n * triggers a beat envelope on the master gain. The cursor becomes the\n * instrument driving the milkdrop preset.\n *\n * @param x01 cursor X normalised to 0..1 (left .. right)\n * @param y01 cursor Y normalised to 0..1 (top .. bottom)\n * @param vel01 cursor speed normalised to 0..1\n */\n setCursorModulation(x01: number, y01: number, vel01: number): void {\n if (this.stopped) return;\n const now = this.ctx.currentTime;\n const ramp = 0.12;\n const x = Math.max(0, Math.min(1, x01));\n const y = Math.max(0, Math.min(1, y01));\n const v = Math.max(0, Math.min(1, vel01));\n\n // bass: bottom of screen = louder\n const bassMul = 0.55 + (1 - y) * 0.90 + v * 0.15;\n // treble: right of screen = brighter\n const trebleMul = 0.55 + x * 0.90 + v * 0.20;\n // mid: rises with velocity (gives \"presence\" to motion)\n const midMul = 0.7 + v * 0.7;\n\n for (const voice of this.voices) {\n const target =\n voice.band === \"bass\" ? voice.baseGain * bassMul :\n voice.band === \"treble\" ? voice.baseGain * trebleMul :\n voice.baseGain * midMul;\n voice.gain.gain.cancelScheduledValues(now);\n voice.gain.gain.setValueAtTime(voice.gain.gain.value, now);\n voice.gain.gain.linearRampToValueAtTime(target, now + ramp);\n }\n\n // Velocity-burst → master gain envelope (rate-limited so a steady fast\n // glide doesn't constantly hammer the analyser).\n if (v > 0.45 && now - this.lastCursorBeatAt > 0.22) {\n const peak = 0.95 + v * 0.45;\n this.output.gain.cancelScheduledValues(now);\n this.output.gain.setValueAtTime(this.output.gain.value, now);\n this.output.gain.linearRampToValueAtTime(peak, now + 0.035);\n this.output.gain.exponentialRampToValueAtTime(0.52, now + 0.22);\n this.lastCursorBeatAt = now;\n }\n }\n\n /**\n * Fire a bass-kick envelope — for hooking up heartbeats, MIDI clicks, or\n * any external rhythmic trigger. Sharp attack + bass voice swell so the\n * spectrum butterchurn sees registers as a clean bass beat.\n *\n * @param intensity 0..1, scales the peak\n */\n pulseBeat(intensity = 0.8): void {\n if (this.stopped) return;\n const now = this.ctx.currentTime;\n const i = Math.max(0, Math.min(1, intensity));\n\n // Master gain — sharp attack, exponential decay (kick-drum shape)\n const masterPeak = 0.95 + i * 0.55;\n this.output.gain.cancelScheduledValues(now);\n this.output.gain.setValueAtTime(this.output.gain.value, now);\n this.output.gain.linearRampToValueAtTime(masterPeak, now + 0.035);\n this.output.gain.exponentialRampToValueAtTime(0.42, now + 0.26);\n\n // Bass voices get an extra swell so the kick lands in the low band\n // (which is where butterchurn's preset reactivity usually lives).\n for (const voice of this.voices) {\n if (voice.band !== \"bass\") continue;\n const peak = voice.baseGain * (1.6 + i * 1.4);\n voice.gain.gain.cancelScheduledValues(now);\n voice.gain.gain.setValueAtTime(voice.gain.gain.value, now);\n voice.gain.gain.linearRampToValueAtTime(peak, now + 0.025);\n voice.gain.gain.exponentialRampToValueAtTime(\n Math.max(0.01, voice.baseGain), now + 0.34,\n );\n }\n }\n\n /** Periodic \"beat\" — short amplitude burst on the master gain. */\n private scheduleBeats(): void {\n const tick = (): void => {\n if (this.stopped) return;\n const now = this.ctx.currentTime;\n const peak = 0.95 + Math.random() * 0.35;\n this.output.gain.cancelScheduledValues(now);\n this.output.gain.setValueAtTime(this.output.gain.value, now);\n this.output.gain.linearRampToValueAtTime(peak, now + 0.04);\n this.output.gain.exponentialRampToValueAtTime(0.45, now + 0.28);\n // BPM-ish range 60–110, jittered\n const nextMs = 600 + Math.random() * 1000;\n this.beatTimer = window.setTimeout(tick, nextMs);\n };\n this.beatTimer = window.setTimeout(tick, 900);\n }\n\n /** Every ~20–35 s, smoothly mutate one voice's base frequency over 8 s. */\n private scheduleMutations(): void {\n const tick = (): void => {\n if (this.stopped) return;\n const v = this.voices[Math.floor(Math.random() * this.voices.length)];\n const now = this.ctx.currentTime;\n const ratio = 0.65 + Math.random() * 0.7; // 0.65× .. 1.35×\n const target = Math.max(20, Math.min(6000, v.base * ratio));\n v.osc.frequency.cancelScheduledValues(now);\n v.osc.frequency.setValueAtTime(v.osc.frequency.value, now);\n v.osc.frequency.linearRampToValueAtTime(target, now + 7.5);\n v.base = target;\n this.mutationTimer = window.setTimeout(tick, 18000 + Math.random() * 16000);\n };\n this.mutationTimer = window.setTimeout(tick, 6000);\n }\n}\n","// player.ts — public PrismPlayer class.\n//\n// Owns a small DOM tree (two stacked <canvas> elements inside `container`),\n// an AudioContext, a SyntheticSignal (the default audio driver), the two\n// rendering backends, and a GraphRuntime that swaps between them.\n//\n// Minimal usage:\n//\n// const player = new PrismPlayer({ container: el, graph });\n//\n// The caller can override the audio source any time via connectAudio(),\n// swap graphs via load(), or pipe a live texture into iChannel1 via\n// setLiveSource(). Backends + runtime + synth + audioCtx are exposed\n// readonly so embedders can drive them directly (read presetName, fire\n// pulseBeat from an external trigger, etc.) without re-creating the API.\n\nimport { createMilkdropBackground, type MilkdropBg } from \"./backends/milkdrop\";\nimport { createShadertoyBackground, type ShadertoyBg } from \"./backends/shadertoy\";\nimport { HeadlessSlideshow } from \"./image-feed\";\nimport { shortIdToGraph } from \"./registry\";\nimport { GraphRuntime, type ApplyResult } from \"./runtime\";\nimport { SyntheticSignal } from \"./synth\";\nimport type { PrismGraph } from \"./types\";\n\n/** Anything `load()` and the `graph` option accept. A string is treated\n * as a catalog short_id (6-char base62) and resolved against the\n * bundled registry. */\nexport type GraphInput = PrismGraph | string;\n\n/** Anything the `image` option / `connectImage()` accepts. Strings\n * beyond the \"webcam\" / \"tab\" sentinels are treated as static URLs. */\nexport type ImageSource =\n | \"webcam\"\n | \"tab\"\n | string\n | string[]\n | HTMLCanvasElement\n | HTMLVideoElement\n | MediaStream;\n\nexport interface PrismPlayerOptions {\n /** Where to mount the visualization canvases. Pass either an element\n * (preferred from frameworks — React refs, Vue template refs, etc.)\n * or a DOM id string for hand-written HTML embeds. The player\n * creates its own canvases inside; it does not touch any children\n * that already exist there. */\n container: HTMLElement | string;\n /** Optional initial graph. Pass a PrismGraph object directly, or a\n * 6-char short_id string (e.g. \"7Hq3pK\") to look up an entry from\n * the bundled catalog. Without it, cold-opens on a curated milkdrop\n * preset; you can call load() later. */\n graph?: GraphInput;\n /** Audio source. An AudioNode is connected directly; \"mic\" and \"tab\"\n * request the matching media stream via getUserMedia /\n * getDisplayMedia and connect that. Undefined → the built-in\n * SyntheticSignal drives both backends. */\n audio?: \"mic\" | \"tab\" | MediaStream | AudioNode;\n /** Bring your own AudioContext (useful when the embedder already has\n * one; web pages can only have a small number of them). */\n audioCtx?: AudioContext;\n /** iChannel1 fallback used by shader backends until something more\n * specific is bound by the graph or setLiveSource(). */\n defaultImage?: string;\n /** Image feed for shader backends (bound to iChannel1). Accepts:\n * - A single URL → static image\n * - An array of URLs → built-in crossfading slideshow\n * - \"webcam\" / \"tab\" → getUserMedia / getDisplayMedia\n * - a MediaStream / HTMLVideoElement → live video\n * - an HTMLCanvasElement → live canvas (e.g. your own\n * renderer; the player just\n * re-uploads its contents every\n * frame)\n * Use this for \"I want pictures showing through the shader\" without\n * thinking about iChannel1 or setLiveSource. */\n image?: ImageSource;\n /** Seconds each image is held on-screen before the crossfade to the\n * next begins. Only used when `image` is a URL array. Defaults to 6. */\n holdSeconds?: number;\n /** Milkdrop preset to load on cold-open. Falls back to a random preset\n * from the bundled library if the name isn't found. */\n initialPresetName?: string;\n /** Called once the first frame has painted. */\n onReady?: () => void;\n /** Async failures (audio capture rejection, graph load errors that\n * cannot be surfaced from a synchronous call) land here. */\n onError?: (err: Error) => void;\n}\n\nexport class PrismPlayer {\n /** AudioContext driving both backends + the default synthetic signal.\n * Created on construction unless one was passed in `opts.audioCtx`. */\n readonly audioCtx: AudioContext;\n /** Default audio driver: a silent \"pink-noise-ish\" pad that keeps the\n * visualization animated when no real audio is connected. Kept alive\n * for the lifetime of the player — call `player.synth.stop()` if you\n * want to free it after switching to a real audio source. */\n readonly synth: SyntheticSignal;\n /** Milkdrop (butterchurn) backend handle. Cold-opens on a random\n * preset (or `initialPresetName` if you passed one). */\n readonly milkdrop: MilkdropBg;\n /** Shadertoy backend handle. Idle until a graph with `lf.shadertoy` is\n * loaded (or you call `shadertoy.loadFromUrl` directly). */\n readonly shadertoy: ShadertoyBg;\n /** Graph executor — dispatches to the right backend based on the\n * graph's light-field generator node. */\n readonly runtime: GraphRuntime;\n /** Tracks which backend's canvas is currently visible. */\n activeBackend: \"milkdrop\" | \"shadertoy\" = \"milkdrop\";\n\n private readonly milkdropCanvas: HTMLCanvasElement;\n private readonly shadertoyCanvas: HTMLCanvasElement;\n private readonly ownsAudioCtx: boolean;\n /** Default hold per image when `image` is a URL list; set in ctor. */\n private readonly defaultHoldSeconds: number;\n /** Owned media resources (MediaStreams we started, <video> elements\n * we created internally, headless slideshow). Cleaned up by\n * connectImage() before re-binding and by destroy() on teardown. */\n private currentSlideshow: HeadlessSlideshow | null = null;\n private currentVideo: HTMLVideoElement | null = null;\n private currentStream: MediaStream | null = null;\n /** Audio MediaStream the player started itself (\"mic\" / \"tab\"). Held\n * so disconnectAudio() can stop the tracks. Null if the caller\n * passed in their own stream/node (we never stop tracks we don't own). */\n private currentAudioOwnedStream: MediaStream | null = null;\n\n constructor(opts: PrismPlayerOptions) {\n const container = resolveContainer(opts.container);\n\n this.audioCtx = opts.audioCtx ?? new AudioContext();\n this.ownsAudioCtx = opts.audioCtx === undefined;\n this.defaultHoldSeconds = opts.holdSeconds ?? 6;\n this.synth = new SyntheticSignal(this.audioCtx);\n\n // Two stacked canvases — opacity-crossfaded by setActiveBackend.\n // Match the legacy id/class pair so existing CSS (cold-open animation,\n // bg-canvas--hidden/--active modifiers) still applies on prism.scott.ai.\n this.milkdropCanvas = createCanvas(\"milkdrop\");\n this.shadertoyCanvas = createCanvas(\"shadertoy\");\n this.shadertoyCanvas.classList.add(\"bg-canvas--hidden\");\n // Inline opacity so visibility works without consumer CSS. Same\n // override behavior — prism.scott.ai's !important rules win.\n this.shadertoyCanvas.style.opacity = \"0\";\n this.shadertoyCanvas.style.pointerEvents = \"none\";\n container.appendChild(this.milkdropCanvas);\n container.appendChild(this.shadertoyCanvas);\n\n this.milkdrop = createMilkdropBackground(\n this.audioCtx,\n this.milkdropCanvas,\n this.synth.getOutput(),\n opts.onReady,\n { initialPresetName: opts.initialPresetName },\n );\n this.shadertoy = createShadertoyBackground(\n this.audioCtx,\n this.shadertoyCanvas,\n this.synth.getOutput(),\n );\n\n if (opts.defaultImage) {\n void this.shadertoy.bindImage(opts.defaultImage).catch((err: Error) => {\n opts.onError?.(err);\n });\n }\n\n this.runtime = new GraphRuntime({\n milkdrop: this.milkdrop,\n shadertoy: this.shadertoy,\n setActiveBackend: (which) => this.setActiveBackend(which),\n });\n\n if (opts.graph !== undefined) {\n const result = this.load(opts.graph);\n if (!result.ok && opts.onError) {\n opts.onError(new Error(result.error ?? \"graph apply failed\"));\n }\n }\n\n if (opts.audio !== undefined) {\n void this.connectAudio(opts.audio).catch((err: Error) => opts.onError?.(err));\n }\n\n if (opts.image !== undefined) {\n void this.connectImage(opts.image).catch((err: Error) => opts.onError?.(err));\n }\n }\n\n /** Swap to a new graph. Accepts either a full PrismGraph object or a\n * 6-char short_id string from the bundled registry. Returns the\n * ApplyResult the runtime emits so the caller can react to backend\n * switches / errors. An unknown short_id returns\n * `{ ok: false, error: \"unknown short_id ...\" }` without touching\n * the running visualization. */\n load(graph: GraphInput, blendSeconds?: number): ApplyResult {\n if (typeof graph === \"string\") {\n const resolved = shortIdToGraph(graph);\n if (!resolved) {\n return { ok: false, error: `unknown short_id: ${graph}` };\n }\n return this.runtime.apply(resolved, blendSeconds);\n }\n return this.runtime.apply(graph, blendSeconds);\n }\n\n /** Connect a new audio source. Accepts:\n * - \"mic\" — getUserMedia({ audio: true })\n * - \"tab\" — getDisplayMedia({ audio: true })\n * - MediaStream — wrapped in a MediaStreamAudioSourceNode\n * - AudioNode — connected directly\n * Routes the source to both backends so whichever one is active sees\n * reactivity. Tracks any MediaStream the player started itself so\n * disconnectAudio() can stop the tracks (mic indicator off, etc.). */\n async connectAudio(source: \"mic\" | \"tab\" | MediaStream | AudioNode): Promise<AudioNode> {\n const { node, owned } = await this.resolveAudioNode(source);\n this.milkdrop.connectAudio(node);\n this.shadertoy.connectAudio(node);\n this.currentAudioOwnedStream = owned;\n return node;\n }\n\n /** Disconnect the current audio source and revert both backends to\n * the built-in SyntheticSignal. Stops any MediaStream tracks the\n * player started itself (mic / tab capture indicator goes off).\n * Streams or AudioNodes the caller passed in are left untouched —\n * the caller owns their lifetime. Idempotent. */\n disconnectAudio(): void {\n if (this.currentAudioOwnedStream) {\n for (const track of this.currentAudioOwnedStream.getTracks()) track.stop();\n this.currentAudioOwnedStream = null;\n }\n const fallback = this.synth.getOutput();\n this.milkdrop.connectAudio(fallback);\n this.shadertoy.connectAudio(fallback);\n }\n\n /** Pipe a live source (e.g. a slideshow's canvas) into iChannel1 — its\n * contents are re-uploaded each frame. Pass null to disable.\n * Most embedders should prefer connectImage(), which handles\n * webcam/tab/slideshow plumbing for you. */\n setLiveSource(source: HTMLCanvasElement | HTMLVideoElement | null): void {\n this.shadertoy.setLiveSource(source);\n }\n\n /** Connect an image source for the shader's iChannel1. Symmetric to\n * connectAudio. Replaces any prior image feed; old MediaStream\n * tracks are stopped and internal slideshow timers are cleared.\n * See ImageSource for the accepted shapes. */\n async connectImage(source: ImageSource): Promise<void> {\n this.teardownImageFeed();\n if (typeof source === \"string\") {\n if (source === \"webcam\") {\n const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });\n this.bindStream(stream);\n return;\n }\n if (source === \"tab\") {\n const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });\n this.bindStream(stream);\n return;\n }\n // Treat any other string as a static image URL.\n this.setLiveSource(null);\n await this.shadertoy.bindImage(source);\n return;\n }\n if (Array.isArray(source)) {\n const slideshow = new HeadlessSlideshow(source, { holdSeconds: this.defaultHoldSeconds });\n this.currentSlideshow = slideshow;\n this.setLiveSource(slideshow.canvas);\n return;\n }\n if (source instanceof MediaStream) {\n this.bindStream(source);\n return;\n }\n // Canvas or video element passed in directly — wire it as the\n // live source without owning it (caller manages its lifetime).\n this.setLiveSource(source);\n }\n\n /** Stop the current image feed and unbind from the shader. Releases\n * resources the player owned: slideshow timer cleared, MediaStream\n * tracks stopped (webcam light off), internal <video> detached.\n * Canvas/video elements the caller passed in are left untouched.\n * After this, the shader reverts to whatever was last bound via\n * `defaultImage` or `shadertoy.bindImage()` (or the 1×1 placeholder).\n * Idempotent. */\n disconnectImage(): void {\n this.teardownImageFeed();\n }\n\n /** Toggle which backend's canvas is visible. Called by GraphRuntime;\n * callers can flip it manually too (rare). */\n setActiveBackend(which: \"milkdrop\" | \"shadertoy\"): void {\n this.activeBackend = which;\n if (which === \"milkdrop\") {\n this.milkdropCanvas.classList.remove(\"bg-canvas--hidden\");\n this.milkdropCanvas.classList.add(\"bg-canvas--active\");\n this.milkdropCanvas.style.opacity = \"1\";\n this.milkdropCanvas.style.pointerEvents = \"\";\n this.shadertoyCanvas.classList.add(\"bg-canvas--hidden\");\n this.shadertoyCanvas.classList.remove(\"bg-canvas--active\");\n this.shadertoyCanvas.style.opacity = \"0\";\n this.shadertoyCanvas.style.pointerEvents = \"none\";\n } else {\n this.shadertoyCanvas.classList.remove(\"bg-canvas--hidden\");\n this.shadertoyCanvas.classList.add(\"bg-canvas--active\");\n this.shadertoyCanvas.style.opacity = \"1\";\n this.shadertoyCanvas.style.pointerEvents = \"\";\n this.milkdropCanvas.classList.add(\"bg-canvas--hidden\");\n this.milkdropCanvas.classList.remove(\"bg-canvas--active\");\n this.milkdropCanvas.style.opacity = \"0\";\n this.milkdropCanvas.style.pointerEvents = \"none\";\n }\n }\n\n /** Stop animation, free GL resources, detach canvases, close the\n * AudioContext (only if the player created it itself). */\n destroy(): void {\n this.teardownImageFeed();\n if (this.currentAudioOwnedStream) {\n for (const track of this.currentAudioOwnedStream.getTracks()) track.stop();\n this.currentAudioOwnedStream = null;\n }\n this.milkdrop.destroy();\n this.shadertoy.destroy();\n this.synth.stop();\n this.milkdropCanvas.remove();\n this.shadertoyCanvas.remove();\n if (this.ownsAudioCtx) {\n void this.audioCtx.close();\n }\n }\n\n /** Wrap a MediaStream in an autoplaying <video> and bind that as the\n * live source. The video element is held internally; teardown is\n * handled by teardownImageFeed(). */\n private bindStream(stream: MediaStream): void {\n const video = document.createElement(\"video\");\n video.srcObject = stream;\n video.autoplay = true;\n video.muted = true;\n video.playsInline = true;\n // Auto-play may reject (Safari, autoplay policy); we ignore — the\n // user gesture that opened the stream usually satisfies the policy.\n void video.play().catch(() => undefined);\n this.currentVideo = video;\n this.currentStream = stream;\n this.setLiveSource(video);\n }\n\n /** Stop the current image feed (slideshow timer, MediaStream tracks,\n * internal <video>) and unbind from the shader. Idempotent. */\n private teardownImageFeed(): void {\n if (this.currentSlideshow) {\n this.currentSlideshow.destroy();\n this.currentSlideshow = null;\n }\n if (this.currentStream) {\n for (const track of this.currentStream.getTracks()) track.stop();\n this.currentStream = null;\n }\n if (this.currentVideo) {\n this.currentVideo.srcObject = null;\n this.currentVideo = null;\n }\n this.setLiveSource(null);\n }\n\n private async resolveAudioNode(\n source: \"mic\" | \"tab\" | MediaStream | AudioNode,\n ): Promise<{ node: AudioNode; owned: MediaStream | null }> {\n if (source instanceof AudioNode) return { node: source, owned: null };\n let stream: MediaStream;\n let owned = false;\n if (source instanceof MediaStream) {\n stream = source;\n } else if (source === \"mic\") {\n stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });\n owned = true;\n } else {\n stream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: false });\n owned = true;\n }\n return {\n node: this.audioCtx.createMediaStreamSource(stream),\n owned: owned ? stream : null,\n };\n }\n}\n\nfunction resolveContainer(container: HTMLElement | string): HTMLElement {\n if (typeof container !== \"string\") return container;\n const el = document.getElementById(container);\n if (!el) {\n throw new Error(`PrismPlayer: no element found with id \"${container}\"`);\n }\n return el;\n}\n\nfunction createCanvas(id: string): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.id = id;\n canvas.className = \"bg-canvas\";\n canvas.setAttribute(\"aria-hidden\", \"true\");\n // Inline defaults so the player renders out of the box without\n // consumer CSS. The deployed prism.scott.ai landing has its own\n // .bg-canvas rules using !important, which override these and\n // keep existing layout behavior intact.\n canvas.style.position = \"fixed\";\n canvas.style.inset = \"0\";\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n canvas.style.zIndex = \"0\";\n return canvas;\n}\n","// image-overlay.ts — draggable, resizable, collapsible PiP card.\n//\n// A pure-UI companion to PrismPlayer. The card has no opinion about\n// what's inside it — it just provides a positioned rectangle (the\n// `rect` property) that an embedder can sync to a slideshow's render\n// region, a video element, or anything else that wants to live in a\n// PiP. Drag from the body to move; drag from the 4 corner handles to\n// resize; click the × button to collapse to a thumbnail in the upper\n// right; click the thumbnail to expand again.\n//\n// Styling is class-based with a customisable prefix so the host page\n// can theme it. Defaults to `prism-overlay` with BEM-style modifiers\n// (`__handle`, `__collapse`); prism.scott.ai passes `slideshow-card` to\n// inherit its existing styles unchanged.\n\nexport interface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface OverlayState {\n rect: Rect;\n collapsed: boolean;\n}\n\nexport interface ImageOverlayOptions {\n /** Element to mount the card div in. Defaults to document.body. */\n mount?: HTMLElement;\n /** Initial card rect in CSS pixels. Defaults to a centered card sized\n * to ~58% of viewport width. */\n initialRect?: Rect;\n /** BEM class prefix for the card and its parts. Defaults to\n * \"prism-overlay\" — produces classes `.prism-overlay`,\n * `.prism-overlay__handle`, `.prism-overlay__collapse`. */\n className?: string;\n /** Thumbnail (collapsed) width in CSS pixels. Defaults to 200. */\n thumbWidth?: number;\n /** Min visible margin in pixels — drag is clamped so this much of\n * the card always stays in the viewport. Defaults to 60. */\n minVisible?: number;\n /** Min top edge (pixels from viewport top), e.g. to clear a status\n * bar. Defaults to 36. */\n topMargin?: number;\n /** Minimum card dimensions while resizing. Defaults to 80×45 (16:9). */\n minWidth?: number;\n minHeight?: number;\n /** Called whenever the rect or collapsed state changes (drag, resize,\n * collapse, expand, window resize re-anchor). */\n onChange?: (state: OverlayState) => void;\n}\n\ninterface DragState {\n startCard: Rect;\n startPointer: { x: number; y: number };\n handle: string | null;\n}\n\nexport class ImageOverlay {\n readonly element: HTMLElement;\n private readonly mount: HTMLElement;\n private readonly className: string;\n private readonly thumbW: number;\n private readonly thumbH: number;\n private readonly thumbMargin = 16;\n private readonly statusBarH: number;\n private readonly minVisible: number;\n private readonly minWidth: number;\n private readonly minHeight: number;\n private readonly onChange?: (state: OverlayState) => void;\n\n private _rect: Rect;\n private _collapsed = false;\n private uncollapsedRect: Rect | null = null;\n private drag: DragState | null = null;\n private destroyed = false;\n\n constructor(opts: ImageOverlayOptions = {}) {\n this.mount = opts.mount ?? document.body;\n this.className = opts.className ?? \"prism-overlay\";\n this.thumbW = opts.thumbWidth ?? 200;\n this.thumbH = this.thumbW * (9 / 16);\n this.minVisible = opts.minVisible ?? 60;\n this.statusBarH = opts.topMargin ?? 36;\n this.minWidth = opts.minWidth ?? 80;\n this.minHeight = opts.minHeight ?? 45;\n this.onChange = opts.onChange;\n this._rect = opts.initialRect ?? this.defaultRect();\n\n this.element = this.buildDom();\n this.mount.appendChild(this.element);\n this.applyRectToDom();\n\n window.addEventListener(\"resize\", this.onWindowResize, { passive: true });\n }\n\n /** Current card rect (live; do not mutate — call setRect instead). */\n get rect(): Rect {\n return { ...this._rect };\n }\n\n isCollapsed(): boolean {\n return this._collapsed;\n }\n\n /** Programmatically set the rect. Silent: does NOT call onChange.\n * Use this to push externally-managed state into the overlay\n * without echoing back into your own change handler. The internal\n * drag/resize handlers emit onChange themselves — that's the only\n * source of change notifications. */\n setRect(next: Rect): void {\n this._rect = { ...next };\n this.applyRectToDom();\n }\n\n /** Programmatically collapse. Silent — see setRect. */\n collapse(): void {\n if (this._collapsed) return;\n this.uncollapsedRect = { ...this._rect };\n this._rect = this.thumbRect();\n this._collapsed = true;\n this.element.setAttribute(\"data-collapsed\", \"\");\n this.applyRectToDom();\n }\n\n /** Programmatically expand. Silent — see setRect. */\n expand(): void {\n if (!this._collapsed) return;\n this._rect = this.uncollapsedRect ?? this.defaultRect();\n this._collapsed = false;\n this.element.removeAttribute(\"data-collapsed\");\n this.applyRectToDom();\n }\n\n show(): void {\n this.element.removeAttribute(\"data-hidden\");\n }\n\n hide(): void {\n this.element.setAttribute(\"data-hidden\", \"\");\n }\n\n /** Whether the card is currently visible (DOM attribute is the\n * source of truth, so this stays accurate if anyone toggles it\n * externally for testing). */\n isVisible(): boolean {\n return !this.element.hasAttribute(\"data-hidden\");\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n window.removeEventListener(\"resize\", this.onWindowResize);\n this.element.remove();\n }\n\n // ── internals ──────────────────────────────────────────────\n\n private buildDom(): HTMLElement {\n const card = document.createElement(\"div\");\n card.className = this.className;\n card.setAttribute(\"data-hidden\", \"\");\n\n const collapseBtn = document.createElement(\"button\");\n collapseBtn.type = \"button\";\n collapseBtn.className = `${this.className}__collapse`;\n collapseBtn.title = \"Collapse to thumbnail\";\n collapseBtn.setAttribute(\"aria-label\", \"Collapse\");\n const collapseIcon = document.createElement(\"span\");\n collapseIcon.setAttribute(\"aria-hidden\", \"true\");\n collapseIcon.textContent = \"−\";\n collapseBtn.appendChild(collapseIcon);\n collapseBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.collapse();\n this.emit();\n });\n card.appendChild(collapseBtn);\n\n for (const corner of [\"tl\", \"tr\", \"bl\", \"br\"] as const) {\n const handle = document.createElement(\"span\");\n handle.className = `${this.className}__handle ${this.className}__handle--${corner}`;\n handle.dataset.handle = corner;\n handle.setAttribute(\"aria-hidden\", \"true\");\n card.appendChild(handle);\n }\n\n card.addEventListener(\"pointerdown\", this.onPointerDown);\n card.addEventListener(\"pointermove\", this.onPointerMove);\n card.addEventListener(\"pointerup\", this.onPointerEnd);\n card.addEventListener(\"pointercancel\", this.onPointerEnd);\n\n return card;\n }\n\n private applyRectToDom(): void {\n const r = this._rect;\n this.element.style.left = `${r.x}px`;\n this.element.style.top = `${r.y}px`;\n this.element.style.width = `${r.w}px`;\n this.element.style.height = `${r.h}px`;\n }\n\n private emit(): void {\n this.onChange?.({ rect: { ...this._rect }, collapsed: this._collapsed });\n }\n\n private defaultRect(): Rect {\n // Centered, ~58% viewport width, 16:9 unless taller fits.\n const W = window.innerWidth;\n const H = window.innerHeight;\n const rw = W * 0.58;\n const rh = Math.min(rw * (9 / 16), H * 0.54);\n return { x: (W - rw) / 2, y: H * 0.16, w: rw, h: rh };\n }\n\n private thumbRect(): Rect {\n return {\n x: window.innerWidth - this.thumbW - this.thumbMargin,\n y: this.statusBarH + this.thumbMargin,\n w: this.thumbW,\n h: this.thumbH,\n };\n }\n\n private clampRect(r: Rect): Rect {\n return {\n x: Math.max(-(r.w - this.minVisible), Math.min(window.innerWidth - this.minVisible, r.x)),\n y: Math.max(this.statusBarH, Math.min(window.innerHeight - this.minVisible, r.y)),\n w: r.w,\n h: r.h,\n };\n }\n\n private readonly onWindowResize = (): void => {\n if (this._collapsed) {\n // Re-anchor the thumb to the new upper-right corner.\n this._rect = this.thumbRect();\n this.applyRectToDom();\n this.emit();\n }\n };\n\n private readonly onPointerDown = (e: PointerEvent): void => {\n const target = e.target as HTMLElement;\n if (target.closest(`.${this.className}__collapse`)) return; // collapse btn handles itself\n\n if (this._collapsed) {\n // Clicking the thumbnail expands.\n e.preventDefault();\n this.expand();\n this.emit();\n return;\n }\n\n e.preventDefault();\n this.element.setPointerCapture(e.pointerId);\n this.drag = {\n startCard: { ...this._rect },\n startPointer: { x: e.clientX, y: e.clientY },\n handle: target.classList.contains(`${this.className}__handle`)\n ? (target.dataset.handle ?? null)\n : null,\n };\n };\n\n private readonly onPointerMove = (e: PointerEvent): void => {\n if (!this.drag) return;\n e.preventDefault();\n const dx = e.clientX - this.drag.startPointer.x;\n const dy = e.clientY - this.drag.startPointer.y;\n const s = this.drag.startCard;\n const h = this.drag.handle;\n\n if (h === null) {\n // Whole-card drag.\n this._rect = this.clampRect({ x: s.x + dx, y: s.y + dy, w: s.w, h: s.h });\n } else {\n // Corner resize. l/r/t/b combinations resize from the matching edge.\n let nx = s.x, ny = s.y, nw = s.w, nh = s.h;\n if (h.includes(\"r\")) nw = Math.max(this.minWidth, s.w + dx);\n if (h.includes(\"l\")) { nw = Math.max(this.minWidth, s.w - dx); nx = s.x + (s.w - nw); }\n if (h.includes(\"b\")) nh = Math.max(this.minHeight, s.h + dy);\n if (h.includes(\"t\")) { nh = Math.max(this.minHeight, s.h - dy); ny = s.y + (s.h - nh); }\n this._rect = { x: nx, y: ny, w: nw, h: nh };\n }\n this.applyRectToDom();\n this.emit();\n };\n\n private readonly onPointerEnd = (e: PointerEvent): void => {\n if (!this.drag) return;\n try {\n this.element.releasePointerCapture(e.pointerId);\n } catch {\n /* ignore */\n }\n this.drag = null;\n };\n}\n\n"],"names":["milkdropConverter","milkdropConverterRaw","unwrap","mod","m","butterchurn","butterchurnRaw","butterchurnPresets","butterchurnPresetsRaw","createMilkdropBackground","audioCtx","canvas","silentSource","onReady","options","sizeTo","w","h","visualizer","presetMap","names","currentRaw","requested","requestedKey","findPresetKey","onResize","running","firstFrame","loop","pickNew","next","prettyPresetName","node","blendSeconds","name","key","url","res","milkText","converted","stem","_a","lower","ci","n","norm","s","target","raw","VERTEX_SHADER","FRAGMENT_PREAMBLE","FRAGMENT_EPILOGUE","createShadertoyBackground","glOrNull","gl","audioSource","analyser","fftBytes","audioTex","imageTex","dummyVao","currentProgram","currentUrl","currentName","liveSource","startTime","lastFrameTime","frame","mouseX","mouseY","mouseDown","compile","type","src","sh","log","numberLines","l","i","link","userSource","vs","fs","prog","frameLoop","now","iTime","iTimeDelta","loc","onPointerMove","e","onPointerDown","onPointerUp","source","img","resolve","reject","DEFAULT_HOLD_SECONDS","CROSSFADE_SECONDS","CANVAS_W","CANVAS_H","HeadlessSlideshow","urls","opts","__publicField","ctx","promises","loadImage","err","candidate","t","drawCovered","cur","nxt","srcAR","dstAR","sx","sy","sw","SCHEMA_VERSION","roleOf","nodesByRole","graph","role","makeMilkdropGraph","presetName","intent","registry","registryData","lookup","shortId","shortIds","shortIdToGraph","entry","entryToGraph","mainParams","mainType","nodes","GraphRuntime","blendSecondsOverride","lfNodes","presetUrl","_b","_c","_d","loaded","_e","imageUrl","_f","_g","SyntheticSignal","seeds","f","g","lfoHz","lfoD","bassEnd","midEnd","b","v","freq","gain","lfoDepth","osc","lfo","lfoGain","band","x01","y01","vel01","ramp","x","y","bassMul","trebleMul","midMul","voice","peak","intensity","masterPeak","tick","nextMs","ratio","PrismPlayer","container","resolveContainer","createCanvas","which","result","resolved","owned","track","fallback","stream","slideshow","video","el","id","ImageOverlay","dx","dy","nx","ny","nw","nh","card","collapseBtn","collapseIcon","corner","handle","r","W","H","rw","rh"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensordoc/prism",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Two-line embed for audio-reactive visualizations. Milkdrop + Shadertoy in a single PrismPlayer class.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,10 +32,10 @@
32
32
  "homepage": "https://prism.scott.ai",
33
33
  "repository": {
34
34
  "type": "git",
35
- "url": "https://github.com/tensordoc/prism.git",
35
+ "url": "https://github.com/Tensor-Doc/prism.git",
36
36
  "directory": "packages/prism"
37
37
  },
38
- "bugs": "https://github.com/tensordoc/prism/issues",
38
+ "bugs": "https://github.com/Tensor-Doc/prism/issues",
39
39
  "author": "Scott Penberthy (https://scott.ai)",
40
40
  "dependencies": {
41
41
  "butterchurn": "^2.6.7",