@wave3d/core 0.2.1 → 0.3.0
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.
- package/README.md +47 -0
- package/dist/config/model.d.ts +104 -1
- package/dist/config/model.js +111 -1
- package/dist/config/model.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/presets.js +15 -0
- package/dist/presets.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +34 -0
- package/dist/renderer/WaveRenderer.js +232 -7
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/interaction.d.ts +119 -0
- package/dist/renderer/interaction.js +474 -0
- package/dist/renderer/interaction.js.map +1 -0
- package/dist/renderer/palette.js +14 -0
- package/dist/renderer/palette.js.map +1 -1
- package/dist/renderer/shaders.js +105 -0
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/shell/createWave.d.ts +9 -1
- package/dist/shell/createWave.js +7 -1
- package/dist/shell/createWave.js.map +1 -1
- package/dist/shell/poster.d.ts +12 -0
- package/dist/shell/poster.js +4 -3
- package/dist/shell/poster.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +864 -219
- package/dist/standalone.d.ts +2 -2
- package/dist/standalone.js +2 -2
- package/dist/studio/StudioWaveRenderer.d.ts +15 -0
- package/dist/studio/StudioWaveRenderer.js +32 -1
- package/dist/studio/StudioWaveRenderer.js.map +1 -1
- package/dist/studio/index.d.ts +2 -1
- package/dist/studio/index.js +2 -1
- package/dist/studio/randomize.d.ts +3 -3
- package/dist/studio/randomize.js +4 -4
- package/dist/studio/randomize.js.map +1 -1
- package/dist/studio/thumbnail.d.ts +14 -0
- package/dist/studio/thumbnail.js +32 -0
- package/dist/studio/thumbnail.js.map +1 -0
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +20 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WaveRenderer.js","names":[],"sources":["../../src/renderer/WaveRenderer.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { EffectComposer } from \"three/addons/postprocessing/EffectComposer.js\";\nimport { RenderPass } from \"three/addons/postprocessing/RenderPass.js\";\nimport { OutputPass } from \"three/addons/postprocessing/OutputPass.js\";\nimport { ShaderPass } from \"three/addons/postprocessing/ShaderPass.js\";\nimport { UnrealBloomPass } from \"three/addons/postprocessing/UnrealBloomPass.js\";\nimport {\n vertexShader,\n fragmentShader,\n lineFragmentShader,\n postVertexShader,\n postFragmentShader,\n} from \"./shaders\";\nimport { WaveGeometry } from \"./WaveGeometry\";\nimport {\n buildPaletteTexture,\n configurePaletteTexture,\n paletteSignature,\n PALETTE_MAPS,\n paletteMapCanvas,\n canvasToTexture,\n loadPaletteImage,\n buildBackgroundGradientCanvas,\n buildBackgroundMeshCanvas,\n buildBackgroundImageCanvas,\n drawBackgroundMediaFrame,\n} from \"./palette\";\nimport { buildHeroPaletteCanvas, buildHeroPaletteTexture } from \"./heroPalette\";\nimport {\n MAX_COLORS,\n MAX_LIGHTS,\n MAX_MESH_POINTS,\n MAX_NOISE_BANDS,\n ensureStudioConfig,\n} from \"../config/model\";\nimport type { StudioConfig, WaveConfig, BlendMode } from \"../config/model\";\n\nconst BASE_SEGMENTS = 220; // base segment count along the ribbon; denser = smoother (scaled down per wave — see get segments)\n\n/** Reference frame (world units) the orthographic camera fills at cameraZoom 1. The wave is\n * framed by COVERING this FRAME_W × FRAME_H rectangle (centred on cameraTarget) into the canvas\n * — scaled to fill both dimensions, cropping the aspect overflow — so a given cameraZoom /\n * cameraTarget frames the wave the SAME at any canvas size or aspect (only the cropped margin\n * differs). FRAME_H = FRAME_W / (16/9) makes the reference a 16:9 rectangle; for canvases wider\n * than that the width binds, narrower ones zoom in to fill instead of\n * showing empty bands. This is what makes a saved preset reproduce on anyone's screen. */\nexport const FRAME_W = 1333;\nexport const FRAME_H = 750;\n\nexport interface WaveRendererOptions {\n /** Honor prefers-reduced-motion by freezing animation. Default true. */\n respectReducedMotion?: boolean;\n /**\n * Skip the intro time-ramp (the ~1s ease-in of animation on load), rendering at full speed\n * immediately. The studio passes `import.meta.env.DEV` here so a fresh renderer on every HMR\n * hot-swap doesn't replay the ease-in (which reads as a \"speed up\" when you tab back). Default\n * false — production embeds keep the ease-in. Ignored while paused (a paused frame is always the\n * full frame). Default false.\n */\n skipIntroRamp?: boolean;\n}\n\n/**\n * Per-wave 2D palette texture (+ optional looping video). One instance per wave, so each\n * wave carries its own palette. Guarded by a signature so it only rebuilds when that wave's\n * palette actually changes (not every refresh).\n */\nclass WavePalette {\n texture?: THREE.Texture;\n private sig = \"\";\n private video?: HTMLVideoElement;\n private videoUrl = \"\";\n private failedUrl = \"\";\n\n constructor(\n private readonly makeVideo: (url: string) => HTMLVideoElement,\n private readonly onVideoReady: () => void,\n ) {}\n\n /** Rebuild this wave's palette texture from its config (video / custom image / stops /\n * built-in map / hero LUT) and point the wave's palette uniforms at it. */\n apply(cfg: WaveConfig, uniforms: Record<string, THREE.IUniform>): void {\n const videoUrl = cfg.paletteVideoUrl;\n const url = cfg.paletteImageUrl;\n const source = cfg.paletteSource ?? \"hero\";\n let sig: string;\n let build: () => THREE.Texture;\n if (videoUrl) {\n this.ensureVideo(videoUrl);\n if (this.video?.readyState && this.video.readyState >= 2) {\n sig = \"video|\" + videoUrl;\n build = () => configurePaletteTexture(new THREE.VideoTexture(this.video!));\n } else {\n sig = \"video-loading|\" + videoUrl;\n build = () => buildHeroPaletteTexture();\n }\n } else if (url) {\n this.clearVideo();\n sig = \"url|\" + url;\n build = () => loadPaletteImage(url);\n } else if (source === \"stops\") {\n this.clearVideo();\n const opts = {\n stops: cfg.palette,\n edgeColor: cfg.paletteEdgeColor ?? \"#8e9dff\",\n edgeAmount: cfg.paletteEdgeAmount ?? 0.3,\n };\n sig = \"stops|\" + paletteSignature(opts);\n build = () => buildPaletteTexture(opts);\n } else if (PALETTE_MAPS[source]) {\n this.clearVideo();\n sig = \"map|\" + source;\n build = () => canvasToTexture(paletteMapCanvas(PALETTE_MAPS[source]));\n } else {\n this.clearVideo();\n sig = \"hero\";\n build = () => buildHeroPaletteTexture();\n }\n if (sig !== this.sig || !this.texture) {\n this.texture?.dispose();\n this.texture = build();\n this.sig = sig;\n }\n uniforms.uPalette.value = this.texture;\n uniforms.uUsePalette.value = cfg.usePaletteTexture === false ? 0 : 1;\n uniforms.uPaletteRaw.value = 1;\n }\n\n /** Play/pause this wave's palette video with the render loop. */\n syncPlayback(cfg: WaveConfig, running: boolean): void {\n const v = this.video;\n if (!v) return;\n const active =\n running &&\n cfg.gradientType !== \"mesh\" &&\n cfg.usePaletteTexture !== false &&\n !!cfg.paletteVideoUrl;\n if (active) void v.play().catch(() => {});\n else v.pause();\n }\n\n private ensureVideo(url: string): void {\n if (this.videoUrl === url || this.failedUrl === url) return;\n this.clearVideo();\n this.videoUrl = url;\n const video = this.makeVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n this.video = video;\n this.failedUrl = \"\";\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n this.failedUrl = url;\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n this.video = video;\n video.load();\n }\n\n private clearVideo(): void {\n if (this.video) {\n this.video.pause();\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = undefined;\n this.videoUrl = \"\";\n }\n\n dispose(): void {\n this.texture?.dispose();\n this.texture = undefined;\n this.clearVideo();\n this.failedUrl = \"\";\n }\n}\n\ntype Wave = {\n mesh: THREE.Mesh;\n material: THREE.ShaderMaterial;\n geometry: WaveGeometry;\n /** This wave's own 2D palette texture + optional video. */\n palette: WavePalette;\n};\n\n// Parse scratch: refresh() converts ~25 hex colours per wave per call (i.e. per slider input),\n// so reuse one Color instead of allocating each time.\nconst HEX_SCRATCH = new THREE.Color();\n\n/** Convert an sRGB hex string to a linear-space RGB vector (three's ColorManagement does the\n * sRGB→linear conversion on parse). Exported for the studio subclass's live light-uniform push. */\nexport function hexToLinearVec3(hex: string, target: THREE.Vector3): THREE.Vector3 {\n // three's ColorManagement (on by default in r169) already converts the sRGB hex to\n // LINEAR when parsing the hex — its .r/.g/.b are linear. Calling\n // convertSRGBToLinear() again would double-linearize (crushing greens → everything\n // turns red), so we read the components directly.\n const c = HEX_SCRATCH.set(hex);\n return target.set(c.r, c.g, c.b);\n}\n\n/**\n * Renders a gradient \"wave of light\" from a {@link StudioConfig}. Framework-agnostic:\n * it needs only a DOM container and a config. The studio mutates the config in\n * place and calls `refresh()` / `rebuild()`.\n */\nexport class WaveRenderer {\n readonly renderer: THREE.WebGLRenderer;\n protected readonly scene = new THREE.Scene();\n protected readonly camera: THREE.OrthographicCamera;\n protected readonly group = new THREE.Group();\n private readonly composer: EffectComposer;\n private readonly postPass: ShaderPass;\n /** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */\n private bloomPass?: UnrealBloomPass;\n protected readonly container: HTMLElement;\n private readonly respectReducedMotion: boolean;\n private readonly skipIntroRamp: boolean;\n\n protected config: StudioConfig;\n protected waves: Wave[] = [];\n\n // 2D palette textures (+ any palette videos) live per-wave — see WavePalette on each Wave.\n private backgroundTexture?: THREE.Texture;\n private backgroundSig = \"\";\n private backgroundImage?: HTMLImageElement;\n private backgroundImageUrl = \"\";\n private failedBackgroundImageUrl = \"\";\n private backgroundVideo?: HTMLVideoElement;\n private backgroundVideoUrl = \"\";\n private failedBackgroundVideoUrl = \"\";\n private backgroundVideoCanvas?: HTMLCanvasElement;\n /** Authored default camera pose, for \"Reset camera\". */\n protected readonly homeCamPos = new THREE.Vector3();\n protected readonly homeCamTarget = new THREE.Vector3();\n\n // Reused scratch for per-frame clip-plane fitting (see updateClipPlanes) — hoisted so the\n // render loop allocates nothing.\n private readonly clipBox = new THREE.Box3();\n private readonly clipSphere = new THREE.Sphere();\n private readonly clipTmpA = new THREE.Vector3();\n private readonly clipTmpB = new THREE.Vector3();\n\n private readonly timer = new THREE.Timer();\n private time = 0;\n private rafId = 0;\n protected running = false;\n private started = false;\n\n private visible = true;\n private pageVisible = true;\n private reducedMotion = false;\n /** Intro ramp: eases animation time 0→1 over ~1s on load (when config.introRamp). */\n private introTimeRamp = 0;\n\n private readonly resizeObserver: ResizeObserver;\n private readonly intersectionObserver: IntersectionObserver;\n private readonly motionQuery: MediaQueryList;\n\n protected capturing = false;\n /** Fixed backing-buffer dimensions used by the studio's visible export frame. Embeds leave\n * this unset and continue to resize responsively with their container and device DPR. */\n private outputSize?: { width: number; height: number };\n\n constructor(container: HTMLElement, config: StudioConfig, options: WaveRendererOptions = {}) {\n this.container = container;\n this.config = ensureStudioConfig(config);\n this.respectReducedMotion = options.respectReducedMotion ?? true;\n this.skipIntroRamp = options.skipIntroRamp ?? false;\n\n this.renderer = new THREE.WebGLRenderer({\n // antialias smooths edges; preserveDrawingBuffer keeps the drawing buffer readable so we\n // can export PNG/WebM. Both cost a little performance, but this authoring tool needs them.\n antialias: true,\n alpha: true,\n preserveDrawingBuffer: true,\n powerPreference: \"high-performance\",\n });\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.renderer.setClearColor(0x000000, 0);\n container.appendChild(this.renderer.domElement);\n\n // Resilience: if the GPU drops the context (memory pressure, sleep/wake), don't\n // let the browser hard-crash the page — prevent the default and rebuild on restore.\n this.renderer.domElement.addEventListener(\"webglcontextlost\", this.onContextLost, false);\n this.renderer.domElement.addEventListener(\n \"webglcontextrestored\",\n this.onContextRestored,\n false,\n );\n\n // Orthographic, framed in device pixels: resize() sets the frustum to the canvas size, and\n // the mesh is scaled up so the wave overflows the frame, leaving only the twist on screen.\n // The left/right/top/bottom bounds here are placeholders overwritten by the first resize();\n // near/far are placeholders too — updateClipPlanes() refits them to the scene every frame so\n // no camera angle ever clips the wave (a fixed slab does — see updateClipPlanes).\n this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 1, 10000);\n this.camera.position.set(\n config.cameraPosition.x,\n config.cameraPosition.y,\n config.cameraPosition.z,\n );\n this.camera.zoom = config.cameraZoom ?? 1;\n this.camera.lookAt(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.camera.updateProjectionMatrix();\n // Remember the authored default pose so \"Reset camera\" returns to it (orbit mutates\n // config.cameraPosition, so we can't read it back from config later).\n this.homeCamPos.copy(this.camera.position);\n this.homeCamTarget.set(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.scene.add(this.group);\n\n this.composer = new EffectComposer(this.renderer);\n this.composer.addPass(new RenderPass(this.scene, this.camera));\n this.postPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: new THREE.Vector2(1, 1) },\n uBlurAmount: { value: config.blur },\n uBlurSamples: { value: 6 },\n uGrainAmount: { value: config.grain },\n uTime: { value: 0 },\n },\n vertexShader: postVertexShader,\n fragmentShader: postFragmentShader,\n });\n this.composer.addPass(this.postPass);\n this.composer.addPass(new OutputPass());\n\n this.motionQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.reducedMotion = this.respectReducedMotion && this.motionQuery.matches;\n this.motionQuery.addEventListener(\"change\", this.onMotionChange);\n document.addEventListener(\"visibilitychange\", this.onVisibilityChange);\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n this.visible = entries[0]?.isIntersecting ?? true;\n this.updateRunning();\n },\n { rootMargin: \"100px\" },\n );\n this.intersectionObserver.observe(container);\n\n this.resizeObserver = new ResizeObserver(this.onResize);\n this.resizeObserver.observe(container);\n\n this.applyBackground();\n this.buildWaves();\n this.resize();\n }\n\n private get segments(): number {\n // Scale detail down as waves multiply, so total geometry stays bounded.\n const q = this.config.quality / Math.sqrt(Math.max(1, this.config.waves.length));\n return THREE.MathUtils.clamp(Math.round(BASE_SEGMENTS * q), 24, 360);\n }\n\n private makeUniforms(): Record<string, THREE.IUniform> {\n const colors: THREE.Vector3[] = [];\n const colorPos: number[] = [];\n for (let i = 0; i < MAX_COLORS; i++) {\n colors.push(new THREE.Vector3(1, 1, 1));\n colorPos.push(MAX_COLORS > 1 ? i / (MAX_COLORS - 1) : 0);\n }\n const meshPointPos: THREE.Vector2[] = [];\n const meshPointColor: THREE.Vector3[] = [];\n const meshPointInfluence: number[] = [];\n for (let i = 0; i < MAX_MESH_POINTS; i++) {\n meshPointPos.push(new THREE.Vector2(0.5, 0.5));\n meshPointColor.push(new THREE.Vector3(1, 1, 1));\n meshPointInfluence.push(0.65);\n }\n const lightPos: THREE.Vector3[] = [];\n const lightColor: THREE.Vector3[] = [];\n const lightIntensity: number[] = [];\n for (let i = 0; i < MAX_LIGHTS; i++) {\n lightPos.push(new THREE.Vector3());\n lightColor.push(new THREE.Vector3(1, 1, 1));\n lightIntensity.push(0);\n }\n const bandBounds: THREE.Vector4[] = [];\n const bandParams: THREE.Vector4[] = [];\n const bandParaPow: number[] = [];\n for (let i = 0; i < MAX_NOISE_BANDS; i++) {\n bandBounds.push(new THREE.Vector4());\n bandParams.push(new THREE.Vector4());\n bandParaPow.push(0);\n }\n return {\n // Deformation (vertex)\n uTime: { value: 0 },\n uSpeed: { value: 0.05 },\n uSeed: { value: 0 },\n uDispFreqX: { value: 0.003234 },\n uDispFreqZ: { value: 0.00799 },\n uDispAmount: { value: 6.051 },\n uDetailFreq: { value: 0.04 },\n uDetailAmount: { value: 0 }, // 2nd displacement octave (read only under DETAIL_OCTAVE)\n uTwFreqX: { value: -0.055 },\n uTwFreqY: { value: 0.077 },\n uTwFreqZ: { value: -0.518 },\n uTwPowX: { value: 3.95 },\n uTwPowY: { value: 5.85 },\n uTwPowZ: { value: 6.33 },\n uLoopSeconds: { value: 0 }, // seamless-loop period (read only under the LOOP_MOTION define)\n // Colour + light (fragment)\n uColors: { value: colors },\n uColorPos: { value: colorPos },\n uColorCount: { value: 2 },\n uGradType: { value: 0 },\n uGradAngle: { value: 0 },\n uGradShift: { value: 0.15 },\n uMeshPointPos: { value: meshPointPos },\n uMeshPointColor: { value: meshPointColor },\n uMeshPointInfluence: { value: meshPointInfluence },\n uMeshPointCount: { value: 0 },\n uMeshSoftness: { value: 0.62 },\n uPalette: { value: null },\n uUsePalette: { value: 1 },\n uPaletteRaw: { value: 1 },\n uPaletteScale: { value: new THREE.Vector2(1, 1) },\n uPaletteOffset: { value: new THREE.Vector2(0, 0) },\n uPaletteRotation: { value: 0 },\n uDebug: { value: 0 },\n uSheen: { value: 1 },\n uRoundness: { value: 0.35 },\n uIridescence: { value: 0 },\n uDepthTint: { value: 0 }, // solid-theme depth tint (read only under DEPTH_TINT)\n uDepthTintColor: { value: new THREE.Vector3() },\n uHueShift: { value: 0 },\n uContrast: { value: 1 },\n uSaturation: { value: 1 },\n uFiberCount: { value: 90 },\n uFiberStrength: { value: 0.25 },\n uTexture: { value: 0 },\n uCreaseLight: { value: 0.15 },\n uCreaseSharpness: { value: 2.0 },\n uCreaseSoftness: { value: 1.0 },\n uEdgeFade: { value: 0.06 },\n uEdgeFeather: { value: 0.1 }, // ribbon-edge softness (read only under EDGE_FEATHER)\n uOpacity: { value: 1 },\n uSquared: { value: 1 }, // \"squared\" deep-colour mode: square the colour in-shader (see applyBlendMode)\n uResolution: { value: new THREE.Vector2(1, 1) },\n uAmbient: { value: 0.45 },\n uNumLights: { value: 1 },\n uLightPos: { value: lightPos },\n uLightColor: { value: lightColor },\n uLightIntensity: { value: lightIntensity },\n uNumNoiseBands: { value: 0 },\n uNoiseBandBounds: { value: bandBounds },\n uNoiseBandParams: { value: bandParams },\n uNoiseBandParaPow: { value: bandParaPow },\n // Wireframe thin-line theme (used only by lineFragmentShader)\n uLineAmount: { value: 425 },\n uLineThickness: { value: 1 },\n uLineDerivativePower: { value: 0.95 },\n uMaxWidth: { value: 1232 },\n uClearColor: { value: new THREE.Vector3(1, 1, 1) },\n };\n }\n\n /** Vertex-shader #defines for a wave: TWIST_MOTION (per-wave animated twist wobble) and\n * LOOP_MOTION (scene-level seamless loop). Both select #ifdef-gated code paths; an empty\n * object compiles the default (linear-time) program. */\n private waveDefines(sc: WaveConfig | undefined): Record<string, string> {\n const defines: Record<string, string> = {};\n if (sc?.twistMotion) defines.TWIST_MOTION = \"\";\n if ((this.config.loopSeconds ?? 0) > 0) defines.LOOP_MOTION = \"\";\n if ((sc?.detailAmount ?? 0) !== 0) defines.DETAIL_OCTAVE = \"\";\n if ((sc?.depthTint ?? 0) > 0) defines.DEPTH_TINT = \"\";\n if ((sc?.edgeFeather ?? 0.1) !== 0.1) defines.EDGE_FEATHER = \"\";\n return defines;\n }\n\n private addWave(): void {\n const geo = new WaveGeometry(this.segments);\n // Initialise defines/fragment/blend from the wave this material will represent, so the\n // first refresh() doesn't force a needless program recompile. Falls back to the first wave.\n const sc = this.config.waves[this.waves.length] ?? this.config.waves[0];\n const material = new THREE.ShaderMaterial({\n uniforms: this.makeUniforms(),\n // TWIST_MOTION / LOOP_MOTION select variant vertex-shader paths. Toggled live in refresh().\n defines: this.waveDefines(sc),\n vertexShader,\n // solid theme = surfaceColor shader; wireframe theme = thin-line shader.\n // Swapped live in refresh() when the wave's theme changes.\n fragmentShader: sc?.theme === \"wireframe\" ? lineFragmentShader : fragmentShader,\n transparent: true,\n depthTest: true,\n depthWrite: true,\n side: THREE.DoubleSide,\n });\n // Blending (incl. the squaring blend) is set from the wave's blendMode — see applyBlendMode —\n // so it survives refresh() instead of being a dead constructor flag.\n this.applyBlendMode(material, sc?.blendMode ?? \"squared\");\n const mesh = new THREE.Mesh(geo.geometry, material);\n mesh.frustumCulled = false;\n this.group.add(mesh);\n const palette = new WavePalette(\n (url) => this.createLoopingVideo(url),\n () => {\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n );\n this.waves.push({ mesh, material, geometry: geo, palette });\n }\n\n /**\n * Apply config.blendMode to a material. \"squared\" (the default) is the hero blend:\n * CustomBlending with AddEquation, src = SrcColorFactor, dst = ZeroFactor, so the\n * framebuffer result is fragColor² — the squaring deepens the colours into the vivid\n * hero look (without it the wave reads pastel). \"additive\"/\"normal\"/\"multiply\" are\n * authoring overrides. Multiply uses Three's premultiplied-alpha path; the custom\n * fragment shaders premultiply their output when Three injects PREMULTIPLIED_ALPHA.\n * Returns true if material state changed (caller flags needsUpdate).\n */\n private applyBlendMode(material: THREE.ShaderMaterial, mode: BlendMode): boolean {\n // \"squared\" is the deep hero look. It used to be a framebuffer-squaring CustomBlending\n // (src·src, dst×0) — which REPLACES the destination rather than compositing over it, so any\n // semi-transparent pixel (soft ribbon edges, and the large near-edge-on regions at oblique\n // camera angles) wiped the framebuffer's colour AND alpha, punching dark / see-through holes\n // through the background and through other waves. On a transparent page you never saw it; on\n // an opaque background or with overlapping waves it's the \"chunks vanish\" artifact.\n //\n // Fix: do the colour-squaring in the shader (uSquared) and composite it with ordinary\n // premultiplied alpha (NormalBlending). Over an opaque body the result is identical (col²);\n // soft edges now blend into what's behind them instead of erasing it.\n const squared = mode === \"squared\";\n const blending =\n mode === \"additive\"\n ? THREE.AdditiveBlending\n : mode === \"multiply\"\n ? THREE.MultiplyBlending\n : THREE.NormalBlending; // \"squared\" (deep, via uSquared) and \"normal\" both composite\n // Premultiplied so the squared/multiply colour composites correctly (the shaders premultiply\n // their output under the PREMULTIPLIED_ALPHA define Three injects for this).\n const premultipliedAlpha = mode === \"multiply\" || squared;\n material.uniforms.uSquared.value = squared ? 1 : 0;\n if (material.blending === blending && material.premultipliedAlpha === premultipliedAlpha) {\n return false;\n }\n material.blending = blending;\n material.premultipliedAlpha = premultipliedAlpha;\n return true;\n }\n\n private disposeWaves(): void {\n for (const s of this.waves) {\n this.group.remove(s.mesh);\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n }\n this.waves = [];\n }\n\n /**\n * Reconcile the wave pool to `waveCount` WITHOUT tearing everything down:\n * keep existing waves (so the compiled shader program is never deleted and\n * re-compiled — that churn can crash some GPU drivers), add/remove only the\n * delta, and resize each geometry to the current quality.\n */\n private buildWaves(): void {\n const target = Math.max(1, this.config.waves.length);\n while (this.waves.length > target) {\n const s = this.waves.pop();\n if (!s) break;\n this.group.remove(s.mesh);\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n }\n while (this.waves.length < target) this.addWave();\n\n const segments = this.segments;\n this.waves.forEach((s, i) => {\n s.geometry.resize(segments);\n s.mesh.renderOrder = i;\n });\n this.refresh();\n }\n\n /** Re-read per-frame-independent values from the (mutated) config. */\n refresh(): void {\n this.applyBackground();\n this.applyPost();\n // Once an external driver (orbit / edit gizmo) owns the camera, don't fight it here; the shell\n // (no orbit) applies the saved camera position/target so it matches the authored view.\n if (!this.isCameraExternallyDriven()) {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n }\n\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const u = wave.material.uniforms;\n if (this.applyBlendMode(wave.material, sc.blendMode)) wave.material.needsUpdate = true;\n // Recompile the program when its #define set changes: TWIST_MOTION / DETAIL_OCTAVE / DEPTH_TINT\n // (per wave) and LOOP_MOTION (scene-level). Compare the whole set so any combination is handled.\n const wantDefines = this.waveDefines(sc);\n const curDefines = wave.material.defines ?? {};\n if (Object.keys(wantDefines).sort().join(\",\") !== Object.keys(curDefines).sort().join(\",\")) {\n wave.material.defines = wantDefines;\n wave.material.needsUpdate = true;\n }\n // Swap the fragment shader when this wave's theme changes: solid surfaceColor <->\n // wireframe thin-line. Three recompiles the program on needsUpdate.\n const wantFrag = sc.theme === \"wireframe\" ? lineFragmentShader : fragmentShader;\n if (wave.material.fragmentShader !== wantFrag) {\n wave.material.fragmentShader = wantFrag;\n wave.material.needsUpdate = true;\n }\n\n const stops = [...sc.palette].sort((a, b) => a.pos - b.pos);\n const colorCount = Math.max(1, Math.min(stops.length, MAX_COLORS));\n const colors = u.uColors.value as THREE.Vector3[];\n const colorPos = u.uColorPos.value as number[];\n for (let c = 0; c < MAX_COLORS; c++) {\n const stop = stops[Math.min(c, colorCount - 1)] ?? { color: \"#ffffff\", pos: 0 };\n hexToLinearVec3(stop.color, colors[c]);\n colorPos[c] = stop.pos;\n }\n u.uColorCount.value = colorCount;\n u.uGradType.value =\n sc.gradientType === \"radial\"\n ? 1\n : sc.gradientType === \"conic\"\n ? 2\n : sc.gradientType === \"mesh\"\n ? 3\n : 0;\n u.uGradAngle.value = ((sc.gradientAngle ?? 0) * Math.PI) / 180;\n u.uGradShift.value = sc.gradientShift ?? 0;\n const meshPoints = sc.meshGradientPoints.slice(0, MAX_MESH_POINTS);\n const meshPointPos = u.uMeshPointPos.value as THREE.Vector2[];\n const meshPointColor = u.uMeshPointColor.value as THREE.Vector3[];\n const meshPointInfluence = u.uMeshPointInfluence.value as number[];\n for (let pointIndex = 0; pointIndex < MAX_MESH_POINTS; pointIndex++) {\n const point = meshPoints[pointIndex] ?? meshPoints[meshPoints.length - 1];\n if (!point) continue;\n meshPointPos[pointIndex].set(point.x, point.y);\n hexToLinearVec3(point.color, meshPointColor[pointIndex]);\n meshPointInfluence[pointIndex] = point.influence;\n }\n u.uMeshPointCount.value = meshPoints.length;\n u.uMeshSoftness.value = sc.meshGradientSoftness;\n u.uPaletteScale.value.set(sc.paletteTextureScale?.x ?? 1, sc.paletteTextureScale?.y ?? 1);\n u.uPaletteOffset.value.set(sc.paletteTextureOffset?.x ?? 0, sc.paletteTextureOffset?.y ?? 0);\n u.uPaletteRotation.value = ((sc.paletteTextureRotation ?? 0) * Math.PI) / 180;\n u.uHueShift.value = sc.hueShift;\n u.uContrast.value = sc.colorContrast;\n u.uSaturation.value = sc.colorSaturation;\n // Wireframe thin-line theme params (used only by lineFragmentShader). uClearColor is the\n // between-line colour = the page background (scene), fed in linear space like the palette.\n u.uLineAmount.value = sc.lineAmount ?? 425;\n u.uLineThickness.value = sc.lineThickness ?? 1;\n u.uLineDerivativePower.value = sc.lineDerivativePower ?? 0.95;\n u.uMaxWidth.value = sc.maxWidth ?? 1232;\n hexToLinearVec3(this.config.background, u.uClearColor.value as THREE.Vector3);\n u.uFiberCount.value = sc.fiberCount;\n u.uFiberStrength.value = sc.fiberStrength;\n u.uTexture.value = sc.texture;\n u.uCreaseLight.value = sc.creaseLight;\n u.uCreaseSharpness.value = sc.creaseSharpness;\n u.uCreaseSoftness.value = sc.creaseSoftness;\n u.uSheen.value = sc.sheen ?? 1;\n u.uRoundness.value = sc.roundness ?? 0.35;\n u.uIridescence.value = sc.iridescence ?? 0;\n u.uDepthTint.value = sc.depthTint ?? 0;\n hexToLinearVec3(sc.depthTintColor ?? \"#0a2540\", u.uDepthTintColor.value as THREE.Vector3);\n u.uEdgeFade.value = sc.edgeFade;\n u.uEdgeFeather.value = sc.edgeFeather ?? 0.1;\n // Lights + ambient are scene-level (shared by every wave).\n const lights = this.config.lights ?? [];\n u.uAmbient.value = this.config.ambient ?? 0.45;\n u.uNumLights.value = Math.min(lights.length, MAX_LIGHTS);\n const lPos = u.uLightPos.value as THREE.Vector3[];\n const lCol = u.uLightColor.value as THREE.Vector3[];\n const lInt = u.uLightIntensity.value as number[];\n for (let li = 0; li < MAX_LIGHTS; li++) {\n const light = lights[li];\n if (light) {\n lPos[li].set(light.position.x, light.position.y, light.position.z);\n hexToLinearVec3(light.color, lCol[li]);\n lInt[li] = light.intensity;\n } else {\n lInt[li] = 0;\n }\n }\n // Noise bands (per-region fiber overrides) — per wave\n const bands = sc.noiseBands ?? [];\n u.uNumNoiseBands.value = Math.min(bands.length, MAX_NOISE_BANDS);\n const bBounds = u.uNoiseBandBounds.value as THREE.Vector4[];\n const bParams = u.uNoiseBandParams.value as THREE.Vector4[];\n const bPara = u.uNoiseBandParaPow.value as number[];\n for (let bi = 0; bi < MAX_NOISE_BANDS; bi++) {\n const band = bands[bi];\n if (band) {\n bBounds[bi].set(band.startX, band.endX, band.startY, band.endY);\n bParams[bi].set(band.feather, band.strength, band.frequency, band.colorAttenuation);\n bPara[bi] = band.parabolaPower;\n }\n }\n // Deformation (absolute per wave)\n u.uSpeed.value = sc.speed;\n u.uSeed.value = sc.seed;\n u.uLoopSeconds.value = this.config.loopSeconds ?? 0; // scene-level; shared by every wave\n u.uDispFreqX.value = sc.displaceFrequency.x;\n u.uDispFreqZ.value = sc.displaceFrequency.y;\n u.uDispAmount.value = sc.displaceAmount;\n u.uDetailFreq.value = sc.detailFrequency ?? 0.04;\n u.uDetailAmount.value = sc.detailAmount ?? 0;\n u.uTwFreqX.value = sc.twistFrequency.x;\n u.uTwFreqY.value = sc.twistFrequency.y;\n u.uTwFreqZ.value = sc.twistFrequency.z;\n u.uTwPowX.value = sc.twistPower.x;\n u.uTwPowY.value = sc.twistPower.y;\n u.uTwPowZ.value = sc.twistPower.z;\n // Mesh transform — each wave's ABSOLUTE scale / rotation / position, applied via\n // modelMatrix using THREE's Euler XYZ order so the on-screen orientation matches the\n // authored view.\n wave.mesh.scale.set(sc.scale.x, sc.scale.y, sc.scale.z);\n wave.mesh.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);\n u.uOpacity.value = sc.opacity;\n });\n\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n\n // Whole-wave mirror (world-space flip ≈ screen flip for the near-frontal camera).\n this.group.scale.set(this.config.mirrorH ? -1 : 1, this.config.mirrorV ? -1 : 1, 1);\n\n this.onAfterRefresh();\n if (!this.running) this.renderOnce();\n }\n\n /** Point every wave's palette sampler at its own WavePalette texture (each rebuilt only\n * when that wave's palette actually changes — see WavePalette.apply). */\n private updatePaletteTextures(): void {\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.apply(sc, wave.material.uniforms);\n });\n }\n\n /** Dev: 0 = normal, 1 = visualise the crease value, 2 = visualise derivative normal. */\n setDebug(v: number): void {\n for (const s of this.waves) s.material.uniforms.uDebug.value = v;\n this.renderOnce();\n }\n\n /** Rebuild geometry + waves (call when waveCount or quality changes). */\n rebuild(): void {\n this.buildWaves();\n }\n\n private applyBackground(): void {\n if (this.config.transparentBackground) {\n this.scene.background = null;\n this.renderer.setClearColor(0x000000, 0);\n return;\n }\n\n const matte = new THREE.Color(this.config.background);\n this.renderer.setClearColor(matte, 1);\n if (this.config.backgroundMode === \"color\") {\n this.applyColorBackground(matte);\n return;\n }\n // Live video is redrawn every frame, so cap its staging canvas near 1080p. Still images\n // and procedural maps retain the full 4K-capable background texture path.\n const { width, height } = this.backgroundCanvasSize(\n this.config.backgroundVideoUrl ? 2048 : 4096,\n );\n if (this.config.backgroundMode === \"gradient\") this.applyGradientBackground(width, height);\n else this.applyImageBackground(matte, width, height);\n }\n\n private applyColorBackground(matte: THREE.Color): void {\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.scene.background = matte;\n }\n\n private applyGradientBackground(width: number, height: number): void {\n this.clearBackgroundVideo();\n const gradType = this.config.backgroundGradientType;\n if (gradType === \"mesh\") {\n const pts = this.config.backgroundMeshPoints ?? [];\n const softness = this.config.backgroundMeshSoftness ?? 0.62;\n const ptSig = pts\n .map((p) => `${p.color}@${p.x.toFixed(3)},${p.y.toFixed(3)},${p.influence.toFixed(3)}`)\n .join(\",\");\n const sig = [\"mesh\", softness.toFixed(3), ptSig, width, height].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundMeshCanvas(pts, softness, width, height);\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n return;\n }\n const source = this.config.backgroundGradientSource ?? \"stops\";\n const def = PALETTE_MAPS[source];\n const stops =\n source !== \"stops\" && def?.kind === \"gradient\" && def.stops\n ? def.stops\n : this.config.backgroundPalette;\n const stopSig = stops.map((stop) => `${stop.color}@${stop.pos.toFixed(3)}`).join(\",\");\n const sig = [\n \"gradient\",\n source,\n gradType,\n this.config.backgroundGradientAngle,\n stopSig,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundGradientCanvas({\n stops,\n type: gradType,\n angle: this.config.backgroundGradientAngle,\n width,\n height,\n });\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n /** Image mode: a live video, a user-loaded image, or a built-in map. `matte` shows while an\n * async source is still loading. */\n private applyImageBackground(matte: THREE.Color, width: number, height: number): void {\n const fit = this.config.backgroundImageFit ?? \"cover\";\n const zoom = this.config.backgroundImageZoom ?? 1;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n const videoUrl = this.config.backgroundVideoUrl;\n const customUrl = this.config.backgroundImageUrl;\n let source: CanvasImageSource;\n let sourceWidth: number;\n let sourceHeight: number;\n let sourceSig: string;\n if (videoUrl) {\n this.ensureBackgroundVideo(videoUrl);\n if (!this.backgroundVideo || this.backgroundVideo.readyState < 2) {\n this.scene.background = matte;\n return;\n }\n source = this.backgroundVideo;\n sourceWidth = this.backgroundVideo.videoWidth;\n sourceHeight = this.backgroundVideo.videoHeight;\n sourceSig = `video|${videoUrl}`;\n } else if (customUrl) {\n this.clearBackgroundVideo();\n if (\n !this.backgroundImage ||\n this.backgroundImageUrl !== customUrl ||\n !this.backgroundImage.complete\n ) {\n this.loadBackgroundImage(customUrl);\n this.scene.background = matte;\n return;\n }\n source = this.backgroundImage;\n sourceWidth = this.backgroundImage.naturalWidth;\n sourceHeight = this.backgroundImage.naturalHeight;\n sourceSig = `custom|${customUrl}`;\n } else {\n this.clearBackgroundVideo();\n const imageSource = this.config.backgroundImageSource ?? \"vaporwave\";\n const canvas =\n imageSource === \"hero\"\n ? buildHeroPaletteCanvas()\n : PALETTE_MAPS[imageSource]?.kind === \"image\"\n ? paletteMapCanvas(PALETTE_MAPS[imageSource], Math.max(width, height))\n : null;\n if (!canvas) {\n this.scene.background = matte;\n return;\n }\n source = canvas;\n sourceWidth = canvas.width;\n sourceHeight = canvas.height;\n sourceSig = `map|${imageSource}`;\n }\n\n const sig = [\n \"image\",\n sourceSig,\n fit,\n zoom,\n position.x,\n position.y,\n this.config.background,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundImageCanvas(\n source,\n sourceWidth,\n sourceHeight,\n width,\n height,\n fit,\n this.config.background,\n zoom,\n position.x,\n position.y,\n );\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n this.backgroundVideoCanvas = videoUrl ? canvas : undefined;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n private backgroundCanvasSize(maxRequestedEdge = 4096): { width: number; height: number } {\n const rawWidth = this.outputSize?.width ?? Math.max(1, this.container.clientWidth);\n const rawHeight = this.outputSize?.height ?? Math.max(1, this.container.clientHeight);\n const maxEdge = Math.min(maxRequestedEdge, this.renderer.capabilities.maxTextureSize);\n const scale = Math.min(1, maxEdge / Math.max(rawWidth, rawHeight));\n return {\n width: Math.max(1, Math.round(rawWidth * scale)),\n height: Math.max(1, Math.round(rawHeight * scale)),\n };\n }\n\n private loadBackgroundImage(url: string): void {\n if (this.backgroundImageUrl === url || this.failedBackgroundImageUrl === url) return;\n this.backgroundImageUrl = url;\n this.backgroundImage = undefined;\n const image = new Image();\n image.decoding = \"async\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) image.crossOrigin = \"anonymous\";\n image.addEventListener(\n \"load\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.backgroundImage = image;\n this.failedBackgroundImageUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.failedBackgroundImageUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.src = url;\n }\n\n private ensureBackgroundVideo(url: string): void {\n if (this.backgroundVideoUrl === url || this.failedBackgroundVideoUrl === url) return;\n this.clearBackgroundVideo();\n this.backgroundVideoUrl = url;\n const video = this.createLoopingVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.backgroundVideo = video;\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.failedBackgroundVideoUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n this.backgroundVideo = video;\n video.load();\n }\n\n private clearBackgroundVideo(): void {\n if (this.backgroundVideo) {\n this.backgroundVideo.pause();\n this.backgroundVideo.removeAttribute(\"src\");\n this.backgroundVideo.load();\n }\n this.backgroundVideo = undefined;\n this.backgroundVideoUrl = \"\";\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundVideoCanvas = undefined;\n }\n\n private createLoopingVideo(url: string): HTMLVideoElement {\n const video = document.createElement(\"video\");\n video.muted = true;\n video.defaultMuted = true;\n video.loop = true;\n video.playsInline = true;\n video.preload = \"auto\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) video.crossOrigin = \"anonymous\";\n video.src = url;\n return video;\n }\n\n private syncVideoPlayback(): void {\n const backgroundActive =\n this.running &&\n !this.config.transparentBackground &&\n this.config.backgroundMode === \"image\" &&\n !!this.config.backgroundVideoUrl;\n if (this.backgroundVideo) {\n if (backgroundActive) void this.backgroundVideo.play().catch(() => {});\n else this.backgroundVideo.pause();\n }\n // Palette videos are per wave.\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.syncPlayback(sc, this.running);\n });\n }\n\n private updateBackgroundVideoFrame(): void {\n const video = this.backgroundVideo;\n const canvas = this.backgroundVideoCanvas;\n if (!video || !canvas || video.readyState < 2 || !this.backgroundTexture) return;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n drawBackgroundMediaFrame(\n canvas,\n video,\n video.videoWidth,\n video.videoHeight,\n this.config.backgroundImageFit ?? \"cover\",\n this.config.background,\n this.config.backgroundImageZoom ?? 1,\n position.x,\n position.y,\n );\n this.backgroundTexture.needsUpdate = true;\n }\n\n private applyPost(): void {\n const u = this.postPass.uniforms;\n u.uBlurAmount.value = this.config.blur;\n u.uGrainAmount.value = this.config.grain;\n u.uBlurSamples.value = Math.round(this.config.blurSamples ?? 6);\n this.applyBloom();\n }\n\n /** Insert / tune / remove the bloom pass. strength 0 removes it from the composer entirely, so\n * cost and pixels are identical to bloom-off; the pass (and its mip-chain render targets) is\n * created lazily the first time bloom is enabled and disposed when turned back off. It sits\n * right after the scene RenderPass so it blooms the wave before the grain/blur pass. */\n private applyBloom(): void {\n const strength = this.config.bloomStrength ?? 0;\n if (strength > 0) {\n if (!this.bloomPass) {\n const size = this.renderer.getDrawingBufferSize(new THREE.Vector2());\n this.bloomPass = new UnrealBloomPass(\n size,\n strength,\n this.config.bloomRadius ?? 0.4,\n this.config.bloomThreshold ?? 0.85,\n );\n this.composer.insertPass(this.bloomPass, 1); // after RenderPass, before postPass/OutputPass\n }\n this.bloomPass.strength = strength;\n this.bloomPass.radius = this.config.bloomRadius ?? 0.4;\n this.bloomPass.threshold = this.config.bloomThreshold ?? 0.85;\n } else if (this.bloomPass) {\n this.composer.removePass(this.bloomPass);\n this.bloomPass.dispose();\n this.bloomPass = undefined;\n }\n }\n\n private onResize = (): void => {\n this.resize();\n };\n\n private onContextLost = (e: Event): void => {\n e.preventDefault(); // tell the browser we'll recover → no \"Aw, Snap\" crash\n cancelAnimationFrame(this.rafId);\n this.running = false;\n };\n\n private onContextRestored = (): void => {\n this.disposeWaves(); // old GPU resources are invalid on a fresh context (per-wave palettes too)\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.buildWaves();\n this.resize();\n this.updateRunning();\n };\n\n resize(): void {\n const w = this.outputSize?.width ?? Math.max(1, this.container.clientWidth);\n const h = this.outputSize?.height ?? Math.max(1, this.container.clientHeight);\n const dpr = this.outputSize ? 1 : Math.min(window.devicePixelRatio || 1, this.config.dprMax);\n this.renderer.setPixelRatio(dpr);\n this.renderer.setSize(w, h, !this.outputSize);\n if (this.outputSize) {\n // setSize(..., false) preserves the exact backing buffer without writing fixed CSS\n // pixel dimensions. Keep the canvas stretched to the visible aspect-ratio frame.\n this.renderer.domElement.style.width = \"100%\";\n this.renderer.domElement.style.height = \"100%\";\n }\n this.composer.setPixelRatio(dpr);\n this.composer.setSize(w, h);\n const dw = w * dpr;\n const dh = h * dpr;\n (this.postPass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n for (const s of this.waves) {\n (s.material.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n }\n // The responsive ortho framing: the frustum = the canvas in DEVICE pixels (1 world unit =\n // 1px at zoom 1). Combined with the ×10 mesh scale, the wave overflows the frame.\n this.camera.left = -dw / 2;\n this.camera.right = dw / 2;\n this.camera.top = dh / 2;\n this.camera.bottom = -dh / 2;\n this.applyZoom(); // responsive ortho zoom (maps FRAME_W world units onto the canvas)\n this.applyBackground();\n this.onAfterResize();\n if (!this.running) this.renderOnce();\n }\n\n /** Set an exact output buffer while CSS scales the canvas into the on-screen export frame. */\n setOutputSize(width: number, height: number): void {\n this.outputSize = {\n width: THREE.MathUtils.clamp(Math.round(width), 64, 8192),\n height: THREE.MathUtils.clamp(Math.round(height), 64, 8192),\n };\n this.resize();\n }\n\n start(): void {\n this.started = true;\n this.updateRunning();\n }\n\n stop(): void {\n this.started = false;\n this.updateRunning();\n }\n\n private onMotionChange = (e: MediaQueryListEvent): void => {\n this.reducedMotion = this.respectReducedMotion && e.matches;\n this.updateRunning();\n };\n\n private onVisibilityChange = (): void => {\n this.pageVisible = document.visibilityState === \"visible\";\n this.updateRunning();\n };\n\n private updateRunning(): void {\n const shouldAnimate =\n this.started &&\n this.visible &&\n this.pageVisible &&\n !this.config.paused &&\n !this.reducedMotion;\n\n if (shouldAnimate && !this.running) {\n this.running = true;\n // Reset the delta baseline (as Clock.start() + a discarded getDelta() used to) so the first\n // frame advances by ~one frame, not by the whole idle/pause gap that elapsed while stopped.\n this.timer.update();\n this.rafId = requestAnimationFrame(this.loop);\n } else if (!shouldAnimate && this.running) {\n this.running = false;\n cancelAnimationFrame(this.rafId);\n }\n // When not animating (paused / reduced-motion / static export) show the FULL frame, not a\n // frozen mid-ease, by forcing introTimeRamp = 1.\n if (!this.running) {\n this.introTimeRamp = 1;\n this.renderOnce();\n }\n this.syncVideoPlayback();\n }\n\n private loop = (): void => {\n if (!this.running) return;\n this.timer.update();\n this.time += this.timer.getDelta();\n if (this.introTimeRamp < 1) this.introTimeRamp = Math.min(1, this.introTimeRamp + 0.016); // ~1s to full at 60fps\n this.renderOnce();\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n /** Advance the per-frame time uniforms (geometry itself is static). Time model:\n * time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */\n private updateTime(): void {\n // Skip the ease-in when asked: a fresh renderer (first load, or the studio's HMR hot-swap)\n // starts introTimeRamp at 0, and replaying the ramp on every save reads as a \"speed up\" when\n // you tab back. The studio passes skipIntroRamp in dev; prod builds + the embed keep the ease-in.\n const ramp = this.config.introRamp === false || this.skipIntroRamp ? 1 : this.introTimeRamp;\n const t = this.time * ramp + (this.config.timeOffset ?? 0);\n // Indexed loop (no per-frame closure) — this runs every frame.\n for (let i = 0; i < this.waves.length; i++) {\n const u = this.waves[i].material.uniforms;\n u.uTime.value = t;\n // Palette drift: flow the colour along the ribbon independently of the geometry by drifting\n // uPaletteOffset over time. Only touch it when nonzero, so refresh()'s static base offset —\n // and every drift-off preset — is left byte-for-byte unchanged.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const dx = sc.paletteDriftX ?? 0;\n const dy = sc.paletteDriftY ?? 0;\n if (dx !== 0 || dy !== 0) {\n const base = sc.paletteTextureOffset;\n (u.uPaletteOffset.value as THREE.Vector2).set(base.x + dx * t, base.y + dy * t);\n }\n }\n this.postPass.uniforms.uTime.value = t;\n }\n\n /** Render exactly one frame at the current time. */\n renderOnce(): void {\n this.updateBackgroundVideoFrame();\n this.updateTime();\n this.updateClipPlanes(); // keep near/far bracketing the scene so no camera angle clips the wave\n this.composer.render();\n // Editor overlays (gizmo/helpers + camera-rig minimap) draw on top of the composed frame —\n // the studio subclass plugs them in here; the base renders nothing extra.\n this.onAfterRenderFrame();\n }\n\n /** Re-evaluate play/pause after `config.paused` changes. */\n refreshPlayback(): void {\n this.updateRunning();\n }\n\n /** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /\n * cameraZoom). Called on whole-config swaps (preset / reset / randomize / import). The base\n * applies the pose directly; the studio subclass overrides it to also drive the orbit target\n * and sync the panel. Hook ⑤. */\n protected applyCameraFromConfig(): void {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n // ---- Editor hook points (no-ops in the base; the studio subclass overrides them) ----\n\n /** Hook ①: true while orbit or an edit gizmo owns the camera, so refresh() won't reset it. */\n protected isCameraExternallyDriven(): boolean {\n return false;\n }\n\n /** Hook ②: called at the end of refresh(), before the trailing renderOnce(). */\n protected onAfterRefresh(): void {}\n\n /** Hook ③: called at the end of renderOnce(), after the composed frame is drawn. */\n protected onAfterRenderFrame(): void {}\n\n /** Hook ④: called at the end of resize(), before the trailing renderOnce(). */\n protected onAfterResize(): void {}\n\n /** Responsive ortho zoom: COVER the FRAME_W × FRAME_H reference frame onto the canvas so the\n * wave frames the same at any size/aspect/dpr (only the cropped margin differs), times the\n * user's cameraZoom. `max(...)` = cover (fill both axes, crop overflow); `min(...)` would be\n * contain (fit with letterbox bands). Cover keeps the wave filling the frame on every screen. */\n protected applyZoom(): void {\n const dw = this.camera.right - this.camera.left; // device px (set in resize)\n const dh = this.camera.top - this.camera.bottom;\n this.camera.zoom = Math.max(dw / FRAME_W, dh / FRAME_H) * (this.config.cameraZoom ?? 1);\n this.camera.updateProjectionMatrix();\n }\n\n /** Fit the orthographic near/far planes to the scene before every render, so no part of a wave\n * is ever clipped as the camera orbits / dollies / pans (or when waves are added or scaled).\n *\n * The camera is *constructed* with fixed 1..10000 planes that only suit the authored hero\n * framing; once the view moves, the wave's depth extent along the view axis easily crosses\n * them and the GPU hard-slices the geometry along a flat plane — chunks of the wave vanish.\n *\n * The in-shader twist rotates each vertex about its LOCAL origin, so it can never move a vertex\n * further from that origin than the base geometry already sits. A sphere at each mesh's origin\n * (radius = base extent × the mesh's largest world scale, plus the Y displacement, ×1.2 slack)\n * therefore safely contains the fully-deformed wave. We union those, then bracket the union\n * along the view axis with a margin. Bracketing to the scene (rather than a fixed huge slab)\n * keeps clip-space depth scene-normalised — so the wireframe theme's depth fade, which reads\n * gl_Position.z, looks the same at any camera distance instead of washing out. Runs each frame;\n * it's a handful of vector ops over 1–8 meshes with no allocation. */\n private updateClipPlanes(): void {\n this.clipBox.makeEmpty();\n for (const wave of this.waves) {\n const mesh = wave.mesh;\n mesh.updateWorldMatrix(true, false);\n const bs = mesh.geometry.boundingSphere;\n if (!bs) continue;\n // The twist pivot (the mesh's local origin) in world space = the safe sphere's centre.\n const center = this.clipTmpA.setFromMatrixPosition(mesh.matrixWorld);\n const disp = Math.abs(Number(wave.material.uniforms.uDispAmount.value) || 0);\n const localRadius = bs.center.length() + bs.radius + disp;\n const radius = localRadius * mesh.matrixWorld.getMaxScaleOnAxis() * 1.2;\n // Enclose this wave's sphere by adding its axis-aligned bounding cube corners.\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(radius));\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(-radius));\n }\n if (this.clipBox.isEmpty()) return;\n this.clipBox.getBoundingSphere(this.clipSphere);\n const viewDir = this.camera.getWorldDirection(this.clipTmpA); // normalised view axis\n const centerDepth = this.clipTmpB\n .copy(this.clipSphere.center)\n .sub(this.camera.position)\n .dot(viewDir);\n const radius = this.clipSphere.radius;\n const margin = radius * 0.25 + 10;\n // Orthographic: a negative near is legal — the slab may extend behind the camera origin.\n this.camera.near = centerDepth - radius - margin;\n this.camera.far = centerDepth + radius + margin;\n this.camera.updateProjectionMatrix();\n }\n\n /** A world-space position delta that drops a duplicated wave into open frame space beside the\n * one it was copied from — screen-left and a touch down, sized to the visible frame — instead\n * of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed\n * off-frame. Camera-relative: it's \"left on screen\" no matter how the view is rotated/zoomed,\n * because it's built from the camera's right/up axes and the frame's visible world size. */\n\n async captureImage(\n mime: string,\n transparent = true,\n quality?: number,\n time?: number,\n ): Promise<Blob> {\n const prevBg = this.config.transparentBackground;\n if (transparent !== prevBg) {\n this.config.transparentBackground = transparent;\n this.applyBackground();\n }\n // Deterministic frame: render at a fixed animation-time (with the ease-in ramp forced full) so\n // a captured poster is reproducible instead of whatever frame happened to be on screen — pass\n // time = 0 for the frame the wave opens on. Restored in `finally`. Undefined = the live frame.\n const fixTime = time !== undefined;\n const prevTime = this.time;\n const prevRamp = this.introTimeRamp;\n if (fixTime) {\n this.time = time;\n this.introTimeRamp = 1;\n }\n let blob: Blob | null = null;\n try {\n this.capturing = true;\n this.renderOnce();\n blob = await new Promise<Blob | null>((resolve) =>\n this.canvas.toBlob(resolve, mime, quality),\n );\n } finally {\n this.capturing = false;\n if (transparent !== prevBg) {\n this.config.transparentBackground = prevBg;\n this.applyBackground();\n }\n if (fixTime) {\n this.time = prevTime;\n this.introTimeRamp = prevRamp;\n }\n this.renderOnce();\n }\n if (!blob || blob.type !== mime) throw new Error(`Failed to capture ${mime}`);\n return blob;\n }\n\n captureStream(fps = 60): MediaStream {\n return this.canvas.captureStream(fps);\n }\n\n get canvas(): HTMLCanvasElement {\n return this.renderer.domElement;\n }\n\n getConfig(): StudioConfig {\n return this.config;\n }\n\n setConfig(config: StudioConfig): void {\n const next = ensureStudioConfig(config);\n const structural =\n next.waves.length !== this.waves.length || next.quality !== this.config.quality;\n this.config = next;\n if (structural) this.rebuild();\n else this.refresh();\n // A whole new config (preset/reset/randomize/import) carries its own authored framing —\n // apply it even in the studio, where refresh() leaves the camera to orbit. Without this,\n // selecting a preset updated the wave but kept the previous camera (wrong framing).\n this.applyCameraFromConfig();\n }\n\n dispose(): void {\n cancelAnimationFrame(this.rafId);\n this.running = false;\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.motionQuery.removeEventListener(\"change\", this.onMotionChange);\n document.removeEventListener(\"visibilitychange\", this.onVisibilityChange);\n this.renderer.domElement.removeEventListener(\"webglcontextlost\", this.onContextLost);\n this.renderer.domElement.removeEventListener(\"webglcontextrestored\", this.onContextRestored);\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n for (const s of this.waves) {\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n }\n this.bloomPass?.dispose();\n this.composer.dispose();\n this.renderer.dispose();\n this.renderer.domElement.remove();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WaveRenderer.js","names":[],"sources":["../../src/renderer/WaveRenderer.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { EffectComposer } from \"three/addons/postprocessing/EffectComposer.js\";\nimport { RenderPass } from \"three/addons/postprocessing/RenderPass.js\";\nimport { OutputPass } from \"three/addons/postprocessing/OutputPass.js\";\nimport { ShaderPass } from \"three/addons/postprocessing/ShaderPass.js\";\nimport { UnrealBloomPass } from \"three/addons/postprocessing/UnrealBloomPass.js\";\nimport {\n vertexShader,\n fragmentShader,\n lineFragmentShader,\n postVertexShader,\n postFragmentShader,\n} from \"./shaders\";\nimport { WaveGeometry } from \"./WaveGeometry\";\nimport {\n InteractionController,\n interactionActive,\n anyPointerFxActive,\n wavePointerFxActive,\n waveRipplesActive,\n WAVE_APPLIERS,\n SCENE_APPLIERS,\n RIPPLE_SLOTS,\n} from \"./interaction\";\nimport {\n buildPaletteTexture,\n configurePaletteTexture,\n paletteSignature,\n PALETTE_MAPS,\n paletteMapCanvas,\n canvasToTexture,\n loadPaletteImage,\n buildBackgroundGradientCanvas,\n buildBackgroundMeshCanvas,\n buildBackgroundImageCanvas,\n drawBackgroundMediaFrame,\n} from \"./palette\";\nimport { buildHeroPaletteCanvas, buildHeroPaletteTexture } from \"./heroPalette\";\nimport {\n MAX_COLORS,\n MAX_LIGHTS,\n MAX_MESH_POINTS,\n MAX_NOISE_BANDS,\n ensureStudioConfig,\n} from \"../config/model\";\nimport type { StudioConfig, WaveConfig, BlendMode } from \"../config/model\";\n\nconst BASE_SEGMENTS = 220; // base segment count along the ribbon; denser = smoother (scaled down per wave — see get segments)\n\n/** Reference frame (world units) the orthographic camera fills at cameraZoom 1. The wave is\n * framed by COVERING this FRAME_W × FRAME_H rectangle (centred on cameraTarget) into the canvas\n * — scaled to fill both dimensions, cropping the aspect overflow — so a given cameraZoom /\n * cameraTarget frames the wave the SAME at any canvas size or aspect (only the cropped margin\n * differs). FRAME_H = FRAME_W / (16/9) makes the reference a 16:9 rectangle; for canvases wider\n * than that the width binds, narrower ones zoom in to fill instead of\n * showing empty bands. This is what makes a saved preset reproduce on anyone's screen. */\nexport const FRAME_W = 1333;\nexport const FRAME_H = 750;\n\nexport interface WaveRendererOptions {\n /** Honor prefers-reduced-motion by freezing animation. Default true. */\n respectReducedMotion?: boolean;\n /**\n * Skip the intro time-ramp (the ~1s ease-in of animation on load), rendering at full speed\n * immediately. The studio passes `import.meta.env.DEV` here so a fresh renderer on every HMR\n * hot-swap doesn't replay the ease-in (which reads as a \"speed up\" when you tab back). Default\n * false — production embeds keep the ease-in. Ignored while paused (a paused frame is always the\n * full frame). Default false.\n */\n skipIntroRamp?: boolean;\n}\n\n/**\n * Per-wave 2D palette texture (+ optional looping video). One instance per wave, so each\n * wave carries its own palette. Guarded by a signature so it only rebuilds when that wave's\n * palette actually changes (not every refresh).\n */\nclass WavePalette {\n texture?: THREE.Texture;\n private sig = \"\";\n private video?: HTMLVideoElement;\n private videoUrl = \"\";\n private failedUrl = \"\";\n\n constructor(\n private readonly makeVideo: (url: string) => HTMLVideoElement,\n private readonly onVideoReady: () => void,\n ) {}\n\n /** Rebuild this wave's palette texture from its config (video / custom image / stops /\n * built-in map / hero LUT) and point the wave's palette uniforms at it. */\n apply(cfg: WaveConfig, uniforms: Record<string, THREE.IUniform>): void {\n const videoUrl = cfg.paletteVideoUrl;\n const url = cfg.paletteImageUrl;\n const source = cfg.paletteSource ?? \"hero\";\n let sig: string;\n let build: () => THREE.Texture;\n if (videoUrl) {\n this.ensureVideo(videoUrl);\n if (this.video?.readyState && this.video.readyState >= 2) {\n sig = \"video|\" + videoUrl;\n build = () => configurePaletteTexture(new THREE.VideoTexture(this.video!));\n } else {\n sig = \"video-loading|\" + videoUrl;\n build = () => buildHeroPaletteTexture();\n }\n } else if (url) {\n this.clearVideo();\n sig = \"url|\" + url;\n build = () => loadPaletteImage(url);\n } else if (source === \"stops\") {\n this.clearVideo();\n const opts = {\n stops: cfg.palette,\n edgeColor: cfg.paletteEdgeColor ?? \"#8e9dff\",\n edgeAmount: cfg.paletteEdgeAmount ?? 0.3,\n };\n sig = \"stops|\" + paletteSignature(opts);\n build = () => buildPaletteTexture(opts);\n } else if (PALETTE_MAPS[source]) {\n this.clearVideo();\n sig = \"map|\" + source;\n build = () => canvasToTexture(paletteMapCanvas(PALETTE_MAPS[source]));\n } else {\n this.clearVideo();\n sig = \"hero\";\n build = () => buildHeroPaletteTexture();\n }\n if (sig !== this.sig || !this.texture) {\n this.texture?.dispose();\n this.texture = build();\n this.sig = sig;\n }\n uniforms.uPalette.value = this.texture;\n uniforms.uUsePalette.value = cfg.usePaletteTexture === false ? 0 : 1;\n uniforms.uPaletteRaw.value = 1;\n }\n\n /** Play/pause this wave's palette video with the render loop. */\n syncPlayback(cfg: WaveConfig, running: boolean): void {\n const v = this.video;\n if (!v) return;\n const active =\n running &&\n cfg.gradientType !== \"mesh\" &&\n cfg.usePaletteTexture !== false &&\n !!cfg.paletteVideoUrl;\n if (active) void v.play().catch(() => {});\n else v.pause();\n }\n\n private ensureVideo(url: string): void {\n if (this.videoUrl === url || this.failedUrl === url) return;\n this.clearVideo();\n this.videoUrl = url;\n const video = this.makeVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n this.video = video;\n this.failedUrl = \"\";\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n this.failedUrl = url;\n this.sig = \"\";\n this.onVideoReady();\n },\n { once: true },\n );\n this.video = video;\n video.load();\n }\n\n private clearVideo(): void {\n if (this.video) {\n this.video.pause();\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = undefined;\n this.videoUrl = \"\";\n }\n\n dispose(): void {\n this.texture?.dispose();\n this.texture = undefined;\n this.clearVideo();\n this.failedUrl = \"\";\n }\n}\n\ntype Wave = {\n mesh: THREE.Mesh;\n material: THREE.ShaderMaterial;\n geometry: WaveGeometry;\n /** This wave's own 2D palette texture + optional video. */\n palette: WavePalette;\n};\n\n// Parse scratch: refresh() converts ~25 hex colours per wave per call (i.e. per slider input),\n// so reuse one Color instead of allocating each time.\nconst HEX_SCRATCH = new THREE.Color();\n\n/** Convert an sRGB hex string to a linear-space RGB vector (three's ColorManagement does the\n * sRGB→linear conversion on parse). Exported for the studio subclass's live light-uniform push. */\nexport function hexToLinearVec3(hex: string, target: THREE.Vector3): THREE.Vector3 {\n // three's ColorManagement (on by default in r169) already converts the sRGB hex to\n // LINEAR when parsing the hex — its .r/.g/.b are linear. Calling\n // convertSRGBToLinear() again would double-linearize (crushing greens → everything\n // turns red), so we read the components directly.\n const c = HEX_SCRATCH.set(hex);\n return target.set(c.r, c.g, c.b);\n}\n\n/**\n * Renders a gradient \"wave of light\" from a {@link StudioConfig}. Framework-agnostic:\n * it needs only a DOM container and a config. The studio mutates the config in\n * place and calls `refresh()` / `rebuild()`.\n */\nexport class WaveRenderer {\n readonly renderer: THREE.WebGLRenderer;\n protected readonly scene = new THREE.Scene();\n protected readonly camera: THREE.OrthographicCamera;\n protected readonly group = new THREE.Group();\n private readonly composer: EffectComposer;\n private readonly postPass: ShaderPass;\n /** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */\n private bloomPass?: UnrealBloomPass;\n protected readonly container: HTMLElement;\n private readonly respectReducedMotion: boolean;\n private readonly skipIntroRamp: boolean;\n\n protected config: StudioConfig;\n protected waves: Wave[] = [];\n\n // 2D palette textures (+ any palette videos) live per-wave — see WavePalette on each Wave.\n private backgroundTexture?: THREE.Texture;\n private backgroundSig = \"\";\n private backgroundImage?: HTMLImageElement;\n private backgroundImageUrl = \"\";\n private failedBackgroundImageUrl = \"\";\n private backgroundVideo?: HTMLVideoElement;\n private backgroundVideoUrl = \"\";\n private failedBackgroundVideoUrl = \"\";\n private backgroundVideoCanvas?: HTMLCanvasElement;\n /** Authored default camera pose, for \"Reset camera\". */\n protected readonly homeCamPos = new THREE.Vector3();\n protected readonly homeCamTarget = new THREE.Vector3();\n\n // Reused scratch for per-frame clip-plane fitting (see updateClipPlanes) — hoisted so the\n // render loop allocates nothing.\n private readonly clipBox = new THREE.Box3();\n private readonly clipSphere = new THREE.Sphere();\n private readonly clipTmpA = new THREE.Vector3();\n private readonly clipTmpB = new THREE.Vector3();\n\n // ---- Interaction layer (optional; created only when config.interaction is active) ----\n /** Created by syncInteraction() when interaction turns on, disposed when it turns off. */\n protected interaction?: InteractionController;\n /** Extra ortho-zoom MULTIPLIER from a cameraZoom binding (1 = none); applied in applyZoom().\n * Protected so the studio's writeCameraToConfig() can divide it back out (keep it out of config). */\n protected interactionZoom = 1;\n /** Extra time-offset DELTA from a timeOffset binding (0 = none); added in updateTime(). */\n private interactionTimeOffset = 0;\n /** Scene-binding out-params: appliers write into this, applyBindings() reads it back. */\n private readonly interactionSceneOut = { timeOffset: 0, zoom: 1 };\n\n private readonly timer = new THREE.Timer();\n private time = 0;\n private rafId = 0;\n protected running = false;\n private started = false;\n\n private visible = true;\n private pageVisible = true;\n private reducedMotion = false;\n /** Intro ramp: eases animation time 0→1 over ~1s on load (when config.introRamp). */\n private introTimeRamp = 0;\n\n private readonly resizeObserver: ResizeObserver;\n private readonly intersectionObserver: IntersectionObserver;\n private readonly motionQuery: MediaQueryList;\n\n protected capturing = false;\n /** Fixed backing-buffer dimensions used by the studio's visible export frame. Embeds leave\n * this unset and continue to resize responsively with their container and device DPR. */\n private outputSize?: { width: number; height: number };\n\n constructor(container: HTMLElement, config: StudioConfig, options: WaveRendererOptions = {}) {\n this.container = container;\n this.config = ensureStudioConfig(config);\n this.respectReducedMotion = options.respectReducedMotion ?? true;\n this.skipIntroRamp = options.skipIntroRamp ?? false;\n\n this.renderer = new THREE.WebGLRenderer({\n // antialias smooths edges; preserveDrawingBuffer keeps the drawing buffer readable so we\n // can export PNG/WebM. Both cost a little performance, but this authoring tool needs them.\n antialias: true,\n alpha: true,\n preserveDrawingBuffer: true,\n powerPreference: \"high-performance\",\n });\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.renderer.setClearColor(0x000000, 0);\n container.appendChild(this.renderer.domElement);\n\n // Resilience: if the GPU drops the context (memory pressure, sleep/wake), don't\n // let the browser hard-crash the page — prevent the default and rebuild on restore.\n this.renderer.domElement.addEventListener(\"webglcontextlost\", this.onContextLost, false);\n this.renderer.domElement.addEventListener(\n \"webglcontextrestored\",\n this.onContextRestored,\n false,\n );\n\n // Orthographic, framed in device pixels: resize() sets the frustum to the canvas size, and\n // the mesh is scaled up so the wave overflows the frame, leaving only the twist on screen.\n // The left/right/top/bottom bounds here are placeholders overwritten by the first resize();\n // near/far are placeholders too — updateClipPlanes() refits them to the scene every frame so\n // no camera angle ever clips the wave (a fixed slab does — see updateClipPlanes).\n this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 1, 10000);\n this.camera.position.set(\n config.cameraPosition.x,\n config.cameraPosition.y,\n config.cameraPosition.z,\n );\n this.camera.zoom = config.cameraZoom ?? 1;\n this.camera.lookAt(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.camera.updateProjectionMatrix();\n // Remember the authored default pose so \"Reset camera\" returns to it (orbit mutates\n // config.cameraPosition, so we can't read it back from config later).\n this.homeCamPos.copy(this.camera.position);\n this.homeCamTarget.set(config.cameraTarget.x, config.cameraTarget.y, config.cameraTarget.z);\n this.scene.add(this.group);\n\n this.composer = new EffectComposer(this.renderer);\n this.composer.addPass(new RenderPass(this.scene, this.camera));\n this.postPass = new ShaderPass({\n uniforms: {\n tDiffuse: { value: null },\n uResolution: { value: new THREE.Vector2(1, 1) },\n uBlurAmount: { value: config.blur },\n uBlurSamples: { value: 6 },\n uGrainAmount: { value: config.grain },\n uTime: { value: 0 },\n },\n vertexShader: postVertexShader,\n fragmentShader: postFragmentShader,\n });\n this.composer.addPass(this.postPass);\n this.composer.addPass(new OutputPass());\n\n this.motionQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.reducedMotion = this.respectReducedMotion && this.motionQuery.matches;\n this.motionQuery.addEventListener(\"change\", this.onMotionChange);\n document.addEventListener(\"visibilitychange\", this.onVisibilityChange);\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n this.visible = entries[0]?.isIntersecting ?? true;\n this.updateRunning();\n },\n { rootMargin: \"100px\" },\n );\n this.intersectionObserver.observe(container);\n\n this.resizeObserver = new ResizeObserver(this.onResize);\n this.resizeObserver.observe(container);\n\n this.applyBackground();\n this.buildWaves();\n this.resize();\n }\n\n private get segments(): number {\n // Scale detail down as waves multiply, so total geometry stays bounded.\n const q = this.config.quality / Math.sqrt(Math.max(1, this.config.waves.length));\n return THREE.MathUtils.clamp(Math.round(BASE_SEGMENTS * q), 24, 360);\n }\n\n private makeUniforms(): Record<string, THREE.IUniform> {\n const colors: THREE.Vector3[] = [];\n const colorPos: number[] = [];\n for (let i = 0; i < MAX_COLORS; i++) {\n colors.push(new THREE.Vector3(1, 1, 1));\n colorPos.push(MAX_COLORS > 1 ? i / (MAX_COLORS - 1) : 0);\n }\n const meshPointPos: THREE.Vector2[] = [];\n const meshPointColor: THREE.Vector3[] = [];\n const meshPointInfluence: number[] = [];\n for (let i = 0; i < MAX_MESH_POINTS; i++) {\n meshPointPos.push(new THREE.Vector2(0.5, 0.5));\n meshPointColor.push(new THREE.Vector3(1, 1, 1));\n meshPointInfluence.push(0.65);\n }\n const lightPos: THREE.Vector3[] = [];\n const lightColor: THREE.Vector3[] = [];\n const lightIntensity: number[] = [];\n for (let i = 0; i < MAX_LIGHTS; i++) {\n lightPos.push(new THREE.Vector3());\n lightColor.push(new THREE.Vector3(1, 1, 1));\n lightIntensity.push(0);\n }\n const bandBounds: THREE.Vector4[] = [];\n const bandParams: THREE.Vector4[] = [];\n const bandParaPow: number[] = [];\n for (let i = 0; i < MAX_NOISE_BANDS; i++) {\n bandBounds.push(new THREE.Vector4());\n bandParams.push(new THREE.Vector4());\n bandParaPow.push(0);\n }\n // Click-ripple ring buffer (read only under POINTER_RIPPLES). Sized to RIPPLE_SLOTS.\n const rippleOrigin: THREE.Vector2[] = [];\n const rippleAge: number[] = [];\n const rippleAmp: number[] = [];\n for (let i = 0; i < RIPPLE_SLOTS; i++) {\n rippleOrigin.push(new THREE.Vector2());\n rippleAge.push(0);\n rippleAmp.push(0);\n }\n return {\n // Deformation (vertex)\n uTime: { value: 0 },\n uSpeed: { value: 0.05 },\n uSeed: { value: 0 },\n uDispFreqX: { value: 0.003234 },\n uDispFreqZ: { value: 0.00799 },\n uDispAmount: { value: 6.051 },\n uDetailFreq: { value: 0.04 },\n uDetailAmount: { value: 0 }, // 2nd displacement octave (read only under DETAIL_OCTAVE)\n uTwFreqX: { value: -0.055 },\n uTwFreqY: { value: 0.077 },\n uTwFreqZ: { value: -0.518 },\n uTwPowX: { value: 3.95 },\n uTwPowY: { value: 5.85 },\n uTwPowZ: { value: 6.33 },\n uLoopSeconds: { value: 0 }, // seamless-loop period (read only under the LOOP_MOTION define)\n // Colour + light (fragment)\n uColors: { value: colors },\n uColorPos: { value: colorPos },\n uColorCount: { value: 2 },\n uGradType: { value: 0 },\n uGradAngle: { value: 0 },\n uGradShift: { value: 0.15 },\n uMeshPointPos: { value: meshPointPos },\n uMeshPointColor: { value: meshPointColor },\n uMeshPointInfluence: { value: meshPointInfluence },\n uMeshPointCount: { value: 0 },\n uMeshSoftness: { value: 0.62 },\n uPalette: { value: null },\n uUsePalette: { value: 1 },\n uPaletteRaw: { value: 1 },\n uPaletteScale: { value: new THREE.Vector2(1, 1) },\n uPaletteOffset: { value: new THREE.Vector2(0, 0) },\n uPaletteRotation: { value: 0 },\n uDebug: { value: 0 },\n uSheen: { value: 1 },\n uRoundness: { value: 0.35 },\n uIridescence: { value: 0 },\n uDepthTint: { value: 0 }, // solid-theme depth tint (read only under DEPTH_TINT)\n uDepthTintColor: { value: new THREE.Vector3() },\n uHueShift: { value: 0 },\n uContrast: { value: 1 },\n uSaturation: { value: 1 },\n uFiberCount: { value: 90 },\n uFiberStrength: { value: 0.25 },\n uTexture: { value: 0 },\n uCreaseLight: { value: 0.15 },\n uCreaseSharpness: { value: 2.0 },\n uCreaseSoftness: { value: 1.0 },\n uEdgeFade: { value: 0.06 },\n uEdgeFeather: { value: 0.1 }, // ribbon-edge softness (read only under EDGE_FEATHER)\n uOpacity: { value: 1 },\n uSquared: { value: 1 }, // \"squared\" deep-colour mode: square the colour in-shader (see applyBlendMode)\n uResolution: { value: new THREE.Vector2(1, 1) },\n uAmbient: { value: 0.45 },\n uNumLights: { value: 1 },\n uLightPos: { value: lightPos },\n uLightColor: { value: lightColor },\n uLightIntensity: { value: lightIntensity },\n uNumNoiseBands: { value: 0 },\n uNoiseBandBounds: { value: bandBounds },\n uNoiseBandParams: { value: bandParams },\n uNoiseBandParaPow: { value: bandParaPow },\n // Wireframe thin-line theme (used only by lineFragmentShader)\n uLineAmount: { value: 425 },\n uLineThickness: { value: 1 },\n uLineDerivativePower: { value: 0.95 },\n uMaxWidth: { value: 1232 },\n uClearColor: { value: new THREE.Vector3(1, 1, 1) },\n // Interaction / pointer field. ALWAYS present in JS (read only under POINTER_FX /\n // POINTER_RIPPLES); three uploads them only when the compiled program declares them, so their\n // presence never affects a non-interactive wave (byte-identity precedent: uDetailAmount).\n uPointer: { value: new THREE.Vector2(0, 0) }, // smoothed pointer NDC\n uPointerActive: { value: 0 }, // presence ramp × per-wave influence\n uPointerRadius: { value: 0.6 }, // falloff radius in NDC-y (config radius × 2)\n uPointerAspect: { value: 1 }, // drawing-buffer dw/dh\n uPointerAgitate: { value: 0 },\n uPointerPush: { value: 0 }, // signed membrane dome at the cursor (+ repel / − attract)\n uPointerWake: { value: 0 }, // drag-wake trough amplitude\n uPointerVel: { value: new THREE.Vector2(0, 0) }, // smoothed pointer velocity, NDC/s (wake dir)\n uPointerThin: { value: 0 },\n uPointerHue: { value: 0 },\n uPointerLighten: { value: 0 },\n uPointerRipple: { value: 0 }, // this wave's ripple amplitude (scales the shared envelope)\n uRippleOrigin: { value: rippleOrigin },\n uRippleAge: { value: rippleAge },\n uRippleAmp: { value: rippleAmp },\n };\n }\n\n /** Vertex-shader #defines for a wave: TWIST_MOTION (per-wave animated twist wobble) and\n * LOOP_MOTION (scene-level seamless loop). Both select #ifdef-gated code paths; an empty\n * object compiles the default (linear-time) program. */\n private waveDefines(sc: WaveConfig | undefined): Record<string, string> {\n const defines: Record<string, string> = {};\n if (sc?.twistMotion) defines.TWIST_MOTION = \"\";\n if ((this.config.loopSeconds ?? 0) > 0) defines.LOOP_MOTION = \"\";\n // A detailAmount binding on THIS wave also needs the octave compiled (else it no-ops on a wave\n // authored at 0). Only this wave's bindings matter now (bindings are per wave).\n const bindsDetail =\n sc?.interaction?.bindings?.some((b) => b.target === \"detailAmount\") ?? false;\n if ((sc?.detailAmount ?? 0) !== 0 || bindsDetail) defines.DETAIL_OCTAVE = \"\";\n if ((sc?.depthTint ?? 0) > 0) defines.DEPTH_TINT = \"\";\n if ((sc?.edgeFeather ?? 0.1) !== 0.1) defines.EDGE_FEATHER = \"\";\n // Pointer field (per wave, config-only, so input never triggers a recompile). Ripples nest inside.\n if (sc && wavePointerFxActive(this.config, sc)) {\n defines.POINTER_FX = \"\";\n if (waveRipplesActive(this.config, sc)) defines.POINTER_RIPPLES = \"\";\n }\n return defines;\n }\n\n private addWave(): void {\n const geo = new WaveGeometry(this.segments);\n // Initialise defines/fragment/blend from the wave this material will represent, so the\n // first refresh() doesn't force a needless program recompile. Falls back to the first wave.\n const sc = this.config.waves[this.waves.length] ?? this.config.waves[0];\n const material = new THREE.ShaderMaterial({\n uniforms: this.makeUniforms(),\n // TWIST_MOTION / LOOP_MOTION select variant vertex-shader paths. Toggled live in refresh().\n defines: this.waveDefines(sc),\n vertexShader,\n // solid theme = surfaceColor shader; wireframe theme = thin-line shader.\n // Swapped live in refresh() when the wave's theme changes.\n fragmentShader: sc?.theme === \"wireframe\" ? lineFragmentShader : fragmentShader,\n transparent: true,\n depthTest: true,\n depthWrite: true,\n side: THREE.DoubleSide,\n });\n // Blending (incl. the squaring blend) is set from the wave's blendMode — see applyBlendMode —\n // so it survives refresh() instead of being a dead constructor flag.\n this.applyBlendMode(material, sc?.blendMode ?? \"squared\");\n const mesh = new THREE.Mesh(geo.geometry, material);\n mesh.frustumCulled = false;\n this.group.add(mesh);\n const palette = new WavePalette(\n (url) => this.createLoopingVideo(url),\n () => {\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n );\n this.waves.push({ mesh, material, geometry: geo, palette });\n }\n\n /**\n * Apply config.blendMode to a material. \"squared\" (the default) is the hero blend:\n * CustomBlending with AddEquation, src = SrcColorFactor, dst = ZeroFactor, so the\n * framebuffer result is fragColor² — the squaring deepens the colours into the vivid\n * hero look (without it the wave reads pastel). \"additive\"/\"normal\"/\"multiply\" are\n * authoring overrides. Multiply uses Three's premultiplied-alpha path; the custom\n * fragment shaders premultiply their output when Three injects PREMULTIPLIED_ALPHA.\n * Returns true if material state changed (caller flags needsUpdate).\n */\n private applyBlendMode(material: THREE.ShaderMaterial, mode: BlendMode): boolean {\n // \"squared\" is the deep hero look. It used to be a framebuffer-squaring CustomBlending\n // (src·src, dst×0) — which REPLACES the destination rather than compositing over it, so any\n // semi-transparent pixel (soft ribbon edges, and the large near-edge-on regions at oblique\n // camera angles) wiped the framebuffer's colour AND alpha, punching dark / see-through holes\n // through the background and through other waves. On a transparent page you never saw it; on\n // an opaque background or with overlapping waves it's the \"chunks vanish\" artifact.\n //\n // Fix: do the colour-squaring in the shader (uSquared) and composite it with ordinary\n // premultiplied alpha (NormalBlending). Over an opaque body the result is identical (col²);\n // soft edges now blend into what's behind them instead of erasing it.\n const squared = mode === \"squared\";\n const blending =\n mode === \"additive\"\n ? THREE.AdditiveBlending\n : mode === \"multiply\"\n ? THREE.MultiplyBlending\n : THREE.NormalBlending; // \"squared\" (deep, via uSquared) and \"normal\" both composite\n // Premultiplied so the squared/multiply colour composites correctly (the shaders premultiply\n // their output under the PREMULTIPLIED_ALPHA define Three injects for this).\n const premultipliedAlpha = mode === \"multiply\" || squared;\n material.uniforms.uSquared.value = squared ? 1 : 0;\n if (material.blending === blending && material.premultipliedAlpha === premultipliedAlpha) {\n return false;\n }\n material.blending = blending;\n material.premultipliedAlpha = premultipliedAlpha;\n return true;\n }\n\n private disposeWaves(): void {\n for (const s of this.waves) {\n this.group.remove(s.mesh);\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n }\n this.waves = [];\n }\n\n /**\n * Reconcile the wave pool to `waveCount` WITHOUT tearing everything down:\n * keep existing waves (so the compiled shader program is never deleted and\n * re-compiled — that churn can crash some GPU drivers), add/remove only the\n * delta, and resize each geometry to the current quality.\n */\n private buildWaves(): void {\n const target = Math.max(1, this.config.waves.length);\n while (this.waves.length > target) {\n const s = this.waves.pop();\n if (!s) break;\n this.group.remove(s.mesh);\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n }\n while (this.waves.length < target) this.addWave();\n\n const segments = this.segments;\n this.waves.forEach((s, i) => {\n s.geometry.resize(segments);\n s.mesh.renderOrder = i;\n });\n this.refresh();\n }\n\n /** Re-read per-frame-independent values from the (mutated) config. */\n refresh(): void {\n this.applyBackground();\n this.applyPost();\n this.syncInteraction(); // create/dispose the interaction controller as config toggles it\n // Once an external driver (orbit / edit gizmo) owns the camera, don't fight it here; the shell\n // (no orbit) applies the saved camera position/target so it matches the authored view.\n if (!this.isCameraExternallyDriven()) {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n }\n\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const u = wave.material.uniforms;\n if (this.applyBlendMode(wave.material, sc.blendMode)) wave.material.needsUpdate = true;\n // Recompile the program when its #define set changes: TWIST_MOTION / DETAIL_OCTAVE / DEPTH_TINT\n // (per wave) and LOOP_MOTION (scene-level). Compare the whole set so any combination is handled.\n const wantDefines = this.waveDefines(sc);\n const curDefines = wave.material.defines ?? {};\n if (Object.keys(wantDefines).sort().join(\",\") !== Object.keys(curDefines).sort().join(\",\")) {\n wave.material.defines = wantDefines;\n wave.material.needsUpdate = true;\n }\n // Swap the fragment shader when this wave's theme changes: solid surfaceColor <->\n // wireframe thin-line. Three recompiles the program on needsUpdate.\n const wantFrag = sc.theme === \"wireframe\" ? lineFragmentShader : fragmentShader;\n if (wave.material.fragmentShader !== wantFrag) {\n wave.material.fragmentShader = wantFrag;\n wave.material.needsUpdate = true;\n }\n\n const stops = [...sc.palette].sort((a, b) => a.pos - b.pos);\n const colorCount = Math.max(1, Math.min(stops.length, MAX_COLORS));\n const colors = u.uColors.value as THREE.Vector3[];\n const colorPos = u.uColorPos.value as number[];\n for (let c = 0; c < MAX_COLORS; c++) {\n const stop = stops[Math.min(c, colorCount - 1)] ?? { color: \"#ffffff\", pos: 0 };\n hexToLinearVec3(stop.color, colors[c]);\n colorPos[c] = stop.pos;\n }\n u.uColorCount.value = colorCount;\n u.uGradType.value =\n sc.gradientType === \"radial\"\n ? 1\n : sc.gradientType === \"conic\"\n ? 2\n : sc.gradientType === \"mesh\"\n ? 3\n : 0;\n u.uGradAngle.value = ((sc.gradientAngle ?? 0) * Math.PI) / 180;\n u.uGradShift.value = sc.gradientShift ?? 0;\n const meshPoints = sc.meshGradientPoints.slice(0, MAX_MESH_POINTS);\n const meshPointPos = u.uMeshPointPos.value as THREE.Vector2[];\n const meshPointColor = u.uMeshPointColor.value as THREE.Vector3[];\n const meshPointInfluence = u.uMeshPointInfluence.value as number[];\n for (let pointIndex = 0; pointIndex < MAX_MESH_POINTS; pointIndex++) {\n const point = meshPoints[pointIndex] ?? meshPoints[meshPoints.length - 1];\n if (!point) continue;\n meshPointPos[pointIndex].set(point.x, point.y);\n hexToLinearVec3(point.color, meshPointColor[pointIndex]);\n meshPointInfluence[pointIndex] = point.influence;\n }\n u.uMeshPointCount.value = meshPoints.length;\n u.uMeshSoftness.value = sc.meshGradientSoftness;\n u.uPaletteScale.value.set(sc.paletteTextureScale?.x ?? 1, sc.paletteTextureScale?.y ?? 1);\n u.uPaletteOffset.value.set(sc.paletteTextureOffset?.x ?? 0, sc.paletteTextureOffset?.y ?? 0);\n u.uPaletteRotation.value = ((sc.paletteTextureRotation ?? 0) * Math.PI) / 180;\n u.uHueShift.value = sc.hueShift;\n u.uContrast.value = sc.colorContrast;\n u.uSaturation.value = sc.colorSaturation;\n // Wireframe thin-line theme params (used only by lineFragmentShader). uClearColor is the\n // between-line colour = the page background (scene), fed in linear space like the palette.\n u.uLineAmount.value = sc.lineAmount ?? 425;\n u.uLineThickness.value = sc.lineThickness ?? 1;\n u.uLineDerivativePower.value = sc.lineDerivativePower ?? 0.95;\n u.uMaxWidth.value = sc.maxWidth ?? 1232;\n hexToLinearVec3(this.config.background, u.uClearColor.value as THREE.Vector3);\n u.uFiberCount.value = sc.fiberCount;\n u.uFiberStrength.value = sc.fiberStrength;\n u.uTexture.value = sc.texture;\n u.uCreaseLight.value = sc.creaseLight;\n u.uCreaseSharpness.value = sc.creaseSharpness;\n u.uCreaseSoftness.value = sc.creaseSoftness;\n u.uSheen.value = sc.sheen ?? 1;\n u.uRoundness.value = sc.roundness ?? 0.35;\n u.uIridescence.value = sc.iridescence ?? 0;\n u.uDepthTint.value = sc.depthTint ?? 0;\n hexToLinearVec3(sc.depthTintColor ?? \"#0a2540\", u.uDepthTintColor.value as THREE.Vector3);\n u.uEdgeFade.value = sc.edgeFade;\n u.uEdgeFeather.value = sc.edgeFeather ?? 0.1;\n // Lights + ambient are scene-level (shared by every wave).\n const lights = this.config.lights ?? [];\n u.uAmbient.value = this.config.ambient ?? 0.45;\n u.uNumLights.value = Math.min(lights.length, MAX_LIGHTS);\n const lPos = u.uLightPos.value as THREE.Vector3[];\n const lCol = u.uLightColor.value as THREE.Vector3[];\n const lInt = u.uLightIntensity.value as number[];\n for (let li = 0; li < MAX_LIGHTS; li++) {\n const light = lights[li];\n if (light) {\n lPos[li].set(light.position.x, light.position.y, light.position.z);\n hexToLinearVec3(light.color, lCol[li]);\n lInt[li] = light.intensity;\n } else {\n lInt[li] = 0;\n }\n }\n // Noise bands (per-region fiber overrides) — per wave\n const bands = sc.noiseBands ?? [];\n u.uNumNoiseBands.value = Math.min(bands.length, MAX_NOISE_BANDS);\n const bBounds = u.uNoiseBandBounds.value as THREE.Vector4[];\n const bParams = u.uNoiseBandParams.value as THREE.Vector4[];\n const bPara = u.uNoiseBandParaPow.value as number[];\n for (let bi = 0; bi < MAX_NOISE_BANDS; bi++) {\n const band = bands[bi];\n if (band) {\n bBounds[bi].set(band.startX, band.endX, band.startY, band.endY);\n bParams[bi].set(band.feather, band.strength, band.frequency, band.colorAttenuation);\n bPara[bi] = band.parabolaPower;\n }\n }\n // Deformation (absolute per wave)\n u.uSpeed.value = sc.speed;\n u.uSeed.value = sc.seed;\n u.uLoopSeconds.value = this.config.loopSeconds ?? 0; // scene-level; shared by every wave\n u.uDispFreqX.value = sc.displaceFrequency.x;\n u.uDispFreqZ.value = sc.displaceFrequency.y;\n u.uDispAmount.value = sc.displaceAmount;\n u.uDetailFreq.value = sc.detailFrequency ?? 0.04;\n u.uDetailAmount.value = sc.detailAmount ?? 0;\n u.uTwFreqX.value = sc.twistFrequency.x;\n u.uTwFreqY.value = sc.twistFrequency.y;\n u.uTwFreqZ.value = sc.twistFrequency.z;\n u.uTwPowX.value = sc.twistPower.x;\n u.uTwPowY.value = sc.twistPower.y;\n u.uTwPowZ.value = sc.twistPower.z;\n // Mesh transform — each wave's ABSOLUTE scale / rotation / position, applied via\n // modelMatrix using THREE's Euler XYZ order so the on-screen orientation matches the\n // authored view.\n wave.mesh.scale.set(sc.scale.x, sc.scale.y, sc.scale.z);\n wave.mesh.rotation.set(\n THREE.MathUtils.degToRad(sc.rotation.x),\n THREE.MathUtils.degToRad(sc.rotation.y),\n THREE.MathUtils.degToRad(sc.rotation.z),\n );\n wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);\n u.uOpacity.value = sc.opacity;\n });\n\n // Static pointer-field params. The falloff radius is SHARED (scene-level); the hover amplitudes\n // and ripple amplitude are PER WAVE. The dynamic pointer state (position/velocity/presence/ripple\n // envelopes) is pushed each frame in applyPointerField().\n const sharedRadius = (this.config.interaction?.radius ?? 0.3) * 2; // radius = fraction of viewport H\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) return;\n const u = wave.material.uniforms;\n const h = sc.interaction?.hover;\n u.uPointerRadius.value = sharedRadius;\n u.uPointerAgitate.value = h?.agitate ?? 0;\n u.uPointerPush.value = h?.push ?? 0;\n u.uPointerWake.value = h?.wake ?? 0;\n u.uPointerThin.value = h?.thin ?? 0;\n u.uPointerHue.value = h?.hueShift ?? 0;\n u.uPointerLighten.value = h?.lighten ?? 0;\n u.uPointerRipple.value = sc.interaction?.press?.ripple ?? 0;\n });\n\n this.updatePaletteTextures();\n this.syncVideoPlayback();\n\n // Whole-wave mirror (world-space flip ≈ screen flip for the near-frontal camera).\n this.group.scale.set(this.config.mirrorH ? -1 : 1, this.config.mirrorV ? -1 : 1, 1);\n\n this.onAfterRefresh();\n if (!this.running) this.renderOnce();\n }\n\n /** Point every wave's palette sampler at its own WavePalette texture (each rebuilt only\n * when that wave's palette actually changes — see WavePalette.apply). */\n private updatePaletteTextures(): void {\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.apply(sc, wave.material.uniforms);\n });\n }\n\n /** Dev: 0 = normal, 1 = visualise the crease value, 2 = visualise derivative normal. */\n setDebug(v: number): void {\n for (const s of this.waves) s.material.uniforms.uDebug.value = v;\n this.renderOnce();\n }\n\n /** Rebuild geometry + waves (call when waveCount or quality changes). */\n rebuild(): void {\n this.buildWaves();\n }\n\n private applyBackground(): void {\n if (this.config.transparentBackground) {\n this.scene.background = null;\n this.renderer.setClearColor(0x000000, 0);\n return;\n }\n\n const matte = new THREE.Color(this.config.background);\n this.renderer.setClearColor(matte, 1);\n if (this.config.backgroundMode === \"color\") {\n this.applyColorBackground(matte);\n return;\n }\n // Live video is redrawn every frame, so cap its staging canvas near 1080p. Still images\n // and procedural maps retain the full 4K-capable background texture path.\n const { width, height } = this.backgroundCanvasSize(\n this.config.backgroundVideoUrl ? 2048 : 4096,\n );\n if (this.config.backgroundMode === \"gradient\") this.applyGradientBackground(width, height);\n else this.applyImageBackground(matte, width, height);\n }\n\n private applyColorBackground(matte: THREE.Color): void {\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.scene.background = matte;\n }\n\n private applyGradientBackground(width: number, height: number): void {\n this.clearBackgroundVideo();\n const gradType = this.config.backgroundGradientType;\n if (gradType === \"mesh\") {\n const pts = this.config.backgroundMeshPoints ?? [];\n const softness = this.config.backgroundMeshSoftness ?? 0.62;\n const ptSig = pts\n .map((p) => `${p.color}@${p.x.toFixed(3)},${p.y.toFixed(3)},${p.influence.toFixed(3)}`)\n .join(\",\");\n const sig = [\"mesh\", softness.toFixed(3), ptSig, width, height].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundMeshCanvas(pts, softness, width, height);\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n return;\n }\n const source = this.config.backgroundGradientSource ?? \"stops\";\n const def = PALETTE_MAPS[source];\n const stops =\n source !== \"stops\" && def?.kind === \"gradient\" && def.stops\n ? def.stops\n : this.config.backgroundPalette;\n const stopSig = stops.map((stop) => `${stop.color}@${stop.pos.toFixed(3)}`).join(\",\");\n const sig = [\n \"gradient\",\n source,\n gradType,\n this.config.backgroundGradientAngle,\n stopSig,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundGradientCanvas({\n stops,\n type: gradType,\n angle: this.config.backgroundGradientAngle,\n width,\n height,\n });\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n /** Image mode: a live video, a user-loaded image, or a built-in map. `matte` shows while an\n * async source is still loading. */\n private applyImageBackground(matte: THREE.Color, width: number, height: number): void {\n const fit = this.config.backgroundImageFit ?? \"cover\";\n const zoom = this.config.backgroundImageZoom ?? 1;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n const videoUrl = this.config.backgroundVideoUrl;\n const customUrl = this.config.backgroundImageUrl;\n let source: CanvasImageSource;\n let sourceWidth: number;\n let sourceHeight: number;\n let sourceSig: string;\n if (videoUrl) {\n this.ensureBackgroundVideo(videoUrl);\n if (!this.backgroundVideo || this.backgroundVideo.readyState < 2) {\n this.scene.background = matte;\n return;\n }\n source = this.backgroundVideo;\n sourceWidth = this.backgroundVideo.videoWidth;\n sourceHeight = this.backgroundVideo.videoHeight;\n sourceSig = `video|${videoUrl}`;\n } else if (customUrl) {\n this.clearBackgroundVideo();\n if (\n !this.backgroundImage ||\n this.backgroundImageUrl !== customUrl ||\n !this.backgroundImage.complete\n ) {\n this.loadBackgroundImage(customUrl);\n this.scene.background = matte;\n return;\n }\n source = this.backgroundImage;\n sourceWidth = this.backgroundImage.naturalWidth;\n sourceHeight = this.backgroundImage.naturalHeight;\n sourceSig = `custom|${customUrl}`;\n } else {\n this.clearBackgroundVideo();\n const imageSource = this.config.backgroundImageSource ?? \"vaporwave\";\n const canvas =\n imageSource === \"hero\"\n ? buildHeroPaletteCanvas()\n : PALETTE_MAPS[imageSource]?.kind === \"image\"\n ? paletteMapCanvas(PALETTE_MAPS[imageSource], Math.max(width, height))\n : null;\n if (!canvas) {\n this.scene.background = matte;\n return;\n }\n source = canvas;\n sourceWidth = canvas.width;\n sourceHeight = canvas.height;\n sourceSig = `map|${imageSource}`;\n }\n\n const sig = [\n \"image\",\n sourceSig,\n fit,\n zoom,\n position.x,\n position.y,\n this.config.background,\n width,\n height,\n ].join(\"|\");\n if (sig !== this.backgroundSig || !this.backgroundTexture) {\n const canvas = buildBackgroundImageCanvas(\n source,\n sourceWidth,\n sourceHeight,\n width,\n height,\n fit,\n this.config.background,\n zoom,\n position.x,\n position.y,\n );\n this.backgroundTexture?.dispose();\n this.backgroundTexture = canvasToTexture(canvas);\n this.backgroundSig = sig;\n this.backgroundVideoCanvas = videoUrl ? canvas : undefined;\n }\n this.scene.background = this.backgroundTexture;\n }\n\n private backgroundCanvasSize(maxRequestedEdge = 4096): { width: number; height: number } {\n const rawWidth = this.outputSize?.width ?? Math.max(1, this.container.clientWidth);\n const rawHeight = this.outputSize?.height ?? Math.max(1, this.container.clientHeight);\n const maxEdge = Math.min(maxRequestedEdge, this.renderer.capabilities.maxTextureSize);\n const scale = Math.min(1, maxEdge / Math.max(rawWidth, rawHeight));\n return {\n width: Math.max(1, Math.round(rawWidth * scale)),\n height: Math.max(1, Math.round(rawHeight * scale)),\n };\n }\n\n private loadBackgroundImage(url: string): void {\n if (this.backgroundImageUrl === url || this.failedBackgroundImageUrl === url) return;\n this.backgroundImageUrl = url;\n this.backgroundImage = undefined;\n const image = new Image();\n image.decoding = \"async\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) image.crossOrigin = \"anonymous\";\n image.addEventListener(\n \"load\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.backgroundImage = image;\n this.failedBackgroundImageUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundImageUrl !== url) return;\n this.failedBackgroundImageUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n image.src = url;\n }\n\n private ensureBackgroundVideo(url: string): void {\n if (this.backgroundVideoUrl === url || this.failedBackgroundVideoUrl === url) return;\n this.clearBackgroundVideo();\n this.backgroundVideoUrl = url;\n const video = this.createLoopingVideo(url);\n video.addEventListener(\n \"loadeddata\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.backgroundVideo = video;\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundSig = \"\";\n this.applyBackground();\n this.syncVideoPlayback();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n video.addEventListener(\n \"error\",\n () => {\n if (this.config.backgroundVideoUrl !== url) return;\n this.failedBackgroundVideoUrl = url;\n this.backgroundSig = \"\";\n this.applyBackground();\n if (!this.running) this.renderOnce();\n },\n { once: true },\n );\n this.backgroundVideo = video;\n video.load();\n }\n\n private clearBackgroundVideo(): void {\n if (this.backgroundVideo) {\n this.backgroundVideo.pause();\n this.backgroundVideo.removeAttribute(\"src\");\n this.backgroundVideo.load();\n }\n this.backgroundVideo = undefined;\n this.backgroundVideoUrl = \"\";\n this.failedBackgroundVideoUrl = \"\";\n this.backgroundVideoCanvas = undefined;\n }\n\n private createLoopingVideo(url: string): HTMLVideoElement {\n const video = document.createElement(\"video\");\n video.muted = true;\n video.defaultMuted = true;\n video.loop = true;\n video.playsInline = true;\n video.preload = \"auto\";\n if (!url.startsWith(\"data:\") && !url.startsWith(\"blob:\")) video.crossOrigin = \"anonymous\";\n video.src = url;\n return video;\n }\n\n private syncVideoPlayback(): void {\n const backgroundActive =\n this.running &&\n !this.config.transparentBackground &&\n this.config.backgroundMode === \"image\" &&\n !!this.config.backgroundVideoUrl;\n if (this.backgroundVideo) {\n if (backgroundActive) void this.backgroundVideo.play().catch(() => {});\n else this.backgroundVideo.pause();\n }\n // Palette videos are per wave.\n this.waves.forEach((wave, i) => {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n wave.palette.syncPlayback(sc, this.running);\n });\n }\n\n private updateBackgroundVideoFrame(): void {\n const video = this.backgroundVideo;\n const canvas = this.backgroundVideoCanvas;\n if (!video || !canvas || video.readyState < 2 || !this.backgroundTexture) return;\n const position = this.config.backgroundImagePosition ?? { x: 0, y: 0 };\n drawBackgroundMediaFrame(\n canvas,\n video,\n video.videoWidth,\n video.videoHeight,\n this.config.backgroundImageFit ?? \"cover\",\n this.config.background,\n this.config.backgroundImageZoom ?? 1,\n position.x,\n position.y,\n );\n this.backgroundTexture.needsUpdate = true;\n }\n\n private applyPost(): void {\n const u = this.postPass.uniforms;\n u.uBlurAmount.value = this.config.blur;\n u.uGrainAmount.value = this.config.grain;\n u.uBlurSamples.value = Math.round(this.config.blurSamples ?? 6);\n this.applyBloom();\n }\n\n /** Insert / tune / remove the bloom pass. strength 0 removes it from the composer entirely, so\n * cost and pixels are identical to bloom-off; the pass (and its mip-chain render targets) is\n * created lazily the first time bloom is enabled and disposed when turned back off. It sits\n * right after the scene RenderPass so it blooms the wave before the grain/blur pass. */\n private applyBloom(): void {\n const strength = this.config.bloomStrength ?? 0;\n if (strength > 0) {\n if (!this.bloomPass) {\n const size = this.renderer.getDrawingBufferSize(new THREE.Vector2());\n this.bloomPass = new UnrealBloomPass(\n size,\n strength,\n this.config.bloomRadius ?? 0.4,\n this.config.bloomThreshold ?? 0.85,\n );\n this.composer.insertPass(this.bloomPass, 1); // after RenderPass, before postPass/OutputPass\n }\n this.bloomPass.strength = strength;\n this.bloomPass.radius = this.config.bloomRadius ?? 0.4;\n this.bloomPass.threshold = this.config.bloomThreshold ?? 0.85;\n } else if (this.bloomPass) {\n this.composer.removePass(this.bloomPass);\n this.bloomPass.dispose();\n this.bloomPass = undefined;\n }\n }\n\n private onResize = (): void => {\n this.resize();\n };\n\n private onContextLost = (e: Event): void => {\n e.preventDefault(); // tell the browser we'll recover → no \"Aw, Snap\" crash\n cancelAnimationFrame(this.rafId);\n this.running = false;\n };\n\n private onContextRestored = (): void => {\n this.disposeWaves(); // old GPU resources are invalid on a fresh context (per-wave palettes too)\n this.backgroundTexture?.dispose();\n this.backgroundTexture = undefined;\n this.backgroundSig = \"\";\n this.buildWaves();\n this.resize();\n this.updateRunning();\n };\n\n resize(): void {\n const w = this.outputSize?.width ?? Math.max(1, this.container.clientWidth);\n const h = this.outputSize?.height ?? Math.max(1, this.container.clientHeight);\n const dpr = this.outputSize ? 1 : Math.min(window.devicePixelRatio || 1, this.config.dprMax);\n this.renderer.setPixelRatio(dpr);\n this.renderer.setSize(w, h, !this.outputSize);\n if (this.outputSize) {\n // setSize(..., false) preserves the exact backing buffer without writing fixed CSS\n // pixel dimensions. Keep the canvas stretched to the visible aspect-ratio frame.\n this.renderer.domElement.style.width = \"100%\";\n this.renderer.domElement.style.height = \"100%\";\n }\n this.composer.setPixelRatio(dpr);\n this.composer.setSize(w, h);\n const dw = w * dpr;\n const dh = h * dpr;\n (this.postPass.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n for (const s of this.waves) {\n (s.material.uniforms.uResolution.value as THREE.Vector2).set(dw, dh);\n }\n // The responsive ortho framing: the frustum = the canvas in DEVICE pixels (1 world unit =\n // 1px at zoom 1). Combined with the ×10 mesh scale, the wave overflows the frame.\n this.camera.left = -dw / 2;\n this.camera.right = dw / 2;\n this.camera.top = dh / 2;\n this.camera.bottom = -dh / 2;\n this.applyZoom(); // responsive ortho zoom (maps FRAME_W world units onto the canvas)\n this.applyBackground();\n this.onAfterResize();\n if (!this.running) this.renderOnce();\n }\n\n /** Set an exact output buffer while CSS scales the canvas into the on-screen export frame. */\n setOutputSize(width: number, height: number): void {\n this.outputSize = {\n width: THREE.MathUtils.clamp(Math.round(width), 64, 8192),\n height: THREE.MathUtils.clamp(Math.round(height), 64, 8192),\n };\n this.resize();\n }\n\n start(): void {\n this.started = true;\n this.updateRunning();\n }\n\n stop(): void {\n this.started = false;\n this.updateRunning();\n }\n\n private onMotionChange = (e: MediaQueryListEvent): void => {\n this.reducedMotion = this.respectReducedMotion && e.matches;\n this.updateRunning();\n };\n\n private onVisibilityChange = (): void => {\n this.pageVisible = document.visibilityState === \"visible\";\n this.updateRunning();\n };\n\n private updateRunning(): void {\n const shouldAnimate =\n this.started &&\n this.visible &&\n this.pageVisible &&\n !this.config.paused &&\n !this.reducedMotion;\n\n if (shouldAnimate && !this.running) {\n this.running = true;\n // Reset the delta baseline (as Clock.start() + a discarded getDelta() used to) so the first\n // frame advances by ~one frame, not by the whole idle/pause gap that elapsed while stopped.\n this.timer.update();\n this.rafId = requestAnimationFrame(this.loop);\n } else if (!shouldAnimate && this.running) {\n this.running = false;\n cancelAnimationFrame(this.rafId);\n }\n // When not animating (paused / reduced-motion / static export) show the FULL frame, not a\n // frozen mid-ease, by forcing introTimeRamp = 1.\n if (!this.running) {\n this.introTimeRamp = 1;\n this.interaction?.settle(); // collapse pointer/input to rest before the one settled frame\n this.renderOnce();\n }\n this.syncVideoPlayback();\n }\n\n private loop = (): void => {\n if (!this.running) return;\n this.timer.update();\n const dt = this.timer.getDelta();\n this.time += dt;\n this.interaction?.update(dt); // advance smoothed input by the SAME delta (no time-model change)\n if (this.introTimeRamp < 1) this.introTimeRamp = Math.min(1, this.introTimeRamp + 0.016); // ~1s to full at 60fps\n this.renderOnce();\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n /** Advance the per-frame time uniforms (geometry itself is static). Time model:\n * time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */\n private updateTime(): void {\n // Skip the ease-in when asked: a fresh renderer (first load, or the studio's HMR hot-swap)\n // starts introTimeRamp at 0, and replaying the ramp on every save reads as a \"speed up\" when\n // you tab back. The studio passes skipIntroRamp in dev; prod builds + the embed keep the ease-in.\n const ramp = this.config.introRamp === false || this.skipIntroRamp ? 1 : this.introTimeRamp;\n const t = this.time * ramp + (this.config.timeOffset ?? 0) + this.interactionTimeOffset;\n // Indexed loop (no per-frame closure) — this runs every frame.\n for (let i = 0; i < this.waves.length; i++) {\n const u = this.waves[i].material.uniforms;\n u.uTime.value = t;\n // Palette drift: flow the colour along the ribbon independently of the geometry by drifting\n // uPaletteOffset over time. Only touch it when nonzero, so refresh()'s static base offset —\n // and every drift-off preset — is left byte-for-byte unchanged.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n const dx = sc.paletteDriftX ?? 0;\n const dy = sc.paletteDriftY ?? 0;\n if (dx !== 0 || dy !== 0) {\n const base = sc.paletteTextureOffset;\n (u.uPaletteOffset.value as THREE.Vector2).set(base.x + dx * t, base.y + dy * t);\n }\n }\n this.postPass.uniforms.uTime.value = t;\n }\n\n /** Render exactly one frame at the current time. */\n renderOnce(): void {\n this.updateBackgroundVideoFrame();\n this.updateTime();\n this.applyInteraction(); // write pointer + binding uniforms (no-op when off / capturing)\n this.updateClipPlanes(); // keep near/far bracketing the scene so no camera angle clips the wave\n this.composer.render();\n // Editor overlays (gizmo/helpers + camera-rig minimap) draw on top of the composed frame —\n // the studio subclass plugs them in here; the base renders nothing extra.\n this.onAfterRenderFrame();\n }\n\n /** Create/dispose the interaction controller as config toggles interaction on/off. Called from\n * refresh(); the compiled define set (POINTER_FX etc.) is handled separately by waveDefines(). */\n private syncInteraction(): void {\n const active = interactionActive(this.config);\n if (active && !this.interaction) {\n this.interaction = new InteractionController(this.container, () => this.config);\n } else if (!active && this.interaction) {\n this.interaction.dispose();\n this.interaction = undefined;\n this.interactionTimeOffset = 0;\n // Clear any live scroll→cameraZoom multiplier left in camera.zoom: with the controller gone\n // applyBindings won't run to reset it, so recompute the zoom here (no-op when already 1).\n if (this.interactionZoom !== 1) {\n this.interactionZoom = 1;\n this.applyZoom();\n }\n }\n }\n\n /** Per-frame interaction write: dynamic pointer-field uniforms + bindings. No-op without a\n * controller. While capturing it writes the REST state instead (pointer field zeroed, every bound\n * param at its authored base) — merely skipping the write would freeze whatever live hover/scroll\n * state the previous frame left in the uniforms, so exports wouldn't be deterministic. */\n private applyInteraction(): void {\n if (!this.interaction) return;\n if (this.capturing) {\n this.applyInteractionRest();\n return;\n }\n if (anyPointerFxActive(this.config)) this.applyPointerField(this.interaction);\n this.applyBindings(this.interaction);\n }\n\n /** Write the capture-frame interaction state: exactly what this config renders with no input —\n * pointer presence + ripple envelopes zeroed (vPointerFall gates every hover effect to 0) and each\n * bound param at its authored base. Live controller state is left untouched, so the frame after\n * the capture resumes mid-gesture; the trailing renderOnce() in captureImage restores the preview.\n * interactionZoom is deliberately NOT reset — captureImage strips it from camera.zoom itself, and\n * the post-capture restore depends on it being unchanged. */\n private applyInteractionRest(): void {\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) continue;\n const u = this.waves[i].material.uniforms;\n u.uPointerActive.value = 0;\n const rAmp = u.uRippleAmp.value as number[];\n for (let r = 0; r < RIPPLE_SLOTS; r++) rAmp[r] = 0;\n }\n // Scene bindings → authored base: blur/grain write straight to the post uniforms; the time\n // offset delta is simply 0 at base. (Live applyBindings recomputes all of these next frame.)\n const sceneBindings = this.config.interaction?.bindings;\n if (sceneBindings) {\n this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;\n this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;\n const sceneArgs = { post: this.postPass.uniforms, out: this.interactionSceneOut };\n for (const b of sceneBindings) {\n const applier = SCENE_APPLIERS[b.target];\n applier.apply(applier.base(this.config), sceneArgs);\n }\n }\n if (this.interactionTimeOffset !== 0) {\n // renderOnce ran updateTime() BEFORE this, so uTime already carries the live offset for the\n // frame about to draw — zero it and re-run updateTime so the capture uses the authored time.\n this.interactionTimeOffset = 0;\n this.updateTime();\n }\n // Per-wave bindings → that wave's authored base.\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i];\n const bindings = sc?.interaction?.bindings;\n if (!sc || !bindings || bindings.length === 0) continue;\n const wave = this.waves[i];\n for (const b of bindings) {\n const applier = WAVE_APPLIERS[b.target];\n applier.apply(applier.base(sc), { u: wave.material.uniforms, mesh: wave.mesh });\n }\n }\n }\n\n /** Write the dynamic pointer-field uniforms to every wave that HAS a pointer field. Position /\n * presence are PER WAVE (each trails the cursor at its own hover smoothing); ripple origins/ages\n * are shared. Per-wave amplitudes were already pushed statically in refresh(). */\n private applyPointerField(ic: InteractionController): void {\n const dw = this.camera.right - this.camera.left;\n const dh = this.camera.top - this.camera.bottom;\n const aspect = dh !== 0 ? dw / dh : 1;\n const ripples = ic.sample().ripples;\n // Velocity-driven agitation: the configured `agitate` is the full-gesture strength; when the\n // cursor is still the drive drops to a low idle floor, so the churn tracks how you move instead\n // of buzzing at a constant rate the whole time the cursor is merely present.\n const AGITATE_IDLE = 0.2; // fraction of `agitate` still shown when the cursor holds still\n const AGITATE_GAIN = 0.85; // how much cursor speed adds back on top (capped at full strength)\n const agitateDrive = Math.min(1, AGITATE_IDLE + AGITATE_GAIN * ic.pointerFlux());\n const vel = ic.pointerVelocity();\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n if (!wavePointerFxActive(this.config, sc)) continue;\n const u = this.waves[i].material.uniforms;\n u.uPointerAgitate.value = (sc.interaction?.hover?.agitate ?? 0) * agitateDrive;\n (u.uPointerVel.value as THREE.Vector2).copy(vel);\n const f = ic.fieldFor(i);\n if (f) {\n (u.uPointer.value as THREE.Vector2).copy(f.ndc);\n u.uPointerActive.value = f.presence;\n } else {\n u.uPointerActive.value = 0; // wave not advanced by update() yet → rest (no hover)\n }\n u.uPointerAspect.value = aspect;\n const rOrigin = u.uRippleOrigin.value as THREE.Vector2[];\n const rAge = u.uRippleAge.value as number[];\n const rAmp = u.uRippleAmp.value as number[];\n for (let r = 0; r < RIPPLE_SLOTS; r++) {\n rOrigin[r].copy(ripples[r].origin);\n rAge[r] = ripples[r].age;\n rAmp[r] = ripples[r].amp;\n }\n }\n }\n\n /** Evaluate bindings via the applier tables: value = mix(from ?? base, to, smoothedSource). Scene\n * bindings drive scene params; each wave's bindings drive that wave's uniforms. */\n private applyBindings(ic: InteractionController): void {\n // Scene bindings → scene params. Seed out-params at base; appliers overwrite only what they drive,\n // so with no scene binding these stay at base → interactionTimeOffset 0 / interactionZoom 1.\n this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;\n this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;\n const sceneArgs = { post: this.postPass.uniforms, out: this.interactionSceneOut };\n for (const b of this.config.interaction?.bindings ?? []) {\n const applier = SCENE_APPLIERS[b.target];\n const value = THREE.MathUtils.lerp(\n b.from ?? applier.base(this.config),\n b.to,\n ic.bindingValue(b),\n );\n applier.apply(value, sceneArgs);\n }\n // Per-wave bindings → that wave's uniforms / mesh.\n for (let i = 0; i < this.waves.length; i++) {\n const sc = this.config.waves[i];\n const bindings = sc?.interaction?.bindings;\n if (!sc || !bindings || bindings.length === 0) continue;\n const wave = this.waves[i];\n for (const b of bindings) {\n const applier = WAVE_APPLIERS[b.target];\n const value = THREE.MathUtils.lerp(b.from ?? applier.base(sc), b.to, ic.bindingValue(b));\n applier.apply(value, { u: wave.material.uniforms, mesh: wave.mesh });\n }\n }\n // interactionTimeOffset is the DELTA over config.timeOffset (updateTime adds both together).\n this.interactionTimeOffset =\n this.interactionSceneOut.timeOffset - (this.config.timeOffset ?? 0);\n // interactionZoom is a MULTIPLIER over config.cameraZoom (applyZoom multiplies both). This runs\n // even in the studio (where orbit owns the camera) so a scroll→cameraZoom reaction previews:\n // applyZoom only re-fires when the multiplier actually CHANGES (i.e. you scrub the scroll\n // preview), so it never fights an idle orbit; writeCameraToConfig divides it back out so it can't\n // bake into the saved/exported config, and captureImage strips it so posters use the authored zoom.\n const nextZoom = this.interactionSceneOut.zoom / (this.config.cameraZoom || 1);\n if (nextZoom !== this.interactionZoom) {\n this.interactionZoom = nextZoom;\n this.applyZoom();\n }\n }\n\n /** Feed a `custom:<name>` interaction input (developer API). No-op when interaction is off. */\n setInteractionInput(name: string, value: number): void {\n this.interaction?.setInput(name, value);\n }\n\n /** Re-evaluate play/pause after `config.paused` changes. */\n refreshPlayback(): void {\n this.updateRunning();\n }\n\n /** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /\n * cameraZoom). Called on whole-config swaps (preset / reset / randomize / import). The base\n * applies the pose directly; the studio subclass overrides it to also drive the orbit target\n * and sync the panel. Hook ⑤. */\n protected applyCameraFromConfig(): void {\n const p = this.config.cameraPosition;\n const tg = this.config.cameraTarget;\n this.camera.position.set(p.x, p.y, p.z);\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(tg.x, tg.y, tg.z);\n this.applyZoom();\n if (!this.running) this.renderOnce();\n }\n\n // ---- Editor hook points (no-ops in the base; the studio subclass overrides them) ----\n\n /** Hook ①: true while orbit or an edit gizmo owns the camera, so refresh() won't reset it. */\n protected isCameraExternallyDriven(): boolean {\n return false;\n }\n\n /** Hook ②: called at the end of refresh(), before the trailing renderOnce(). */\n protected onAfterRefresh(): void {}\n\n /** Hook ③: called at the end of renderOnce(), after the composed frame is drawn. */\n protected onAfterRenderFrame(): void {}\n\n /** Hook ④: called at the end of resize(), before the trailing renderOnce(). */\n protected onAfterResize(): void {}\n\n /** Responsive ortho zoom: COVER the FRAME_W × FRAME_H reference frame onto the canvas so the\n * wave frames the same at any size/aspect/dpr (only the cropped margin differs), times the\n * user's cameraZoom. `max(...)` = cover (fill both axes, crop overflow); `min(...)` would be\n * contain (fit with letterbox bands). Cover keeps the wave filling the frame on every screen. */\n protected applyZoom(): void {\n const dw = this.camera.right - this.camera.left; // device px (set in resize)\n const dh = this.camera.top - this.camera.bottom;\n // A cameraZoom binding multiplies in here. It stays 1 unless a binding drives it; when one does,\n // writeCameraToConfig divides it back out (so the studio's orbit-persisted config isn't polluted)\n // and captureImage strips it from camera.zoom (so exports use the authored framing).\n this.camera.zoom =\n Math.max(dw / FRAME_W, dh / FRAME_H) * (this.config.cameraZoom ?? 1) * this.interactionZoom;\n this.camera.updateProjectionMatrix();\n }\n\n /** Fit the orthographic near/far planes to the scene before every render, so no part of a wave\n * is ever clipped as the camera orbits / dollies / pans (or when waves are added or scaled).\n *\n * The camera is *constructed* with fixed 1..10000 planes that only suit the authored hero\n * framing; once the view moves, the wave's depth extent along the view axis easily crosses\n * them and the GPU hard-slices the geometry along a flat plane — chunks of the wave vanish.\n *\n * The in-shader twist rotates each vertex about its LOCAL origin, so it can never move a vertex\n * further from that origin than the base geometry already sits. A sphere at each mesh's origin\n * (radius = base extent × the mesh's largest world scale, plus the Y displacement, ×1.2 slack)\n * therefore safely contains the fully-deformed wave. We union those, then bracket the union\n * along the view axis with a margin. Bracketing to the scene (rather than a fixed huge slab)\n * keeps clip-space depth scene-normalised — so the wireframe theme's depth fade, which reads\n * gl_Position.z, looks the same at any camera distance instead of washing out. Runs each frame;\n * it's a handful of vector ops over 1–8 meshes with no allocation. */\n private updateClipPlanes(): void {\n this.clipBox.makeEmpty();\n for (let i = 0; i < this.waves.length; i++) {\n const wave = this.waves[i];\n const mesh = wave.mesh;\n mesh.updateWorldMatrix(true, false);\n const bs = mesh.geometry.boundingSphere;\n if (!bs) continue;\n // The twist pivot (the mesh's local origin) in world space = the safe sphere's centre.\n const center = this.clipTmpA.setFromMatrixPosition(mesh.matrixWorld);\n const disp = Math.abs(Number(wave.material.uniforms.uDispAmount.value) || 0);\n // Extra wave-local displacement THIS wave's pointer field can add (agitation + push/wake +\n // ripple — worst case they all stack at the cursor), so the deformed surface never crosses the\n // fitted near/far planes. 0 when off → byte-identical.\n const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];\n let pointerDisp = 0;\n if (wavePointerFxActive(this.config, sc)) {\n const h = sc.interaction?.hover;\n pointerDisp =\n (h?.agitate ?? 0) +\n Math.abs(h?.push ?? 0) +\n (h?.wake ?? 0) +\n (sc.interaction?.press?.ripple ?? 0);\n }\n const localRadius = bs.center.length() + bs.radius + disp + pointerDisp;\n const radius = localRadius * mesh.matrixWorld.getMaxScaleOnAxis() * 1.2;\n // Enclose this wave's sphere by adding its axis-aligned bounding cube corners.\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(radius));\n this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(-radius));\n }\n if (this.clipBox.isEmpty()) return;\n this.clipBox.getBoundingSphere(this.clipSphere);\n const viewDir = this.camera.getWorldDirection(this.clipTmpA); // normalised view axis\n const centerDepth = this.clipTmpB\n .copy(this.clipSphere.center)\n .sub(this.camera.position)\n .dot(viewDir);\n const radius = this.clipSphere.radius;\n const margin = radius * 0.25 + 10;\n // Orthographic: a negative near is legal — the slab may extend behind the camera origin.\n this.camera.near = centerDepth - radius - margin;\n this.camera.far = centerDepth + radius + margin;\n this.camera.updateProjectionMatrix();\n }\n\n /** A world-space position delta that drops a duplicated wave into open frame space beside the\n * one it was copied from — screen-left and a touch down, sized to the visible frame — instead\n * of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed\n * off-frame. Camera-relative: it's \"left on screen\" no matter how the view is rotated/zoomed,\n * because it's built from the camera's right/up axes and the frame's visible world size. */\n\n async captureImage(\n mime: string,\n transparent = true,\n quality?: number,\n time?: number,\n ): Promise<Blob> {\n const prevBg = this.config.transparentBackground;\n if (transparent !== prevBg) {\n this.config.transparentBackground = transparent;\n this.applyBackground();\n }\n // Deterministic frame: render at a fixed animation-time (with the ease-in ramp forced full) so\n // a captured poster is reproducible instead of whatever frame happened to be on screen — pass\n // time = 0 for the frame the wave opens on. Restored in `finally`. Undefined = the live frame.\n const fixTime = time !== undefined;\n const prevTime = this.time;\n const prevRamp = this.introTimeRamp;\n if (fixTime) {\n this.time = time;\n this.introTimeRamp = 1;\n }\n let blob: Blob | null = null;\n // Exports use the AUTHORED framing — strip any live scroll→cameraZoom multiplier from camera.zoom\n // for the capture render (config itself is never polluted, so code/embed exports are already\n // clean; this covers the pixel capture). The trailing renderOnce() restores the live preview.\n const prevZoom = this.camera.zoom;\n const strippingZoom = this.interactionZoom !== 1;\n try {\n this.capturing = true;\n if (strippingZoom) {\n this.camera.zoom = prevZoom / this.interactionZoom;\n this.camera.updateProjectionMatrix();\n }\n this.renderOnce();\n blob = await new Promise<Blob | null>((resolve) =>\n this.canvas.toBlob(resolve, mime, quality),\n );\n } finally {\n this.capturing = false;\n if (strippingZoom) {\n this.camera.zoom = prevZoom;\n this.camera.updateProjectionMatrix();\n }\n if (transparent !== prevBg) {\n this.config.transparentBackground = prevBg;\n this.applyBackground();\n }\n if (fixTime) {\n this.time = prevTime;\n this.introTimeRamp = prevRamp;\n }\n this.renderOnce();\n }\n if (!blob || blob.type !== mime) throw new Error(`Failed to capture ${mime}`);\n return blob;\n }\n\n captureStream(fps = 60): MediaStream {\n return this.canvas.captureStream(fps);\n }\n\n get canvas(): HTMLCanvasElement {\n return this.renderer.domElement;\n }\n\n getConfig(): StudioConfig {\n return this.config;\n }\n\n setConfig(config: StudioConfig): void {\n const next = ensureStudioConfig(config);\n const structural =\n next.waves.length !== this.waves.length || next.quality !== this.config.quality;\n this.config = next;\n if (structural) this.rebuild();\n else this.refresh();\n // A whole new config (preset/reset/randomize/import) carries its own authored framing —\n // apply it even in the studio, where refresh() leaves the camera to orbit. Without this,\n // selecting a preset updated the wave but kept the previous camera (wrong framing).\n this.applyCameraFromConfig();\n }\n\n dispose(): void {\n cancelAnimationFrame(this.rafId);\n this.running = false;\n this.interaction?.dispose();\n this.interaction = undefined;\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.motionQuery.removeEventListener(\"change\", this.onMotionChange);\n document.removeEventListener(\"visibilitychange\", this.onVisibilityChange);\n this.renderer.domElement.removeEventListener(\"webglcontextlost\", this.onContextLost);\n this.renderer.domElement.removeEventListener(\"webglcontextrestored\", this.onContextRestored);\n this.clearBackgroundVideo();\n this.backgroundTexture?.dispose();\n for (const s of this.waves) {\n s.material.dispose();\n s.geometry.dispose();\n s.palette.dispose();\n }\n this.bloomPass?.dispose();\n this.composer.dispose();\n this.renderer.dispose();\n this.renderer.domElement.remove();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { SceneInteractionBinding, StudioConfig, WaveInteractionBinding } from "../config/model.js";
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/interaction.d.ts
|
|
5
|
+
type AnyBinding = WaveInteractionBinding | SceneInteractionBinding;
|
|
6
|
+
/** What a wave-scoped applier writes into: one wave's uniforms + its mesh transform. */
|
|
7
|
+
interface RippleSlot {
|
|
8
|
+
origin: THREE.Vector2;
|
|
9
|
+
age: number;
|
|
10
|
+
amp: number;
|
|
11
|
+
}
|
|
12
|
+
/** A per-frame snapshot of the pointer-field state. Fields are LIVE references into the controller's
|
|
13
|
+
* state — read them synchronously each frame; don't retain them. */
|
|
14
|
+
interface InteractionSample {
|
|
15
|
+
/** Smoothed pointer position, NDC (-1..1). */
|
|
16
|
+
ndc: THREE.Vector2;
|
|
17
|
+
/** Smoothed pointer presence 0..1 (→ uPointerActive). */
|
|
18
|
+
presence: number;
|
|
19
|
+
/** Click-ripple ring buffer (amp = shared 0..1 envelope; 0 = free slot). */
|
|
20
|
+
ripples: readonly RippleSlot[];
|
|
21
|
+
}
|
|
22
|
+
/** A wave's own smoothed pointer-field state — trails the shared cursor at the wave's own rate. */
|
|
23
|
+
interface PointerField {
|
|
24
|
+
/** Smoothed pointer position for this wave, NDC (-1..1). */
|
|
25
|
+
ndc: THREE.Vector2;
|
|
26
|
+
/** Spring velocity of `ndc` (NDC/s) — internal spring state, not read by the renderer. */
|
|
27
|
+
vel: THREE.Vector2;
|
|
28
|
+
/** Smoothed pointer presence 0..1 for this wave. */
|
|
29
|
+
presence: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Owns the one cursor's input + scroll + press/appear/custom and all smoothing. Constructed by the
|
|
33
|
+
* renderer when {@link interactionActive} first turns true, disposed when it turns false. All
|
|
34
|
+
* listeners are passive and container-scoped (the poster overlay passes events through).
|
|
35
|
+
*/
|
|
36
|
+
declare class InteractionController {
|
|
37
|
+
private readonly container;
|
|
38
|
+
private readonly cfg;
|
|
39
|
+
/** Studio-only scroll preview: when non-null, overrides the computed scroll progress. */
|
|
40
|
+
scrollOverride: number | null;
|
|
41
|
+
private readonly ndc;
|
|
42
|
+
private readonly ndcTarget;
|
|
43
|
+
private readonly ndcPrev;
|
|
44
|
+
private readonly velNdc;
|
|
45
|
+
private presence;
|
|
46
|
+
private presenceTarget;
|
|
47
|
+
private press;
|
|
48
|
+
private pressTarget;
|
|
49
|
+
private pointerSpeed;
|
|
50
|
+
private scroll;
|
|
51
|
+
private scrollPrev;
|
|
52
|
+
private scrollVel;
|
|
53
|
+
private appearLatched;
|
|
54
|
+
private readonly customInputs;
|
|
55
|
+
private readonly ripples;
|
|
56
|
+
private readonly fields;
|
|
57
|
+
private readonly bindingState;
|
|
58
|
+
private readonly seenBindings;
|
|
59
|
+
private readonly out;
|
|
60
|
+
constructor(container: HTMLElement, cfg: () => StudioConfig | undefined);
|
|
61
|
+
/** Ignore coarse (touch) pointers unless the scene opts in with interaction.touch. */
|
|
62
|
+
private ignore;
|
|
63
|
+
private setNdcTarget;
|
|
64
|
+
private onPointerEnter;
|
|
65
|
+
private onPointerMove;
|
|
66
|
+
private onPointerLeave;
|
|
67
|
+
private onPointerCancel;
|
|
68
|
+
private onPointerDown;
|
|
69
|
+
private onPointerUp;
|
|
70
|
+
/** Spawn a normalized ripple (envelope 0..1) at the click NDC; per-wave amplitude scales it in the
|
|
71
|
+
* shader. Reuses a free slot or evicts the oldest. */
|
|
72
|
+
private spawnRipple;
|
|
73
|
+
/** Advance all smoothed state by `dt` seconds. Called from the render loop with the same delta. */
|
|
74
|
+
update(dt: number): void;
|
|
75
|
+
private updateBindings;
|
|
76
|
+
/** Advance one binding's smoothed source value by `dt` and mark it live in `seenBindings`. */
|
|
77
|
+
private advanceBinding;
|
|
78
|
+
/** The current smoothed 0..1 value of a binding's source (0 if the binding is unknown). */
|
|
79
|
+
bindingValue(b: AnyBinding): number;
|
|
80
|
+
/** The current raw (un-per-binding-smoothed) 0..1 value of a source signal. */
|
|
81
|
+
private rawSource;
|
|
82
|
+
/** Container progress through the viewport: 0 as it enters from below, 1 once scrolled past. */
|
|
83
|
+
private computeScroll;
|
|
84
|
+
/** The shared pointer-field state + ripples for the renderer (live references — read synchronously). */
|
|
85
|
+
sample(): InteractionSample;
|
|
86
|
+
/** This wave's smoothed pointer-field state (it trails the cursor at its own hover smoothing), or
|
|
87
|
+
* null if the wave hasn't been advanced by update() yet (treat as rest). */
|
|
88
|
+
fieldFor(waveIdx: number): PointerField | null;
|
|
89
|
+
/** Velocity-driven agitation drive 0..1 (how fast the cursor is moving, presence-gated). The
|
|
90
|
+
* renderer scales each wave's hover `agitate` by this, so the churn tracks the gesture instead
|
|
91
|
+
* of buzzing at a constant rate whenever the cursor is merely present. */
|
|
92
|
+
pointerFlux(): number;
|
|
93
|
+
/** Smoothed pointer velocity, NDC/s (direction + speed of the drag). The renderer feeds it to the
|
|
94
|
+
* drag-wake shader so the trailing trough forms behind the motion. Live reference — read per frame. */
|
|
95
|
+
pointerVelocity(): THREE.Vector2;
|
|
96
|
+
/** Feed a `custom:<name>` input (developer API; staged/forwarded by the shell). */
|
|
97
|
+
setInput(name: string, value: number): void;
|
|
98
|
+
/**
|
|
99
|
+
* Collapse to the settled resting state for the single frame drawn when the loop stops (paused /
|
|
100
|
+
* reduced-motion / offscreen): presence / velocity / press / pointerSpeed → 0, ripples cleared,
|
|
101
|
+
* scroll → its current raw value, pointer → centre, and `appear` → 1 (reduced-motion users must
|
|
102
|
+
* see the FINAL entered state). Custom inputs KEEP their last explicit values. Each binding snaps
|
|
103
|
+
* to its settled source so the one settled frame shows the final look.
|
|
104
|
+
*/
|
|
105
|
+
settle(): void;
|
|
106
|
+
/**
|
|
107
|
+
* Snap scroll progress + the scroll-sourced bindings to the current override at once, leaving
|
|
108
|
+
* every other input (pointer / press / appear / velocity / custom) advancing live. Used by the
|
|
109
|
+
* studio scroll preview: the studio page never really scrolls, so dragging the preview slider is a
|
|
110
|
+
* manual scrub that must reflect the instant you move it — not on the next animation frame, which
|
|
111
|
+
* the browser fully suspends whenever the tab isn't foreground. Unlike settle() (which collapses
|
|
112
|
+
* ALL input to rest for a paused still frame), this touches only the scroll signal.
|
|
113
|
+
*/
|
|
114
|
+
snapScroll(): void;
|
|
115
|
+
dispose(): void;
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { InteractionController, InteractionSample, PointerField };
|
|
119
|
+
//# sourceMappingURL=interaction.d.ts.map
|