@lovo/matter 0.6.0 → 2.0.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/CHANGELOG.md +37 -6
- package/dist/index.cjs +46 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -15
- package/dist/index.d.ts +33 -15
- package/dist/index.js +46 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/runtime/create-renderer/create-renderer.ts","../src/runtime/create-renderer/gamut.ts","../src/inputs/cursor-input/cursor-input.ts","../src/primitives/color-ramp/color-ramp.ts","../src/primitives/color-space/hue.ts","../src/primitives/color-space/hsl.ts","../src/primitives/color-space/transfer.ts","../src/primitives/color-space/hsv.ts","../src/primitives/color-space/lch.ts","../src/primitives/color-space/linear.ts","../src/primitives/color-space/oklab.ts","../src/primitives/color-space/oklch.ts","../src/primitives/color-space/registry.ts","../src/primitives/color-space/mix-color.ts","../src/primitives/color-space/cpu-convert.ts","../src/primitives/noise/noise.ts","../src/primitives/fbm/fbm.ts","../src/primitives/voronoi/voronoi.ts","../src/primitives/quantize/quantize.ts","../src/primitives/sdf-circle/sdf-circle.ts","../src/primitives/displace/displace.ts","../src/primitives/cursor-ripple/cursor-ripple.ts","../src/primitives/time/time.ts","../src/runtime/reduced-motion/reduced-motion.ts","../src/primitives/film-grain/film-grain.ts","../src/primitives/dither/dither.ts","../src/runtime/visibility/visibility.ts","../src/runtime/intersection/intersection.ts","../src/runtime/frame-scheduler/frame-scheduler.ts"],"sourcesContent":["// @lovo/matter — engine package public API.\n\nexport { createRenderer } from './runtime/create-renderer/create-renderer.js';\nexport type {\n GpuRenderer,\n GpuBackend,\n CreateRendererOptions,\n OutputGamut,\n} from './runtime/create-renderer/create-renderer.js';\n\nexport { CursorInput } from './inputs/cursor-input/cursor-input.js';\nexport type { CursorInputOptions, Vector2 } from './inputs/cursor-input/cursor-input.js';\n\nexport { colorRamp } from './primitives/color-ramp/color-ramp.js';\nexport type { ColorRampStop, TSLNode } from './primitives/color-ramp/color-ramp.js';\n\nexport {\n mixColor,\n srgbChannelToLinear,\n oklabToLinearSrgb,\n oklchToLinearSrgb,\n parseColorString,\n} from './primitives/color-space/index.js';\nexport type { ColorSpace, HueInterpolation } from './primitives/color-space/index.js';\n\nexport { simplexNoise } from './primitives/noise/noise.js';\n\nexport { fractalNoise } from './primitives/fbm/fbm.js';\nexport type { FractalNoiseOptions } from './primitives/fbm/fbm.js';\n\nexport { voronoi } from './primitives/voronoi/voronoi.js';\n\nexport { quantize } from './primitives/quantize/quantize.js';\n\nexport { signedDistanceFieldCircle } from './primitives/sdf-circle/sdf-circle.js';\n\nexport { displace } from './primitives/displace/displace.js';\n\nexport { cursorRipple } from './primitives/cursor-ripple/cursor-ripple.js';\nexport type { CursorRippleOptions } from './primitives/cursor-ripple/cursor-ripple.js';\n\nexport { elapsedTime } from './primitives/time/time.js';\n\nexport { filmGrain } from './primitives/film-grain/film-grain.js';\n\nexport { dither } from './primitives/dither/dither.js';\n\nexport {\n setReducedMotionPolicy,\n getReducedMotionPolicy,\n getReducedMotionTimeScale,\n createReducedMotionWatcher,\n} from './runtime/reduced-motion/reduced-motion.js';\nexport type {\n ReducedMotionPolicy,\n ReducedMotionWatcher,\n} from './runtime/reduced-motion/reduced-motion.js';\n\nexport { createVisibilityWatcher } from './runtime/visibility/visibility.js';\nexport type { VisibilityWatcher } from './runtime/visibility/visibility.js';\n\nexport { createIntersectionWatcher } from './runtime/intersection/intersection.js';\nexport type { IntersectionWatcher } from './runtime/intersection/intersection.js';\n\nexport { FrameScheduler } from './runtime/frame-scheduler/frame-scheduler.js';\nexport type { SchedulerTick, SchedulerClient } from './runtime/frame-scheduler/frame-scheduler.js';\n","import { Color, Vector2 } from 'three';\nimport { WebGPURenderer } from 'three/webgpu';\n\nimport { applyCanvasGamut, gamutToColorSpace, type OutputGamut } from './gamut.js';\n\nexport type { OutputGamut } from './gamut.js';\n\nexport type GpuBackend = 'webgpu' | 'webgl2';\n\nexport interface CreateRendererOptions {\n /** Anti-alias the framebuffer. Default: true. */\n antialias?: boolean;\n /** Force WebGL2 even if WebGPU is available (useful for testing fallback). Default: false. */\n forceWebGL?: boolean;\n /** Clear color (hex, CSS string, or THREE.Color). Default: transparent. */\n clearColor?: number | string | Color;\n /** Clear alpha (0–1). Default: 0 (transparent). */\n clearAlpha?: number;\n /** Cap on devicePixelRatio. Default: 2. Pass Infinity to disable. */\n maxDPR?: number;\n /** Output color gamut the framebuffer is encoded for. Default: 'srgb'. */\n gamut?: OutputGamut;\n}\n\nexport interface GpuRenderer {\n /** The underlying Three.js WebGPURenderer (which may be running on a WebGL2 backend). */\n three: WebGPURenderer;\n /** Which backend the renderer initialized with. */\n backend: GpuBackend;\n /** Tear down the renderer and release GPU resources. */\n dispose: () => void;\n /** Resize the renderer to the canvas's current client dimensions. */\n resize: () => void;\n}\n\n/**\n * Create a Matter renderer wrapping THREE.WebGPURenderer.\n *\n * Tries WebGPU first; falls back to WebGL2 automatically if WebGPU is\n * unavailable on the host. The returned object exposes the underlying\n * three renderer plus a small wrapper for resize and disposal.\n */\nexport async function createRenderer(\n canvas: HTMLCanvasElement,\n opts: CreateRendererOptions = {},\n): Promise<GpuRenderer> {\n const {\n antialias = true,\n forceWebGL = false,\n clearColor = 0x000000,\n clearAlpha = 0,\n maxDPR = 2,\n gamut = 'srgb',\n } = opts;\n\n const three = new WebGPURenderer({\n canvas,\n antialias,\n forceWebGL,\n });\n\n await three.init();\n\n three.outputColorSpace = gamutToColorSpace(gamut);\n\n three.setPixelRatio(Math.min(window.devicePixelRatio, maxDPR));\n const resolvedClearColor = clearColor instanceof Color ? clearColor : new Color(clearColor);\n\n three.setClearColor(resolvedClearColor, clearAlpha);\n\n const rendererSize = new Vector2();\n const resize = () => {\n const canvasWidth = canvas.clientWidth;\n const canvasHeight = canvas.clientHeight;\n\n // Ignore zero-size (canvas not yet laid out); a ResizeObserver will call\n // back once it has real dimensions.\n if (canvasWidth === 0 || canvasHeight === 0) return;\n\n // Compare against the renderer's *logical* size (getSize), NOT canvas.width.\n // The backend can set the canvas drawing buffer during init while the\n // renderer's logical size stays at the 300x150 default — comparing\n // canvas.width would then skip setSize and leave the render target\n // (and every shader's output) compressed.\n three.getSize(rendererSize);\n\n if (rendererSize.width !== canvasWidth || rendererSize.height !== canvasHeight) {\n three.setSize(canvasWidth, canvasHeight, false);\n }\n };\n\n resize();\n\n // Detect backend after init. `isWebGLBackend` is an internal duck-type flag\n // not declared in three's public Backend type — probe via `in` rather than\n // a property access that would trip strict typing.\n const isWebGL = 'isWebGLBackend' in three.backend && three.backend.isWebGLBackend === true;\n const backend: GpuBackend = forceWebGL || isWebGL ? 'webgl2' : 'webgpu';\n\n // three's WebGPU backend doesn't configure the canvas context for P3, so do it\n // ourselves now that the backend is known. No-op for sRGB / WebGL fallback.\n applyCanvasGamut(three, backend, gamut);\n\n return {\n three,\n backend,\n dispose: () => three.dispose(),\n resize,\n };\n}\n","import { ColorManagement, SRGBColorSpace } from 'three';\nimport {\n DisplayP3ColorSpace,\n DisplayP3ColorSpaceImpl,\n LinearDisplayP3ColorSpace,\n LinearDisplayP3ColorSpaceImpl,\n} from 'three/examples/jsm/math/ColorSpaces.js';\nimport type { WebGPURenderer } from 'three/webgpu';\n\nimport type { GpuBackend } from './create-renderer.js';\n\n/** The output color gamut the renderer encodes its framebuffer for. */\nexport type OutputGamut = 'srgb' | 'p3';\n\n// three 0.170 core registers only sRGB and linear-sRGB in ColorManagement. The\n// Display P3 spaces ship as an addon that does NOT self-register, so we define\n// them once here (idempotent) before any P3 output. This makes the renderer's\n// linear-sRGB working space convert correctly into P3 on output.\nColorManagement.define({\n [DisplayP3ColorSpace]: DisplayP3ColorSpaceImpl,\n [LinearDisplayP3ColorSpace]: LinearDisplayP3ColorSpaceImpl,\n});\n\n/**\n * Map a resolved output gamut to the three color-space constant for\n * `renderer.outputColorSpace`. `'p3'` selects Display P3; `'srgb'` the default.\n */\nexport function gamutToColorSpace(gamut: OutputGamut): string {\n return gamut === 'p3' ? DisplayP3ColorSpace : SRGBColorSpace;\n}\n\n/**\n * Minimal structural view of the WebGPU backend internals we must reach into.\n * three's public `Backend` type surfaces neither `device` nor `context`, but the\n * WebGPU backend sets both at init (`this.device`, `this.context`).\n */\ninterface WebGpuBackendInternals {\n device: GPUDevice;\n context: GPUCanvasContext;\n}\n\nfunction hasWebGpuBackendInternals(backend: unknown): backend is WebGpuBackendInternals {\n if (typeof backend !== 'object' || backend === null) return false;\n if (!('device' in backend) || !('context' in backend)) return false;\n\n const { device, context } = backend;\n\n return (\n typeof device === 'object' &&\n device !== null &&\n typeof context === 'object' &&\n context !== null &&\n 'configure' in context &&\n typeof context.configure === 'function'\n );\n}\n\n/**\n * Re-configure the WebGPU canvas context for the output gamut.\n *\n * Necessary because three 0.170's WebGPU backend configures the `GPUCanvasContext`\n * only at init, with no `colorSpace` field — so it defaults to sRGB and a P3\n * `outputColorSpace` would write P3-encoded pixels into an sRGB surface. We re-run\n * `configure()` once with `colorSpace: 'display-p3'`, mirroring three's other\n * config values. Critically, `alphaMode` must mirror three's choice: the WebGPU\n * backend defaults `alpha` to `true` and therefore configures the context as\n * `'premultiplied'` (transparent). Passing `'opaque'` here would override that and\n * paint the canvas opaque black until the first shader frame, producing a black\n * flash over whatever sits behind a transparent canvas. Usage matches the\n * backend's `RENDER_ATTACHMENT | COPY_SRC`. Resize never re-configures the\n * context, so this sticks for the renderer's lifetime.\n *\n * No-op for sRGB output, for the WebGL2 fallback (which stays sRGB in v1), and\n * where WebGPU is unavailable.\n */\nexport function applyCanvasGamut(\n renderer: WebGPURenderer,\n backend: GpuBackend,\n gamut: OutputGamut,\n): void {\n if (gamut !== 'p3' || backend !== 'webgpu') return;\n\n // `navigator.gpu` is typed as always-present but is genuinely absent on hosts\n // without WebGPU, so probe with `in` (a `=== undefined` check reads as dead).\n if (typeof navigator === 'undefined' || !('gpu' in navigator)) return;\n\n const webGpuBackend = renderer.backend;\n\n if (!hasWebGpuBackendInternals(webGpuBackend)) return;\n\n webGpuBackend.context.configure({\n device: webGpuBackend.device,\n format: navigator.gpu.getPreferredCanvasFormat(),\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,\n alphaMode: 'premultiplied',\n colorSpace: 'display-p3',\n });\n}\n","export type Vector2 = readonly [number, number];\n\nexport interface CursorInputOptions {\n /**\n * Smoothing factor: 0 = no smoothing (snap to target instantly).\n * 1 = max smoothing (essentially never reaches target).\n * Sensible default: 0.1.\n *\n * Implementation: per-frame, value moves toward target by `(1 - smoothing) * delta * 60`,\n * roughly meaning \"at smoothing=0.1, ~90% of the gap is closed in 1 second at 60fps.\"\n */\n smoothing?: number;\n /** Starting position. Default: [0.5, 0.5] (center). */\n initial?: Vector2;\n /** Listen on this target. Default: window. */\n target?: EventTarget;\n /**\n * Element to normalize cursor coordinates against. Default: window viewport.\n *\n * When set, cursor x/y are in [0,1] across the element's bounding rect, with\n * extrapolation outside (negative when left/above, >1 when right/below). This\n * matches what shader UV space expects: a cursor at the canvas's top-left\n * corner reads as (0, 0); at bottom-right as (1, 1); regardless of where the\n * canvas sits in the viewport. Without this, components inside a partial-\n * viewport scene (e.g. a 70vh hero section) see a cursor offset that scales\n * with the canvas's vertical position on the page.\n */\n element?: {\n getBoundingClientRect(): { left: number; top: number; width: number; height: number };\n };\n}\n\ntype ChangeListener = (value: Vector2) => void;\n\n/**\n * Smoothed pointer tracker emitting a normalized (0..1) Vec2 position.\n * Implements the AnimatableSignal protocol (`get()` + `on('change', cb)`)\n * so it composes with Motion's `useTransform` and similar tools.\n */\nexport class CursorInput {\n private value: [number, number];\n private target: [number, number];\n private targetDirty = false;\n private readonly smoothing: number;\n private readonly listeners = new Set<ChangeListener>();\n private readonly eventTarget: EventTarget;\n private readonly element: CursorInputOptions['element'];\n private readonly handleMouseMove: (e: Event) => void;\n private disposed = false;\n\n constructor(opts: CursorInputOptions = {}) {\n const { smoothing = 0.1, initial = [0.5, 0.5], target, element } = opts;\n\n this.smoothing = clamp01(smoothing);\n this.value = [initial[0], initial[1]];\n this.target = [initial[0], initial[1]];\n this.eventTarget = target ?? (typeof window !== 'undefined' ? window : new EventTarget());\n this.element = element;\n\n this.handleMouseMove = (e: Event) => {\n if (!(e instanceof MouseEvent)) return;\n const mouseEvent = e;\n\n if (this.element) {\n // Normalize to 0..1 across the element's bounding rect. Reading the\n // rect on every move is fine — `getBoundingClientRect` is cheap and\n // mousemove is already throttled to ~60Hz by the browser. The benefit\n // is tracking the element's position even if it moved/scrolled since\n // the last frame.\n const elementRect = this.element.getBoundingClientRect();\n const elementWidth = elementRect.width || 1;\n const elementHeight = elementRect.height || 1;\n\n this.target = [\n (mouseEvent.clientX - elementRect.left) / elementWidth,\n (mouseEvent.clientY - elementRect.top) / elementHeight,\n ];\n } else {\n // Fallback: viewport-normalized. Used when no element is supplied —\n // mostly the standalone-API case for users not consuming through\n // <ShaderScene>'s context.\n const viewportWidth = (typeof window !== 'undefined' && window.innerWidth) || 1;\n const viewportHeight = (typeof window !== 'undefined' && window.innerHeight) || 1;\n\n this.target = [mouseEvent.clientX / viewportWidth, mouseEvent.clientY / viewportHeight];\n }\n this.targetDirty = true;\n };\n\n this.eventTarget.addEventListener('mousemove', this.handleMouseMove);\n }\n\n /** Current smoothed position. Implements AnimatableSignal protocol. */\n get(): Vector2 {\n return this.value;\n }\n\n /** Subscribe to change events. Returns an unsubscribe function. */\n on(_eventType: 'change', changeListener: ChangeListener): () => void {\n this.listeners.add(changeListener);\n\n return () => this.listeners.delete(changeListener);\n }\n\n /**\n * Advance the smoothing one tick. Called by the host scheduler; not\n * typically called directly except in tests.\n */\n tick(delta: number): void {\n if (this.disposed) return;\n const factor = this.smoothing === 0 ? 1 : 1 - Math.pow(this.smoothing, delta * 60);\n const prev0 = this.value[0];\n const prev1 = this.value[1];\n const next0 = lerp(prev0, this.target[0], factor);\n const next1 = lerp(prev1, this.target[1], factor);\n const moved = next0 !== prev0 || next1 !== prev1;\n\n if (moved || this.targetDirty) {\n this.value = [next0, next1];\n this.targetDirty = false;\n const snapshot: Vector2 = [next0, next1];\n\n for (const listener of this.listeners) listener(snapshot);\n }\n }\n\n /** Tear down listeners. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.eventTarget.removeEventListener('mousemove', this.handleMouseMove);\n this.listeners.clear();\n }\n}\n\nconst clamp01 = (value: number) => Math.max(0, Math.min(1, value));\nconst lerp = (startValue: number, endValue: number, blendFactor: number) =>\n startValue + (endValue - startValue) * blendFactor;\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { clamp, div, sub, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { hueArcInterpolators } from '../color-space/hue.js';\nimport { colorSpaces } from '../color-space/registry.js';\nimport type { ColorSpace, HueInterpolation } from '../color-space/types.js';\n\n/**\n * Canonical TSL-node *input* shape used throughout `@lovo/matter`.\n *\n * Stays as the broad `Node | ShaderNodeObject<Node>` union so callers can\n * pass uniform-typed nodes (e.g. `ShaderNodeObject<UniformNode<Vector2>>`)\n * without casting at the call site — those are subtypes of `Node` but NOT\n * subtypes of `ShaderNodeObject<Node>` due to invariant generic parameters.\n *\n * Wrappers should return the narrower `ShaderNodeObject<Node>` so the\n * **output** is always chainable without casts.\n */\nexport type TSLNode = Node | ShaderNodeObject<Node>;\n\nexport interface ColorRampStop {\n /** Color expressed as a TSL node (typically `vec3(r,g,b)`), in linear-sRGB. */\n color: TSLNode;\n /** Position 0..1 along the ramp. */\n position: number;\n}\n\n/**\n * Multi-stop color interpolation. Given a t in [0..1] and N color stops at\n * fixed positions, returns the smoothly-interpolated color.\n *\n * `colorSpace` controls the interpolation space (default `'linear'` — a plain\n * per-channel mix that preserves the input values). Stops are converted into\n * the space up front, the nested-mix chain runs IN that space, and the result\n * is converted back to linear-sRGB once at the end.\n *\n * `hueInterpolation` chooses which way around the wheel cylindrical spaces\n * travel (default `'shorter'`); it's inert for rectangular spaces (linear/oklab).\n *\n * Falls back to the first/last stop's color outside the bracketing positions.\n */\nexport function colorRamp(\n t: TSLNode,\n stops: ColorRampStop[],\n colorSpace: ColorSpace = 'linear',\n hueInterpolation: HueInterpolation = 'shorter',\n): ShaderNodeObject<Node> {\n const space = colorSpaces[colorSpace];\n const hue = hueArcInterpolators[hueInterpolation];\n const first = stops[0];\n\n if (first === undefined) return vec3(0, 0, 0);\n\n const firstCoords = space.fromLinear(vec3(first.color));\n\n if (stops.length === 1) return space.toLinear(firstCoords);\n\n // Build a chain of nested mixes, one per adjacent pair of stops, working in\n // the interpolation space. The running result collapses to the previous stop\n // exactly at each segment boundary, so this stays clean pairwise interpolation\n // (and per-segment shortest-arc hue stays correct for cylindrical spaces).\n let resultCoords = firstCoords;\n\n for (let i = 1; i < stops.length; i += 1) {\n const previousStop = stops[i - 1];\n const next = stops[i];\n\n if (previousStop === undefined || next === undefined) continue;\n const positionSpan = next.position - previousStop.position;\n\n if (positionSpan <= 0) continue;\n // Localize t into the [prev..next] range. `t` is TSLNode (the union),\n // so we use functional-form ops to avoid needing a chain-method receiver.\n const localT = clamp(div(sub(t, previousStop.position), positionSpan), 0, 1);\n\n const nextCoords = space.fromLinear(vec3(next.color));\n\n resultCoords = space.lerp(resultCoords, nextCoords, localT, hue);\n }\n\n return space.toLinear(resultCoords);\n}\n","import { mod, sign, step } from 'three/tsl';\n\nimport type { ArcHueFn, HueInterpolation } from './types.js';\n\n// Below this sweep (radians or turns) two hues count as equal, so `decreasing`\n// doesn't fire a full backward spin between identical-hued stops.\nconst EQUAL_HUE_EPSILON = 1e-6;\n\n/**\n * `shorter` — travel the SHORTER arc (CSS Color 4 default). The signed delta is\n * wrapped into [-period/2, period/2) so the lerp never goes the long way.\n */\nexport const shortestArcHue: ArcHueFn = (h1, h2, t, period) => {\n const half = period / 2;\n const delta = mod(h2.sub(h1).add(half), period).sub(half);\n\n return h1.add(delta.mul(t));\n};\n\n/**\n * `longer` — travel the LONGER arc: take the shorter signed delta and step a\n * full period the other way. At exactly-equal hues `sign` is 0, so the delta\n * stays 0 (no surprise full-circle spin) rather than looping the wheel.\n */\nexport const longestArcHue: ArcHueFn = (h1, h2, t, period) => {\n const half = period / 2;\n const short = mod(h2.sub(h1).add(half), period).sub(half);\n const delta = short.sub(sign(short).mul(period));\n\n return h1.add(delta.mul(t));\n};\n\n/**\n * `increasing` — hue counts strictly UP (wrapping period→0). Delta in [0, period),\n * so a multi-stop ramp marches one way around the wheel without reversing.\n */\nexport const increasingArcHue: ArcHueFn = (h1, h2, t, period) => {\n const delta = mod(h2.sub(h1), period);\n\n return h1.add(delta.mul(t));\n};\n\n/**\n * `decreasing` — hue counts strictly DOWN. The upward delta in [0, period) has a\n * full period subtracted (unless the hues are equal, guarded by the epsilon), so\n * the result lands in (-period, 0].\n */\nexport const decreasingArcHue: ArcHueFn = (h1, h2, t, period) => {\n const up = mod(h2.sub(h1), period);\n const delta = up.sub(step(EQUAL_HUE_EPSILON, up).mul(period));\n\n return h1.add(delta.mul(t));\n};\n\n/** Resolves a `HueInterpolation` keyword to its arc function. */\nexport const hueArcInterpolators: Record<HueInterpolation, ArcHueFn> = {\n shorter: shortestArcHue,\n longer: longestArcHue,\n increasing: increasingArcHue,\n decreasing: decreasingArcHue,\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { abs, clamp, fract, max, min, mix, step, vec3, vec4 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { linearToSrgb, srgbToLinear } from './transfer.js';\nimport type { ColorSpaceImpl } from './types.js';\n\nconst EPSILON = 1e-10;\n\n/** Hocevar branchless hue (turns [0,1)) from gamma RGB — shared shape with HSV. */\nfunction gammaRgbHue(c: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const p = mix(vec4(c.b, c.g, -1 / 3, 2 / 3), vec4(c.g, c.b, 0, -1 / 3), step(c.b, c.g));\n const q = mix(vec4(p.x, p.y, p.w, c.r), vec4(c.r, p.y, p.z, p.x), step(p.x, c.r));\n const chroma = q.x.sub(min(q.w, q.y));\n\n return abs(q.z.add(q.w.sub(q.y).div(chroma.mul(6).add(EPSILON))));\n}\n\n/** gamma sRGB -> HSL (h, s, l). */\nfunction gammaRgbToHsl(c: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const maxChannel = max(c.r, max(c.g, c.b));\n const minChannel = min(c.r, min(c.g, c.b));\n const lightness = maxChannel.add(minChannel).mul(0.5);\n const chroma = maxChannel.sub(minChannel);\n // s = chroma / (1 - |2L - 1|)\n const saturation = chroma.div(abs(lightness.mul(2).sub(1)).oneMinus().add(EPSILON));\n\n return vec3(gammaRgbHue(c), saturation, lightness);\n}\n\n/** HSL (h, s, l) -> gamma sRGB. */\nfunction hslToGammaRgb(hsl: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const hue = hsl.x;\n const saturation = hsl.y;\n const lightness = hsl.z;\n\n const chroma = abs(lightness.mul(2).sub(1)).oneMinus().mul(saturation);\n // Per-channel triangle-wave hue ramp, same basis as Hocevar's hsv2rgb.\n const ramp = abs(\n fract(vec3(hue).add(vec3(1, 2 / 3, 1 / 3)))\n .mul(6)\n .sub(vec3(3)),\n );\n const hueRgb = clamp(ramp.sub(vec3(1)), 0, 1); // pure-hue color at full chroma\n\n return hueRgb.sub(0.5).mul(chroma).add(lightness);\n}\n\nexport const hslSpace: ColorSpaceImpl = {\n // Clamp into sRGB before the gamma transfer: HSL is an sRGB-gamut concept, and\n // the sRGB OETF's pow() can't be WGSL const-evaluated on the negative channels\n // of an out-of-sRGB (wide-gamut) stop color — that crashed the shader compile.\n fromLinear: (rgb) => gammaRgbToHsl(linearToSrgb(clamp(rgb, 0, 1))),\n toLinear: (hsl) => srgbToLinear(hslToGammaRgb(hsl)),\n lerp: (a, b, t, hue) => vec3(hue(a.x, b.x, t, 1), mix(a.y, b.y, t), mix(a.z, b.z, t)),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { mix, pow, step } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * sRGB-encoded channel in [0,1] -> linear-sRGB. Standard sRGB EOTF.\n * Mirrors three's `convertSRGBToLinear` (e.g. 0.5 -> 0.21404114).\n */\nexport function srgbChannelToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;\n}\n\n/** TSL: vec3 sRGB-encoded -> linear-sRGB (branchless via step/mix). */\nexport function srgbToLinear(srgb: TSLNode): ShaderNodeObject<Node> {\n // pow(srgb, 1) normalizes the TSLNode union into a chainable node (no-op).\n const value = pow(srgb, 1);\n const lowSegment = value.div(12.92);\n const highSegment = pow(value.add(0.055).div(1.055), 2.4);\n\n // step(0.04045, value) == 1 where value >= 0.04045 -> pick the high segment.\n return mix(lowSegment, highSegment, step(0.04045, value));\n}\n\n/** TSL: vec3 linear-sRGB -> sRGB-encoded (branchless via step/mix). Inverse OETF. */\nexport function linearToSrgb(linear: TSLNode): ShaderNodeObject<Node> {\n const value = pow(linear, 1);\n const lowSegment = value.mul(12.92);\n const highSegment = pow(value, 1 / 2.4)\n .mul(1.055)\n .sub(0.055);\n\n return mix(lowSegment, highSegment, step(0.0031308, value));\n}\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { abs, clamp, fract, min, mix, step, vec3, vec4 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { linearToSrgb, srgbToLinear } from './transfer.js';\nimport type { ColorSpaceImpl } from './types.js';\n\nconst EPSILON = 1e-10;\n\n// Sam Hocevar's branchless RGB->HSV. Reference (GLSL):\n// vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);\n// vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n// vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n// float d = q.x - min(q.w, q.y);\n// return vec3(abs(q.z + (q.w - q.y)/(6.0*d + e)), d/(q.x + e), q.x);\nfunction gammaRgbToHsv(c: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const p = mix(vec4(c.b, c.g, -1 / 3, 2 / 3), vec4(c.g, c.b, 0, -1 / 3), step(c.b, c.g));\n const q = mix(vec4(p.x, p.y, p.w, c.r), vec4(c.r, p.y, p.z, p.x), step(p.x, c.r));\n const chroma = q.x.sub(min(q.w, q.y));\n const hue = abs(q.z.add(q.w.sub(q.y).div(chroma.mul(6).add(EPSILON))));\n const saturation = chroma.div(q.x.add(EPSILON));\n\n return vec3(hue, saturation, q.x);\n}\n\n// Hocevar's branchless HSV->RGB. Reference (GLSL):\n// vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);\n// vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n// return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\nfunction hsvToGammaRgb(hsv: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const hue = hsv.x;\n const saturation = hsv.y;\n const value = hsv.z;\n const ramp = abs(\n fract(vec3(hue).add(vec3(1, 2 / 3, 1 / 3)))\n .mul(6)\n .sub(vec3(3)),\n );\n\n return mix(vec3(1), clamp(ramp.sub(vec3(1)), 0, 1), saturation).mul(value);\n}\n\nexport const hsvSpace: ColorSpaceImpl = {\n // Clamp into sRGB before the gamma transfer: HSV is an sRGB-gamut concept, and\n // the sRGB OETF's pow() can't be WGSL const-evaluated on the negative channels\n // of an out-of-sRGB (wide-gamut) stop color — that crashed the shader compile.\n fromLinear: (rgb) => gammaRgbToHsv(linearToSrgb(clamp(rgb, 0, 1))),\n toLinear: (hsv) => srgbToLinear(hsvToGammaRgb(hsv)),\n lerp: (a, b, t, hue) => vec3(hue(a.x, b.x, t, 1), mix(a.y, b.y, t), mix(a.z, b.z, t)),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { atan2, cbrt, cos, length, mix, sin, step, vec2, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { ColorSpaceImpl } from './types.js';\n\nconst TWO_PI = Math.PI * 2;\n\n// D65 reference white (CIE 1931 2°).\nconst WHITE_X = 0.95047;\nconst WHITE_Y = 1.0;\nconst WHITE_Z = 1.08883;\n\n// CIELAB nonlinearity constants.\nconst EPSILON = 216 / 24389; // ~0.008856\nconst KAPPA = 24389 / 27; // ~903.3\n\n/** CIELAB forward nonlinearity f(t), branchless via step/mix. */\nfunction labForward(ratio: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const linearPart = ratio.mul(KAPPA).add(16).div(116);\n const cubeRootPart = cbrt(ratio);\n\n return mix(linearPart, cubeRootPart, step(EPSILON, ratio));\n}\n\n/** CIELAB inverse nonlinearity, branchless via step/mix on f^3. */\nfunction labInverse(f: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const cubed = f.mul(f).mul(f);\n const linearPart = f.mul(116).sub(16).div(KAPPA);\n\n return mix(linearPart, cubed, step(EPSILON, cubed));\n}\n\n/** linear-sRGB -> CIELAB LCh (L, C, h). h in radians. */\nfunction linearToLch(rgb: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const r = rgb.r;\n const g = rgb.g;\n const b = rgb.b;\n\n // linear-sRGB -> CIE XYZ (D65).\n const x = r.mul(0.4123907993).add(g.mul(0.3575843394)).add(b.mul(0.1804807884));\n const y = r.mul(0.2126390059).add(g.mul(0.7151686788)).add(b.mul(0.0721923154));\n const z = r.mul(0.0193308187).add(g.mul(0.1191947798)).add(b.mul(0.9505321522));\n\n const fx = labForward(x.div(WHITE_X));\n const fy = labForward(y.div(WHITE_Y));\n const fz = labForward(z.div(WHITE_Z));\n\n const lightness = fy.mul(116).sub(16);\n const greenRed = fx.sub(fy).mul(500);\n const blueYellow = fy.sub(fz).mul(200);\n\n const chroma = length(vec2(greenRed, blueYellow));\n const hue = atan2(blueYellow, greenRed);\n\n return vec3(lightness, chroma, hue);\n}\n\n/** CIELAB LCh (L, C, h) -> linear-sRGB. */\nfunction lchToLinear(lch: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lightness = lch.x;\n const chroma = lch.y;\n const hue = lch.z;\n\n const greenRed = chroma.mul(cos(hue));\n const blueYellow = chroma.mul(sin(hue));\n\n const fy = lightness.add(16).div(116);\n const fx = fy.add(greenRed.div(500));\n const fz = fy.sub(blueYellow.div(200));\n\n const x = labInverse(fx).mul(WHITE_X);\n const y = labInverse(fy).mul(WHITE_Y);\n const z = labInverse(fz).mul(WHITE_Z);\n\n // CIE XYZ (D65) -> linear-sRGB.\n const r = x.mul(3.2409699419).sub(y.mul(1.5373831776)).sub(z.mul(0.4986107603));\n const g = x.mul(-0.9692436363).add(y.mul(1.8759675015)).add(z.mul(0.0415550574));\n const b = x.mul(0.0556300797).sub(y.mul(0.2039769589)).add(z.mul(1.0569715142));\n\n return vec3(r, g, b);\n}\n\nexport const lchSpace: ColorSpaceImpl = {\n fromLinear: linearToLch,\n toLinear: lchToLinear,\n lerp: (a, b, t, hue) => vec3(mix(a.x, b.x, t), mix(a.y, b.y, t), hue(a.z, b.z, t, TWO_PI)),\n};\n","import { mix, vec3 } from 'three/tsl';\n\nimport type { ColorSpaceImpl } from './types.js';\n\n/** Identity space: interpolate raw linear-sRGB values with no conversion. */\nexport const linearSpace: ColorSpaceImpl = {\n fromLinear: (rgb) => vec3(rgb),\n toLinear: (coords) => vec3(coords),\n lerp: (a, b, t) => mix(a, b, t),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { cbrt, mix, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { ColorSpaceImpl } from './types.js';\n\n/** linear-sRGB -> OKLab (L, a, b). */\nexport function linearToOklab(rgb: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const r = rgb.r;\n const g = rgb.g;\n const b = rgb.b;\n\n // Step 1: linear RGB -> LMS (cone response) via matrix M1.\n const longCone = r.mul(0.4122214708).add(g.mul(0.5363325363)).add(b.mul(0.0514459929));\n const mediumCone = r.mul(0.2119034982).add(g.mul(0.6806995451)).add(b.mul(0.1073969566));\n const shortCone = r.mul(0.0883024619).add(g.mul(0.2817188376)).add(b.mul(0.6299787005));\n\n // Step 2: the perceptual cube-root nonlinearity.\n const longRoot = cbrt(longCone);\n const mediumRoot = cbrt(mediumCone);\n const shortRoot = cbrt(shortCone);\n\n // Step 3: LMS' -> OKLab via matrix M2.\n const lightness = longRoot\n .mul(0.2104542553)\n .add(mediumRoot.mul(0.793617785))\n .sub(shortRoot.mul(0.0040720468));\n const greenRed = longRoot\n .mul(1.9779984951)\n .sub(mediumRoot.mul(2.428592205))\n .add(shortRoot.mul(0.4505937099));\n const blueYellow = longRoot\n .mul(0.0259040371)\n .add(mediumRoot.mul(0.7827717662))\n .sub(shortRoot.mul(0.808675766));\n\n return vec3(lightness, greenRed, blueYellow);\n}\n\n/** OKLab (L, a, b) -> linear-sRGB. */\nexport function oklabToLinear(lab: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lightness = lab.x;\n const greenRed = lab.y;\n const blueYellow = lab.z;\n\n // Inverse of step 3: OKLab -> LMS'.\n const longRoot = lightness.add(greenRed.mul(0.3963377774)).add(blueYellow.mul(0.2158037573));\n const mediumRoot = lightness.sub(greenRed.mul(0.1055613458)).sub(blueYellow.mul(0.0638541728));\n const shortRoot = lightness.sub(greenRed.mul(0.0894841775)).sub(blueYellow.mul(1.291485548));\n\n // Inverse of step 2: cube (x^3) to undo the cube root.\n const longCone = longRoot.mul(longRoot).mul(longRoot);\n const mediumCone = mediumRoot.mul(mediumRoot).mul(mediumRoot);\n const shortCone = shortRoot.mul(shortRoot).mul(shortRoot);\n\n // Inverse of step 1: LMS -> linear RGB.\n const r = longCone\n .mul(4.0767416621)\n .sub(mediumCone.mul(3.3077115913))\n .add(shortCone.mul(0.2309699292));\n const g = longCone\n .mul(-1.2684380046)\n .add(mediumCone.mul(2.6097574011))\n .sub(shortCone.mul(0.3413193965));\n const b = longCone\n .mul(-0.0041960863)\n .sub(mediumCone.mul(0.7034186147))\n .add(shortCone.mul(1.707614701));\n\n return vec3(r, g, b);\n}\n\nexport const oklabSpace: ColorSpaceImpl = {\n fromLinear: linearToOklab,\n toLinear: oklabToLinear,\n lerp: (a, b, t) => mix(a, b, t),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { atan2, cos, length, mix, sin, vec2, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { linearToOklab, oklabToLinear } from './oklab.js';\nimport type { ColorSpaceImpl } from './types.js';\n\nconst TWO_PI = Math.PI * 2;\n\n/** linear-sRGB -> OKLch (L, C, h). h in radians [-π, π]. */\nfunction linearToOklch(rgb: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lab = linearToOklab(rgb);\n const lightness = lab.x;\n const greenRed = lab.y;\n const blueYellow = lab.z;\n\n const chroma = length(vec2(greenRed, blueYellow));\n const hue = atan2(blueYellow, greenRed);\n\n return vec3(lightness, chroma, hue);\n}\n\n/** OKLch (L, C, h) -> linear-sRGB. */\nfunction oklchToLinear(lch: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lightness = lch.x;\n const chroma = lch.y;\n const hue = lch.z;\n\n const greenRed = chroma.mul(cos(hue));\n const blueYellow = chroma.mul(sin(hue));\n\n return oklabToLinear(vec3(lightness, greenRed, blueYellow));\n}\n\nexport const oklchSpace: ColorSpaceImpl = {\n fromLinear: linearToOklch,\n toLinear: oklchToLinear,\n lerp: (a, b, t, hue) => vec3(mix(a.x, b.x, t), mix(a.y, b.y, t), hue(a.z, b.z, t, TWO_PI)),\n};\n","import { hslSpace } from './hsl.js';\nimport { hsvSpace } from './hsv.js';\nimport { lchSpace } from './lch.js';\nimport { linearSpace } from './linear.js';\nimport { oklabSpace } from './oklab.js';\nimport { oklchSpace } from './oklch.js';\nimport type { ColorSpace, ColorSpaceImpl } from './types.js';\n\n/** Maps each ColorSpace to its TSL implementation. */\nexport const colorSpaces: Record<ColorSpace, ColorSpaceImpl> = {\n linear: linearSpace,\n oklab: oklabSpace,\n oklch: oklchSpace,\n lch: lchSpace,\n hsl: hslSpace,\n hsv: hsvSpace,\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\nimport { hueArcInterpolators } from './hue.js';\nimport { colorSpaces } from './registry.js';\nimport type { ColorSpace, HueInterpolation } from './types.js';\n\n/**\n * Blend two linear-sRGB colors in `colorSpace`: convert both endpoints into the\n * space, interpolate, convert back to linear-sRGB. `hueInterpolation` chooses\n * the hue-wheel direction for cylindrical spaces (default `'shorter'`; inert for\n * rectangular spaces). The result is NOT clamped — extended (out-of-sRGB) values\n * are preserved so a wide-gamut (P3) output can display them; an sRGB output\n * clamps per-channel at the framebuffer, identical to the prior behavior.\n */\nexport function mixColor(\n colorA: TSLNode,\n colorB: TSLNode,\n t: TSLNode,\n colorSpace: ColorSpace = 'oklab',\n hueInterpolation: HueInterpolation = 'shorter',\n): ShaderNodeObject<Node> {\n const space = colorSpaces[colorSpace];\n const hue = hueArcInterpolators[hueInterpolation];\n const a = space.fromLinear(vec3(colorA));\n const b = space.fromLinear(vec3(colorB));\n\n return space.toLinear(space.lerp(a, b, t, hue));\n}\n","import { srgbChannelToLinear } from './transfer.js';\n\n/**\n * OKLab (L, a, b) -> extended linear-sRGB. CPU mirror of the TSL `oklabToLinear`\n * in `oklab.ts` (same M2^-1 / cube / M1^-1 matrices). The result is NOT clamped:\n * colors outside sRGB return channels below 0 or above 1, which a wide-gamut\n * output can render and an sRGB output clamps at the framebuffer.\n */\nexport function oklabToLinearSrgb(\n lightness: number,\n greenRed: number,\n blueYellow: number,\n): [number, number, number] {\n const longRoot = lightness + 0.3963377774 * greenRed + 0.2158037573 * blueYellow;\n const mediumRoot = lightness - 0.1055613458 * greenRed - 0.0638541728 * blueYellow;\n const shortRoot = lightness - 0.0894841775 * greenRed - 1.291485548 * blueYellow;\n\n const longCone = longRoot * longRoot * longRoot;\n const mediumCone = mediumRoot * mediumRoot * mediumRoot;\n const shortCone = shortRoot * shortRoot * shortRoot;\n\n const red = 4.0767416621 * longCone - 3.3077115913 * mediumCone + 0.2309699292 * shortCone;\n const green = -1.2684380046 * longCone + 2.6097574011 * mediumCone - 0.3413193965 * shortCone;\n const blue = -0.0041960863 * longCone - 0.7034186147 * mediumCone + 1.707614701 * shortCone;\n\n return [red, green, blue];\n}\n\n/** OKLch (L, C, h-in-degrees) -> extended linear-sRGB. */\nexport function oklchToLinearSrgb(\n lightness: number,\n chroma: number,\n hueDegrees: number,\n): [number, number, number] {\n const hueRadians = (hueDegrees * Math.PI) / 180;\n const greenRed = chroma * Math.cos(hueRadians);\n const blueYellow = chroma * Math.sin(hueRadians);\n\n return oklabToLinearSrgb(lightness, greenRed, blueYellow);\n}\n\n/** Parse `50%` -> 0.5 or a bare number. `scale` is the value of 100% (default 1). */\nfunction parseComponent(token: string, scale: number): number {\n const trimmed = token.trim();\n\n if (trimmed.endsWith('%')) {\n return (parseFloat(trimmed.slice(0, -1)) / 100) * scale;\n }\n\n return parseFloat(trimmed);\n}\n\n/** Split `oklch(...)`/`oklab(...)` inner text into component tokens, dropping `/ alpha`. */\nfunction functionArgs(input: string, prefix: string): string[] {\n const inner = input.slice(prefix.length, input.lastIndexOf(')'));\n const beforeAlpha = inner.split('/')[0] ?? '';\n\n return beforeAlpha\n .trim()\n .split(/[\\s,]+/)\n .filter((token) => token.length > 0);\n}\n\n/**\n * Parse a color string to **extended** linear-sRGB. Accepts `#rrggbb`,\n * `oklab(L a b)`, and `oklch(L C H)` (CSS Color 4 syntax: L/C may be percentages,\n * H may carry a `deg` suffix, an optional `/ alpha` is parsed and dropped).\n * Throws on any other syntax.\n */\nexport function parseColorString(input: string): [number, number, number] {\n const value = input.trim();\n\n if (value.startsWith('#')) {\n const hex = value.slice(1);\n\n return [\n srgbChannelToLinear(parseInt(hex.slice(0, 2), 16) / 255),\n srgbChannelToLinear(parseInt(hex.slice(2, 4), 16) / 255),\n srgbChannelToLinear(parseInt(hex.slice(4, 6), 16) / 255),\n ];\n }\n\n if (value.startsWith('oklch(')) {\n const [lightnessToken, chromaToken, hueToken] = functionArgs(value, 'oklch(');\n\n if (lightnessToken === undefined || chromaToken === undefined || hueToken === undefined) {\n throw new Error(`Invalid oklch() color: \"${input}\"`);\n }\n\n const lightness = parseComponent(lightnessToken, 1);\n const chroma = parseComponent(chromaToken, 0.4);\n const hueDegrees = parseFloat(hueToken.replace(/deg$/, ''));\n\n return oklchToLinearSrgb(lightness, chroma, hueDegrees);\n }\n\n if (value.startsWith('oklab(')) {\n const [lightnessToken, aToken, bToken] = functionArgs(value, 'oklab(');\n\n if (lightnessToken === undefined || aToken === undefined || bToken === undefined) {\n throw new Error(`Invalid oklab() color: \"${input}\"`);\n }\n\n return oklabToLinearSrgb(\n parseComponent(lightnessToken, 1),\n parseComponent(aToken, 0.4),\n parseComponent(bToken, 0.4),\n );\n }\n\n throw new Error(`Unsupported color syntax: \"${input}\". Use #rrggbb, oklch(...), or oklab(...).`);\n}\n","import { mx_noise_float } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * 2D simplex noise sampled at a point. Returns a scalar TSL node in\n * approximately [-1, 1] (MaterialX's mx_noise_float is roughly that range).\n *\n * @param p — Vec2 TSL node (typically `uv()` or a scaled/offset uv).\n *\n * Built on top of three's `mx_noise_float`; we wrap it so consumers have a\n * stable import path through `@lovo/matter` and we can swap the\n * implementation if a different noise primitive proves better in practice.\n *\n * Returns `ShaderNodeObject<Node>` (chainable) rather than the broader\n * `TSLNode` union, so callers can `.add(...)`/`.mul(...)` without casting.\n */\nexport function simplexNoise(p: TSLNode): ShaderNodeObject<Node> {\n return mx_noise_float(p);\n}\n","import { add, mul } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\nimport { simplexNoise } from '../noise/noise.js';\n\nexport interface FractalNoiseOptions {\n /** Number of octaves to sum. JS-side number — fixed at TSL build time, not a uniform. Default: 4. */\n octaves?: number;\n /** Per-octave frequency multiplier. JS-side number. Default: 2. */\n lacunarity?: number;\n /** Per-octave amplitude multiplier. JS-side number. Default: 0.5. */\n gain?: number;\n}\n\n/**\n * Fractal Brownian Motion — sum of N octaves of 2D simplex noise.\n *\n * Each octave samples noise at a higher frequency (× `lacunarity`) and lower\n * amplitude (× `gain`) than the previous one, AND at a translated coordinate\n * so the octaves sample uncorrelated regions of noise space. Without the\n * per-octave translation, octaves at related frequencies tend to pile up\n * peaks and troughs at the same input coordinates, producing visibly muddy\n * \"spotty\" output. With it, the octaves look like independent noise patterns\n * layered together — Inigo Quilez's classic FBM technique.\n *\n * `octaves`, `lacunarity`, and `gain` are JavaScript numbers (NOT TSL\n * uniforms) because the loop must be unrolled at TSL-build time — TSL has\n * no dynamic-length loop primitive that maps cleanly to all backends.\n * Animatable parameters that *do* survive on the GPU are the input UV\n * (which the caller can scale/translate per frame) and `time`.\n *\n * Returns `ShaderNodeObject<Node>` (chainable) for cast-free call sites.\n *\n * @param p — Vec2 or Vec3 TSL node (UV-space position).\n * @returns scalar TSL node, normalized to roughly [-1..1] regardless of\n * octave count thanks to the amplitude-sum division at the end.\n */\nexport function fractalNoise(p: TSLNode, opts: FractalNoiseOptions = {}): ShaderNodeObject<Node> {\n const octaves = opts.octaves ?? 4;\n const lacunarity = opts.lacunarity ?? 2;\n const gain = opts.gain ?? 0.5;\n\n let sum: ShaderNodeObject<Node> = simplexNoise(p);\n let amplitude = 1;\n let frequency = 1;\n let total = amplitude;\n\n for (let i = 1; i < octaves; i += 1) {\n frequency *= lacunarity;\n amplitude *= gain;\n total += amplitude;\n // Per-octave decorrelation: translate the sample point by a growing\n // offset so this octave reads from a totally different region of noise\n // space than the previous one. Magnitude 100 is well past simplex\n // noise's ~1-unit feature size, so adjacent octaves are fully\n // decorrelated. The scalar broadcasts across all components of `p`\n // (works for vec2 and vec3 inputs alike).\n //\n // Build the chain functionally from `p`: gotcha #12 doesn't apply\n // because `p` is uv-rooted, but the TSLNode union still requires\n // functional form on this hop.\n const pAtFreq = add(mul(p, frequency), i * 100);\n const layer = simplexNoise(pAtFreq).mul(amplitude);\n\n sum = sum.add(layer);\n }\n\n // Normalize to approximate [-1..1] regardless of octave count / gain.\n return sum.div(total);\n}\n","import { mx_worley_noise_float } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * 2D voronoi (Worley) noise — distance to the nearest jittered cell point,\n * normalized roughly to [0, 1]. Higher values = farther from any cell point\n * (cell interiors); lower values = near a cell boundary.\n *\n * Built on three's `mx_worley_noise_float`. Combine with `colorRamp` for\n * a multi-color cellular pattern; threshold via `step`/`smoothstep` for\n * hard cell shapes.\n *\n * Returns `ShaderNodeObject<Node>` (chainable) for cast-free call sites.\n *\n * @param p — Vec2 TSL node, typically `uv() * scale`.\n */\nexport function voronoi(p: TSLNode): ShaderNodeObject<Node> {\n return mx_worley_noise_float(p);\n}\n","import type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\n/**\n * Quantize a scalar TSL node to `steps` discrete levels.\n *\n * quantize(t, 4) → values in {0, 0.25, 0.5, 0.75, 1.0}\n *\n * `steps` is a JS-side number (loop-equivalent at TSL build time, baked in).\n * If you need an animatable step count, rebuild the TSL fragment.\n */\nexport function quantize(t: ShaderNodeObject<Node>, steps: number): ShaderNodeObject<Node> {\n if (steps <= 1) {\n // Edge case: single step → constant 0. Return as-is wrapped in mul(0).\n return t.mul(0);\n }\n const denominator = steps - 1;\n\n // floor(t * (steps-1) + 0.5) / (steps-1)\n // Using floor(x + 0.5) instead of round() for TSL portability.\n return t.mul(denominator).add(0.5).floor().div(denominator);\n}\n","import { length } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * Signed distance field for a circle centered at the origin.\n *\n * sdfCircle(p, r) = length(p) - r\n *\n * Negative inside the circle, zero on the boundary, positive outside.\n * Combine with `smoothstep(-edge, +edge, sdf)` to render a soft-edged disk.\n *\n * @param p — Vec2 TSL node (typically a UV-space offset from the center).\n * @param radius — JS-side scalar OR a scalar TSL node.\n */\nexport function signedDistanceFieldCircle(\n p: TSLNode,\n radius: TSLNode | number,\n): ShaderNodeObject<Node> {\n return length(p).sub(radius);\n}\n","import { add } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * Naive vector addition: returns `p + by`.\n *\n * displace(p, by) = p + by\n *\n * Thin wrapper that names the spatial intent of shifting a sample point.\n *\n * **SDF caveat:** when using this to translate an SDF render, pass the\n * NEGATED translation — `sdfCircle(displace(p, v.mul(-1)), r)` renders the\n * disk at position `+v` because SDF translation evaluates as\n * `length(p - center) - r`. Adding `+v` to the sample point shifts the\n * rendered shape in the OPPOSITE direction.\n *\n * @param p — Vec2 TSL node (the position being displaced).\n * @param by — Vec2 TSL node (the displacement vector).\n */\nexport function displace(p: TSLNode, by: TSLNode): ShaderNodeObject<Node> {\n return add(p, by);\n}\n","import { length, sin, smoothstep, sub } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\nimport { elapsedTime } from '../time/time.js';\n\nexport interface CursorRippleOptions {\n /** Decay radius (UV space). Beyond this, the ripple is ~0. Default: 0.4. */\n reach?: number;\n /** Wavelength controls the ripple spacing. Default: 30. Larger = wider rings. */\n frequency?: number;\n /** Time multiplier on the wave phase. Default: 6. Larger = faster oscillation. */\n speed?: number;\n /** Output amplitude. Default: 0.5. Final result is in roughly [-amplitude, +amplitude]. */\n amplitude?: number;\n}\n\n/**\n * A radial ripple emanating from `center`. Returns a scalar TSL node in\n * roughly [-amplitude, +amplitude] that decays to ~0 outside `reach`.\n *\n * ripple = sin(d*frequency - time*speed) * amplitude * smoothstep(reach, 0, d)\n *\n * Compose into a wave field by adding it to the underlying base wave.\n *\n * Note: `frequency` / `speed` / `reach` / `amplitude` are JS-side numbers\n * (baked into the TSL fragment at material-build time). The animatable\n * cursor position is the only live uniform consumed.\n *\n * @param p — Vec2 TSL node (typically `uv()`).\n * @param center — Vec2 TSL node (cursor uniform, in UV space).\n */\nexport function cursorRipple(\n p: TSLNode,\n center: TSLNode,\n opts: CursorRippleOptions = {},\n): ShaderNodeObject<Node> {\n const reach = opts.reach ?? 0.4;\n const frequency = opts.frequency ?? 30;\n const speed = opts.speed ?? 6;\n const amplitude = opts.amplitude ?? 0.5;\n\n // d = length(p - center). Use functional `sub(p, center)` because both\n // are typed as the broad TSLNode union (no chain receiver). Per gotcha #12,\n // building from a raw `uniform()` receiver silently produces wrong GPU\n // values, so the functional form is also safer for `center` being a uniform.\n const d = length(sub(p, center));\n // `time` is the engine-gated TSL node (from primitives/time/time.ts);\n // chains rooted in `time` automatically respect `prefers-reduced-motion` and\n // the runtime override set via `setReducedMotionPolicy`.\n const wave = sin(d.mul(frequency).sub(elapsedTime.mul(speed)));\n const decay = smoothstep(reach, 0, d);\n\n return wave.mul(amplitude).mul(decay);\n}\n","// Engine-gated `time` — equals the TSL built-in `time` multiplied by the\n// reduced-motion scale uniform. Components consuming `time` from `@lovo/matter`\n// automatically respect `prefers-reduced-motion` and the policy override set\n// via `setReducedMotionPolicy`.\n//\n// If you want raw uncapped time (e.g. for a debug overlay), import `time`\n// from `three/tsl` directly.\nimport { time as _builtinTime } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { getReducedMotionTimeScale } from '../../runtime/reduced-motion/reduced-motion.js';\n\nexport const elapsedTime: ShaderNodeObject<Node> = _builtinTime.mul(getReducedMotionTimeScale());\n","import { uniform } from 'three/tsl';\n\nexport type ReducedMotionPolicy = 'auto' | 'off' | 'slow' | 'paused';\n\n/**\n * Public surface exposed to package consumers. `recompute` is intentionally\n * absent — it is engine-internal and should not be callable from outside.\n */\nexport interface ReducedMotionWatcher {\n /** Current time scale: 0, 0.3, or 1. */\n scale(): number;\n /** Subscribe to scale changes. Returns unsubscribe. */\n subscribe(cb: (scale: number) => void): () => void;\n /** Tear down media-query listener. */\n dispose(): void;\n}\n\n/**\n * Engine-internal extension of the public watcher. Only `setReducedMotionPolicy`\n * calls `recompute`; it is never part of the consumer-visible type.\n */\ninterface InternalWatcher extends ReducedMotionWatcher {\n recompute(): void;\n}\n\ninterface PolicyState {\n policy: ReducedMotionPolicy;\n watchers: Set<InternalWatcher>;\n}\n\nconst state: PolicyState = {\n policy: 'auto',\n watchers: new Set(),\n};\n\n/**\n * Override Matter's default behavior of honoring `prefers-reduced-motion`.\n * - 'auto' — follow the OS media query (default)\n * - 'off' — full speed regardless of OS setting\n * - 'slow' — 30% speed regardless of OS setting\n * - 'paused' — 0 (animation effectively frozen) regardless of OS setting\n */\nexport function setReducedMotionPolicy(policy: ReducedMotionPolicy): void {\n if (state.policy === policy) return;\n state.policy = policy;\n for (const watcher of state.watchers) watcher.recompute();\n}\n\nexport function getReducedMotionPolicy(): ReducedMotionPolicy {\n return state.policy;\n}\n\nconst computeScale = (mqlMatches: boolean): number => {\n switch (state.policy) {\n case 'off':\n return 1;\n case 'slow':\n return 0.3;\n case 'paused':\n return 0;\n case 'auto':\n return mqlMatches ? 0.3 : 1;\n }\n};\n\n/**\n * Create a watcher that tracks `prefers-reduced-motion: reduce` and the\n * global Matter policy override. Strict-mode-safe — callers create+dispose\n * one per mount cycle.\n */\nexport function createReducedMotionWatcher(): ReducedMotionWatcher {\n // SSR safety: bail to the no-op watcher if matchMedia is missing.\n // SSR watcher: scale() respects policy override but does not emit\n // subscription events (the engine has no way to notify SSR-created\n // watchers because they are not added to state.watchers — but in\n // practice CLAUDE.md gotcha #10 requires `ssr: false` for any component\n // that touches the matter engine).\n if (typeof matchMedia !== 'function') {\n return {\n scale: () => computeScale(false),\n subscribe: (listener) => {\n // No-op: SSR watchers are not in state.watchers and will never\n // receive policy-change notifications.\n void listener;\n\n return () => {\n // SSR no-op unsubscribe\n };\n },\n /** SSR watcher does not emit policy-change notifications. */\n dispose: () => {\n // SSR no-op dispose\n },\n };\n }\n\n const mediaQueryList = matchMedia('(prefers-reduced-motion: reduce)');\n const subscriptions = new Set<(scale: number) => void>();\n let lastComputedScale = computeScale(mediaQueryList.matches);\n\n const onChange = () => {\n const next = computeScale(mediaQueryList.matches);\n\n if (next !== lastComputedScale) {\n lastComputedScale = next;\n for (const listener of subscriptions) listener(next);\n }\n };\n\n mediaQueryList.addEventListener('change', onChange);\n\n const watcher: InternalWatcher = {\n scale: () => lastComputedScale,\n subscribe(listener) {\n subscriptions.add(listener);\n\n return () => subscriptions.delete(listener);\n },\n recompute() {\n const next = computeScale(mediaQueryList.matches);\n\n if (next !== lastComputedScale) {\n lastComputedScale = next;\n for (const listener of subscriptions) listener(next);\n }\n },\n dispose() {\n mediaQueryList.removeEventListener('change', onChange);\n subscriptions.clear();\n state.watchers.delete(watcher);\n },\n };\n\n state.watchers.add(watcher);\n\n return watcher;\n}\n\nlet globalScaleUniform: ReturnType<typeof uniform<number>> | null = null;\nlet globalWatcher: ReducedMotionWatcher | null = null;\n\n/**\n * Returns the engine-shared TSL uniform that `time` is multiplied by. Lazily\n * initialized on first read; reused across all materials. Mutating `.value`\n * imperatively when policy changes is safe — TSL re-reads the uniform every\n * frame.\n */\nexport function getReducedMotionTimeScale(): ReturnType<typeof uniform<number>> {\n if (globalScaleUniform === null) {\n globalWatcher = createReducedMotionWatcher();\n globalScaleUniform = uniform(globalWatcher.scale());\n globalWatcher.subscribe((s) => {\n if (globalScaleUniform === null) return;\n globalScaleUniform.value = s;\n });\n }\n\n return globalScaleUniform;\n}\n\n// Keep a typed reference for tests that may want to re-init between tests.\nexport const resetReducedMotionForTests = () => {\n globalWatcher?.dispose();\n globalWatcher = null;\n globalScaleUniform = null;\n};\n","import { hash, mul, screenCoordinate } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\ntype TSLScalar = TSLNode | number;\n\nexport function filmGrain(intensity: TSLScalar, timeOffset: TSLScalar = 0): ShaderNodeObject<Node> {\n const pixel = screenCoordinate.xy.floor();\n // Convert to uint before multiplying so the seed arithmetic stays in exact\n // integer space. Float32 loses integer precision above 2^24 (~16.7M), and\n // pixel.y * 9277 exceeds that on 4K+ screens, corrupting the seed.\n const seed = pixel.x\n .toUint()\n .mul(1973)\n .add(pixel.y.toUint().mul(9277))\n .add(mul(timeOffset, 26699).toUint());\n\n return hash(seed).sub(0.5).mul(intensity);\n}\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { dot, fract, sin, vec2, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/** Cheap per-pixel hash -> pseudo-random value in [0,1) from a 2D coordinate. */\nfunction hash21(coord: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n return fract(sin(dot(coord, vec2(12.9898, 78.233))).mul(43758.5453));\n}\n\n/**\n * Add sub-LSB dither to break up 8-bit quantization banding (most visible on\n * wide-gamut/P3 output, where the same 256 levels span a wider gamut).\n *\n * `coord` is a per-pixel coordinate (pass `uv()`); `amount` is the noise\n * magnitude in the color's units (default ~1/255, roughly one 8-bit step). Uses a\n * triangular PDF (difference of two hashes) for flatter, less \"gritty\" noise than\n * uniform white noise.\n *\n * SPIKE NOTE: applied here in linear-sRGB working space (before the renderer's\n * output transfer), so the effective dither is uneven across tones (over-dithers\n * shadows, under-dithers highlights). The correct home is a final output pass\n * after color-space conversion; this is good enough to evaluate the banding fix.\n */\nexport function dither(color: TSLNode, coord: TSLNode, amount = 1 / 255): ShaderNodeObject<Node> {\n const pixelCoord = vec2(coord);\n const firstHash = hash21(pixelCoord);\n const secondHash = hash21(pixelCoord.add(vec2(0.5, 0.5)));\n // (r1 - r2) is triangular on [-1, 1]; halve to [-0.5, 0.5] then scale to amount.\n const triangularNoise = firstHash.sub(secondHash).mul(0.5);\n\n return vec3(color).add(triangularNoise.mul(amount));\n}\n","export interface VisibilityWatcher {\n isVisible(): boolean;\n /** Subscribe to changes. Receives the new visibility state. Returns unsubscribe. */\n subscribe(cb: (visible: boolean) => void): () => void;\n dispose(): void;\n}\n\n/**\n * Watch `document.visibilityState`. Strict-mode-safe — callers create+dispose\n * one per mount cycle.\n *\n * SSR: if `document` is unavailable, returns a no-op watcher whose\n * `isVisible()` always returns `true` and whose `subscribe` does nothing.\n */\nexport function createVisibilityWatcher(): VisibilityWatcher {\n if (typeof document === 'undefined') {\n return {\n isVisible: () => true,\n subscribe: () => () => {\n // SSR no-op unsubscribe\n },\n dispose: () => {\n // SSR no-op dispose\n },\n };\n }\n\n const subscriptions = new Set<(visible: boolean) => void>();\n const onChange = () => {\n const isVisible = document.visibilityState === 'visible';\n\n for (const listener of subscriptions) listener(isVisible);\n };\n\n document.addEventListener('visibilitychange', onChange);\n\n return {\n isVisible: () => document.visibilityState === 'visible',\n subscribe(listener) {\n subscriptions.add(listener);\n\n return () => subscriptions.delete(listener);\n },\n dispose() {\n document.removeEventListener('visibilitychange', onChange);\n subscriptions.clear();\n },\n };\n}\n","export interface IntersectionWatcher {\n isInView(): boolean;\n /** Subscribe to changes. Receives the new in-view state. Returns unsubscribe. */\n subscribe(cb: (inView: boolean) => void): () => void;\n dispose(): void;\n}\n\n/**\n * Watch a canvas's viewport intersection. Pauses tied to this watcher should\n * be resumed when the canvas is *any* fraction visible. Strict-mode-safe.\n *\n * SSR: if `IntersectionObserver` is unavailable, returns a no-op watcher whose\n * `isInView()` always returns `true` and whose `subscribe` does nothing.\n */\nexport function createIntersectionWatcher(canvas: HTMLCanvasElement): IntersectionWatcher {\n if (typeof IntersectionObserver === 'undefined') {\n return {\n isInView: () => true,\n subscribe: () => () => {\n // SSR no-op unsubscribe\n },\n dispose: () => {\n // SSR no-op dispose\n },\n };\n }\n\n const subscriptions = new Set<(inView: boolean) => void>();\n let inView = true;\n const observer = new IntersectionObserver(\n (entries) => {\n const next = entries.some((entry) => entry.isIntersecting);\n\n if (next === inView) return;\n inView = next;\n for (const listener of subscriptions) listener(inView);\n },\n { threshold: 0 },\n );\n\n observer.observe(canvas);\n\n return {\n isInView: () => inView,\n subscribe(listener) {\n subscriptions.add(listener);\n\n return () => subscriptions.delete(listener);\n },\n dispose() {\n observer.disconnect();\n subscriptions.clear();\n },\n };\n}\n","export interface SchedulerTick {\n delta: number;\n elapsed: number;\n now: number;\n}\n\nexport type SchedulerClient = (tick: SchedulerTick) => void;\n\nexport class FrameScheduler {\n private readonly clients = new Set<SchedulerClient>();\n private rafId: number | null = null;\n private running = false;\n private paused = false;\n private flushPending = false;\n private startedAt = 0;\n private lastTickAt = 0;\n\n // Reference-counted idle voting. The scheduler is idle only when at least\n // one component has voted idle AND no component has voted animated. This\n // prevents a static component (e.g. LinearGradient speed=0) from halting\n // the loop while an animated overlay (e.g. FilmGrain) is still running.\n private idleVotes = 0;\n private animatedVotes = 0;\n\n /** True when all participating components prefer idle and none need animation. */\n get idle(): boolean {\n return this.idleVotes > 0 && this.animatedVotes === 0;\n }\n\n /** Activate the scheduler. The rAF loop starts on the first client added. */\n start(): void {\n this.running = true;\n this.paused = false;\n this.maybeQueue();\n }\n\n /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */\n stop(): void {\n this.running = false;\n this.cancel();\n }\n\n /** Temporarily skip ticks without losing client registrations. */\n pause(): void {\n this.paused = true;\n }\n\n /** Resume after pause(). */\n resume(): void {\n this.paused = false;\n if (this.running) this.maybeQueue();\n }\n\n /** Register a client to be called every frame. */\n add(client: SchedulerClient): void {\n this.clients.add(client);\n if (this.running) this.maybeQueue();\n }\n\n /** Unregister a client. */\n remove(client: SchedulerClient): void {\n this.clients.delete(client);\n }\n\n /** Permanent teardown: stop the loop and drop all clients. */\n dispose(): void {\n this.stop();\n this.clients.clear();\n }\n\n /**\n * Cast a vote on whether the scheduler should be idle.\n *\n * `setIdle(true)` increments the idle-vote count; the returned cleanup\n * decrements it. `setIdle(false)` increments the animated-vote count;\n * its cleanup decrements that. The scheduler halts (after one flush tick)\n * only when `idleVotes > 0 && animatedVotes === 0`.\n *\n * Callers are responsible for calling the returned cleanup on unmount.\n * Use `requestRender()` or cast a `setIdle(false)` vote to wake the loop\n * without permanently registering an animated preference.\n */\n setIdle(idle: boolean): () => void {\n if (idle) {\n const wasIdle = this.idle;\n\n this.idleVotes += 1;\n const nowIdle = this.idle;\n\n if (!wasIdle && nowIdle) this.onBecameIdle();\n\n return () => {\n const prevIdle = this.idle;\n\n this.idleVotes = Math.max(0, this.idleVotes - 1);\n const afterIdle = this.idle;\n\n if (prevIdle && !afterIdle) this.onBecameAnimated();\n };\n } else {\n const wasIdle = this.idle;\n\n this.animatedVotes += 1;\n if (wasIdle) this.onBecameAnimated();\n\n return () => {\n const prevIdle = this.idle;\n\n this.animatedVotes = Math.max(0, this.animatedVotes - 1);\n const nowIdle = this.idle;\n\n if (!prevIdle && nowIdle) this.onBecameIdle();\n };\n }\n }\n\n /** Force a single tick while idle. Useful for prop-change invalidation. */\n requestRender(): void {\n if (!this.idle) return;\n this.flushPending = true;\n this.maybeQueue();\n }\n\n private onBecameIdle(): void {\n this.flushPending = true;\n this.maybeQueue();\n }\n\n private onBecameAnimated(): void {\n this.flushPending = false;\n this.maybeQueue();\n }\n\n private maybeQueue(): void {\n if (this.rafId !== null) return;\n if (!this.running) return;\n if (this.clients.size === 0) return;\n if (this.idle && !this.flushPending) return;\n this.rafId = requestAnimationFrame(this.frame);\n }\n\n private cancel(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n private readonly frame = (now: number): void => {\n this.rafId = null;\n if (!this.running || this.paused) return;\n\n if (this.startedAt === 0) {\n this.startedAt = now;\n this.lastTickAt = now;\n }\n const delta = (now - this.lastTickAt) / 1000;\n const elapsed = (now - this.startedAt) / 1000;\n\n this.lastTickAt = now;\n\n const tick: SchedulerTick = { delta, elapsed, now };\n\n for (const client of this.clients) {\n client(tick);\n }\n\n this.flushPending = false;\n this.maybeQueue();\n };\n}\n"],"mappings":"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","names":["import_three","import_tsl","import_tsl","import_tsl","import_tsl","EPSILON","import_tsl","EPSILON","import_tsl","import_tsl","import_tsl","TWO_PI","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","_builtinTime","import_tsl","import_tsl"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runtime/create-renderer/create-renderer.ts","../src/runtime/create-renderer/gamut.ts","../src/inputs/cursor-input/cursor-input.ts","../src/primitives/color-ramp/color-ramp.ts","../src/primitives/color-space/hue.ts","../src/primitives/color-space/hsl.ts","../src/primitives/color-space/transfer.ts","../src/primitives/color-space/hsv.ts","../src/primitives/color-space/lch.ts","../src/primitives/color-space/linear.ts","../src/primitives/color-space/oklab.ts","../src/primitives/color-space/oklch.ts","../src/primitives/color-space/registry.ts","../src/primitives/color-space/mix-color.ts","../src/primitives/color-space/cpu-convert.ts","../src/primitives/noise/noise.ts","../src/primitives/fbm/fbm.ts","../src/primitives/voronoi/voronoi.ts","../src/primitives/quantize/quantize.ts","../src/primitives/sdf-circle/sdf-circle.ts","../src/primitives/displace/displace.ts","../src/primitives/cursor-ripple/cursor-ripple.ts","../src/primitives/time/time.ts","../src/runtime/reduced-motion/reduced-motion.ts","../src/runtime/clock/reset-clock.ts","../src/primitives/grain/grain.ts","../src/primitives/dither/dither.ts","../src/runtime/visibility/visibility.ts","../src/runtime/intersection/intersection.ts","../src/runtime/frame-scheduler/frame-scheduler.ts"],"sourcesContent":["// @lovo/matter — engine package public API.\n\nexport { createRenderer } from './runtime/create-renderer/create-renderer.js';\nexport type {\n GpuRenderer,\n GpuBackend,\n CreateRendererOptions,\n OutputGamut,\n} from './runtime/create-renderer/create-renderer.js';\n\nexport { CursorInput } from './inputs/cursor-input/cursor-input.js';\nexport type { CursorInputOptions, Vector2 } from './inputs/cursor-input/cursor-input.js';\n\nexport { colorRamp } from './primitives/color-ramp/color-ramp.js';\nexport type { ColorRampStop, TSLNode } from './primitives/color-ramp/color-ramp.js';\n\nexport {\n mixColor,\n srgbChannelToLinear,\n oklabToLinearSrgb,\n oklchToLinearSrgb,\n parseColorString,\n} from './primitives/color-space/index.js';\nexport type { ColorSpace, HueInterpolation } from './primitives/color-space/index.js';\n\nexport { simplexNoise } from './primitives/noise/noise.js';\n\nexport { fractalNoise } from './primitives/fbm/fbm.js';\nexport type { FractalNoiseOptions } from './primitives/fbm/fbm.js';\n\nexport { voronoi } from './primitives/voronoi/voronoi.js';\n\nexport { quantize } from './primitives/quantize/quantize.js';\n\nexport { signedDistanceFieldCircle } from './primitives/sdf-circle/sdf-circle.js';\n\nexport { displace } from './primitives/displace/displace.js';\n\nexport { cursorRipple } from './primitives/cursor-ripple/cursor-ripple.js';\nexport type { CursorRippleOptions } from './primitives/cursor-ripple/cursor-ripple.js';\n\nexport { elapsedTime } from './primitives/time/time.js';\n\nexport { resetRendererClock } from './runtime/clock/reset-clock.js';\n\nexport { grain } from './primitives/grain/grain.js';\n\nexport { dither } from './primitives/dither/dither.js';\n\nexport {\n setReducedMotionPolicy,\n getReducedMotionPolicy,\n getReducedMotionTimeScale,\n createReducedMotionWatcher,\n} from './runtime/reduced-motion/reduced-motion.js';\nexport type {\n ReducedMotionPolicy,\n ReducedMotionWatcher,\n} from './runtime/reduced-motion/reduced-motion.js';\n\nexport { createVisibilityWatcher } from './runtime/visibility/visibility.js';\nexport type { VisibilityWatcher } from './runtime/visibility/visibility.js';\n\nexport { createIntersectionWatcher } from './runtime/intersection/intersection.js';\nexport type { IntersectionWatcher } from './runtime/intersection/intersection.js';\n\nexport { FrameScheduler } from './runtime/frame-scheduler/frame-scheduler.js';\nexport type { SchedulerTick, SchedulerClient } from './runtime/frame-scheduler/frame-scheduler.js';\n","import { Color, Vector2 } from 'three';\nimport { WebGPURenderer } from 'three/webgpu';\n\nimport { applyCanvasGamut, gamutToColorSpace, type OutputGamut } from './gamut.js';\n\nexport type { OutputGamut } from './gamut.js';\n\nexport type GpuBackend = 'webgpu' | 'webgl2';\n\nexport interface CreateRendererOptions {\n /** Anti-alias the framebuffer. Default: true. */\n antialias?: boolean;\n /** Force WebGL2 even if WebGPU is available (useful for testing fallback). Default: false. */\n forceWebGL?: boolean;\n /** Clear color (hex, CSS string, or THREE.Color). Default: transparent. */\n clearColor?: number | string | Color;\n /** Clear alpha (0–1). Default: 0 (transparent). */\n clearAlpha?: number;\n /** Cap on devicePixelRatio. Default: 2. Pass Infinity to disable. */\n maxDPR?: number;\n /** Output color gamut the framebuffer is encoded for. Default: 'srgb'. */\n gamut?: OutputGamut;\n}\n\nexport interface GpuRenderer {\n /** The underlying Three.js WebGPURenderer (which may be running on a WebGL2 backend). */\n three: WebGPURenderer;\n /** Which backend the renderer initialized with. */\n backend: GpuBackend;\n /** Tear down the renderer and release GPU resources. */\n dispose: () => void;\n /** Resize the renderer to the canvas's current client dimensions. */\n resize: () => void;\n}\n\n/**\n * Create a Matter renderer wrapping THREE.WebGPURenderer.\n *\n * Tries WebGPU first; falls back to WebGL2 automatically if WebGPU is\n * unavailable on the host. The returned object exposes the underlying\n * three renderer plus a small wrapper for resize and disposal.\n */\nexport async function createRenderer(\n canvas: HTMLCanvasElement,\n opts: CreateRendererOptions = {},\n): Promise<GpuRenderer> {\n const {\n antialias = true,\n forceWebGL = false,\n clearColor = 0x000000,\n clearAlpha = 0,\n maxDPR = 2,\n gamut = 'srgb',\n } = opts;\n\n const three = new WebGPURenderer({\n canvas,\n antialias,\n forceWebGL,\n });\n\n await three.init();\n\n three.outputColorSpace = gamutToColorSpace(gamut);\n\n three.setPixelRatio(Math.min(window.devicePixelRatio, maxDPR));\n const resolvedClearColor = clearColor instanceof Color ? clearColor : new Color(clearColor);\n\n three.setClearColor(resolvedClearColor, clearAlpha);\n\n const rendererSize = new Vector2();\n const resize = () => {\n const canvasWidth = canvas.clientWidth;\n const canvasHeight = canvas.clientHeight;\n\n // Ignore zero-size (canvas not yet laid out); a ResizeObserver will call\n // back once it has real dimensions.\n if (canvasWidth === 0 || canvasHeight === 0) return;\n\n // Compare against the renderer's *logical* size (getSize), NOT canvas.width.\n // The backend can set the canvas drawing buffer during init while the\n // renderer's logical size stays at the 300x150 default — comparing\n // canvas.width would then skip setSize and leave the render target\n // (and every shader's output) compressed.\n three.getSize(rendererSize);\n\n if (rendererSize.width !== canvasWidth || rendererSize.height !== canvasHeight) {\n three.setSize(canvasWidth, canvasHeight, false);\n }\n };\n\n resize();\n\n // Detect backend after init. `isWebGLBackend` is an internal duck-type flag\n // not declared in three's public Backend type — probe via `in` rather than\n // a property access that would trip strict typing.\n const isWebGL = 'isWebGLBackend' in three.backend && three.backend.isWebGLBackend === true;\n const backend: GpuBackend = forceWebGL || isWebGL ? 'webgl2' : 'webgpu';\n\n // three's WebGPU backend doesn't configure the canvas context for P3, so do it\n // ourselves now that the backend is known. No-op for sRGB / WebGL fallback.\n applyCanvasGamut(three, backend, gamut);\n\n return {\n three,\n backend,\n dispose: () => three.dispose(),\n resize,\n };\n}\n","import { ColorManagement, SRGBColorSpace } from 'three';\nimport {\n DisplayP3ColorSpace,\n DisplayP3ColorSpaceImpl,\n LinearDisplayP3ColorSpace,\n LinearDisplayP3ColorSpaceImpl,\n} from 'three/examples/jsm/math/ColorSpaces.js';\nimport type { WebGPURenderer } from 'three/webgpu';\n\nimport type { GpuBackend } from './create-renderer.js';\n\n/** The output color gamut the renderer encodes its framebuffer for. */\nexport type OutputGamut = 'srgb' | 'p3';\n\n// three 0.170 core registers only sRGB and linear-sRGB in ColorManagement. The\n// Display P3 spaces ship as an addon that does NOT self-register, so we define\n// them once here (idempotent) before any P3 output. This makes the renderer's\n// linear-sRGB working space convert correctly into P3 on output.\nColorManagement.define({\n [DisplayP3ColorSpace]: DisplayP3ColorSpaceImpl,\n [LinearDisplayP3ColorSpace]: LinearDisplayP3ColorSpaceImpl,\n});\n\n/**\n * Map a resolved output gamut to the three color-space constant for\n * `renderer.outputColorSpace`. `'p3'` selects Display P3; `'srgb'` the default.\n */\nexport function gamutToColorSpace(gamut: OutputGamut): string {\n return gamut === 'p3' ? DisplayP3ColorSpace : SRGBColorSpace;\n}\n\n/**\n * Minimal structural view of the WebGPU backend internals we must reach into.\n * three's public `Backend` type surfaces neither `device` nor `context`, but the\n * WebGPU backend sets both at init (`this.device`, `this.context`).\n */\ninterface WebGpuBackendInternals {\n device: GPUDevice;\n context: GPUCanvasContext;\n}\n\nfunction hasWebGpuBackendInternals(backend: unknown): backend is WebGpuBackendInternals {\n if (typeof backend !== 'object' || backend === null) return false;\n if (!('device' in backend) || !('context' in backend)) return false;\n\n const { device, context } = backend;\n\n return (\n typeof device === 'object' &&\n device !== null &&\n typeof context === 'object' &&\n context !== null &&\n 'configure' in context &&\n typeof context.configure === 'function'\n );\n}\n\n/**\n * Re-configure the WebGPU canvas context for the output gamut.\n *\n * Necessary because three 0.170's WebGPU backend configures the `GPUCanvasContext`\n * only at init, with no `colorSpace` field — so it defaults to sRGB and a P3\n * `outputColorSpace` would write P3-encoded pixels into an sRGB surface. We re-run\n * `configure()` once with `colorSpace: 'display-p3'`, mirroring three's other\n * config values. Critically, `alphaMode` must mirror three's choice: the WebGPU\n * backend defaults `alpha` to `true` and therefore configures the context as\n * `'premultiplied'` (transparent). Passing `'opaque'` here would override that and\n * paint the canvas opaque black until the first shader frame, producing a black\n * flash over whatever sits behind a transparent canvas. Usage matches the\n * backend's `RENDER_ATTACHMENT | COPY_SRC`. Resize never re-configures the\n * context, so this sticks for the renderer's lifetime.\n *\n * No-op for sRGB output, for the WebGL2 fallback (which stays sRGB in v1), and\n * where WebGPU is unavailable.\n */\nexport function applyCanvasGamut(\n renderer: WebGPURenderer,\n backend: GpuBackend,\n gamut: OutputGamut,\n): void {\n if (gamut !== 'p3' || backend !== 'webgpu') return;\n\n // `navigator.gpu` is typed as always-present but is genuinely absent on hosts\n // without WebGPU, so probe with `in` (a `=== undefined` check reads as dead).\n if (typeof navigator === 'undefined' || !('gpu' in navigator)) return;\n\n const webGpuBackend = renderer.backend;\n\n if (!hasWebGpuBackendInternals(webGpuBackend)) return;\n\n webGpuBackend.context.configure({\n device: webGpuBackend.device,\n format: navigator.gpu.getPreferredCanvasFormat(),\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,\n alphaMode: 'premultiplied',\n colorSpace: 'display-p3',\n });\n}\n","export type Vector2 = readonly [number, number];\n\nexport interface CursorInputOptions {\n /**\n * Smoothing factor: 0 = no smoothing (snap to target instantly).\n * 1 = max smoothing (essentially never reaches target).\n * Sensible default: 0.1.\n *\n * Implementation: per-frame, value moves toward target by `(1 - smoothing) * delta * 60`,\n * roughly meaning \"at smoothing=0.1, ~90% of the gap is closed in 1 second at 60fps.\"\n */\n smoothing?: number;\n /** Starting position. Default: [0.5, 0.5] (center). */\n initial?: Vector2;\n /** Listen on this target. Default: window. */\n target?: EventTarget;\n /**\n * Element to normalize cursor coordinates against. Default: window viewport.\n *\n * When set, cursor x/y are in [0,1] across the element's bounding rect, with\n * extrapolation outside (negative when left/above, >1 when right/below). This\n * matches what shader UV space expects: a cursor at the canvas's top-left\n * corner reads as (0, 0); at bottom-right as (1, 1); regardless of where the\n * canvas sits in the viewport. Without this, components inside a partial-\n * viewport scene (e.g. a 70vh hero section) see a cursor offset that scales\n * with the canvas's vertical position on the page.\n */\n element?: {\n getBoundingClientRect(): { left: number; top: number; width: number; height: number };\n };\n}\n\ntype ChangeListener = (value: Vector2) => void;\n\n/**\n * Smoothed pointer tracker emitting a normalized (0..1) Vec2 position.\n * Implements the AnimatableSignal protocol (`get()` + `on('change', cb)`)\n * so it composes with Motion's `useTransform` and similar tools.\n */\nexport class CursorInput {\n private value: [number, number];\n private target: [number, number];\n private targetDirty = false;\n private readonly smoothing: number;\n private readonly listeners = new Set<ChangeListener>();\n private readonly eventTarget: EventTarget;\n private readonly element: CursorInputOptions['element'];\n private readonly handleMouseMove: (e: Event) => void;\n private disposed = false;\n\n constructor(opts: CursorInputOptions = {}) {\n const { smoothing = 0.1, initial = [0.5, 0.5], target, element } = opts;\n\n this.smoothing = clamp01(smoothing);\n this.value = [initial[0], initial[1]];\n this.target = [initial[0], initial[1]];\n this.eventTarget = target ?? (typeof window !== 'undefined' ? window : new EventTarget());\n this.element = element;\n\n this.handleMouseMove = (e: Event) => {\n if (!(e instanceof MouseEvent)) return;\n const mouseEvent = e;\n\n if (this.element) {\n // Normalize to 0..1 across the element's bounding rect. Reading the\n // rect on every move is fine — `getBoundingClientRect` is cheap and\n // mousemove is already throttled to ~60Hz by the browser. The benefit\n // is tracking the element's position even if it moved/scrolled since\n // the last frame.\n const elementRect = this.element.getBoundingClientRect();\n const elementWidth = elementRect.width || 1;\n const elementHeight = elementRect.height || 1;\n\n this.target = [\n (mouseEvent.clientX - elementRect.left) / elementWidth,\n (mouseEvent.clientY - elementRect.top) / elementHeight,\n ];\n } else {\n // Fallback: viewport-normalized. Used when no element is supplied —\n // mostly the standalone-API case for users not consuming through\n // <ShaderScene>'s context.\n const viewportWidth = (typeof window !== 'undefined' && window.innerWidth) || 1;\n const viewportHeight = (typeof window !== 'undefined' && window.innerHeight) || 1;\n\n this.target = [mouseEvent.clientX / viewportWidth, mouseEvent.clientY / viewportHeight];\n }\n this.targetDirty = true;\n };\n\n this.eventTarget.addEventListener('mousemove', this.handleMouseMove);\n }\n\n /** Current smoothed position. Implements AnimatableSignal protocol. */\n get(): Vector2 {\n return this.value;\n }\n\n /** Subscribe to change events. Returns an unsubscribe function. */\n on(_eventType: 'change', changeListener: ChangeListener): () => void {\n this.listeners.add(changeListener);\n\n return () => this.listeners.delete(changeListener);\n }\n\n /**\n * Advance the smoothing one tick. Called by the host scheduler; not\n * typically called directly except in tests.\n */\n tick(delta: number): void {\n if (this.disposed) return;\n const factor = this.smoothing === 0 ? 1 : 1 - Math.pow(this.smoothing, delta * 60);\n const prev0 = this.value[0];\n const prev1 = this.value[1];\n const next0 = lerp(prev0, this.target[0], factor);\n const next1 = lerp(prev1, this.target[1], factor);\n const moved = next0 !== prev0 || next1 !== prev1;\n\n if (moved || this.targetDirty) {\n this.value = [next0, next1];\n this.targetDirty = false;\n const snapshot: Vector2 = [next0, next1];\n\n for (const listener of this.listeners) listener(snapshot);\n }\n }\n\n /** Tear down listeners. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.eventTarget.removeEventListener('mousemove', this.handleMouseMove);\n this.listeners.clear();\n }\n}\n\nconst clamp01 = (value: number) => Math.max(0, Math.min(1, value));\nconst lerp = (startValue: number, endValue: number, blendFactor: number) =>\n startValue + (endValue - startValue) * blendFactor;\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { clamp, div, sub, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { hueArcInterpolators } from '../color-space/hue.js';\nimport { colorSpaces } from '../color-space/registry.js';\nimport type { ColorSpace, HueInterpolation } from '../color-space/types.js';\n\n/**\n * Canonical TSL-node *input* shape used throughout `@lovo/matter`.\n *\n * Stays as the broad `Node | ShaderNodeObject<Node>` union so callers can\n * pass uniform-typed nodes (e.g. `ShaderNodeObject<UniformNode<Vector2>>`)\n * without casting at the call site — those are subtypes of `Node` but NOT\n * subtypes of `ShaderNodeObject<Node>` due to invariant generic parameters.\n *\n * Wrappers should return the narrower `ShaderNodeObject<Node>` so the\n * **output** is always chainable without casts.\n */\nexport type TSLNode = Node | ShaderNodeObject<Node>;\n\nexport interface ColorRampStop {\n /** Color expressed as a TSL node (typically `vec3(r,g,b)`), in linear-sRGB. */\n color: TSLNode;\n /** Position 0..1 along the ramp. */\n position: number;\n}\n\n/**\n * Multi-stop color interpolation. Given a t in [0..1] and N color stops at\n * fixed positions, returns the smoothly-interpolated color.\n *\n * `colorSpace` controls the interpolation space (default `'linear'` — a plain\n * per-channel mix that preserves the input values). Stops are converted into\n * the space up front, the nested-mix chain runs IN that space, and the result\n * is converted back to linear-sRGB once at the end.\n *\n * `hueInterpolation` chooses which way around the wheel cylindrical spaces\n * travel (default `'shorter'`); it's inert for rectangular spaces (linear/oklab).\n *\n * Falls back to the first/last stop's color outside the bracketing positions.\n */\nexport function colorRamp(\n t: TSLNode,\n stops: ColorRampStop[],\n colorSpace: ColorSpace = 'linear',\n hueInterpolation: HueInterpolation = 'shorter',\n): ShaderNodeObject<Node> {\n const space = colorSpaces[colorSpace];\n const hue = hueArcInterpolators[hueInterpolation];\n const first = stops[0];\n\n if (first === undefined) return vec3(0, 0, 0);\n\n const firstCoords = space.fromLinear(vec3(first.color));\n\n if (stops.length === 1) return space.toLinear(firstCoords);\n\n // Build a chain of nested mixes, one per adjacent pair of stops, working in\n // the interpolation space. The running result collapses to the previous stop\n // exactly at each segment boundary, so this stays clean pairwise interpolation\n // (and per-segment shortest-arc hue stays correct for cylindrical spaces).\n let resultCoords = firstCoords;\n\n for (let i = 1; i < stops.length; i += 1) {\n const previousStop = stops[i - 1];\n const next = stops[i];\n\n if (previousStop === undefined || next === undefined) continue;\n const positionSpan = next.position - previousStop.position;\n\n if (positionSpan <= 0) continue;\n // Localize t into the [prev..next] range. `t` is TSLNode (the union),\n // so we use functional-form ops to avoid needing a chain-method receiver.\n const localT = clamp(div(sub(t, previousStop.position), positionSpan), 0, 1);\n\n const nextCoords = space.fromLinear(vec3(next.color));\n\n resultCoords = space.lerp(resultCoords, nextCoords, localT, hue);\n }\n\n return space.toLinear(resultCoords);\n}\n","import { mod, sign, step } from 'three/tsl';\n\nimport type { ArcHueFn, HueInterpolation } from './types.js';\n\n// Below this sweep (radians or turns) two hues count as equal, so `decreasing`\n// doesn't fire a full backward spin between identical-hued stops.\nconst EQUAL_HUE_EPSILON = 1e-6;\n\n/**\n * `shorter` — travel the SHORTER arc (CSS Color 4 default). The signed delta is\n * wrapped into [-period/2, period/2) so the lerp never goes the long way.\n */\nexport const shortestArcHue: ArcHueFn = (h1, h2, t, period) => {\n const half = period / 2;\n const delta = mod(h2.sub(h1).add(half), period).sub(half);\n\n return h1.add(delta.mul(t));\n};\n\n/**\n * `longer` — travel the LONGER arc: take the shorter signed delta and step a\n * full period the other way. At exactly-equal hues `sign` is 0, so the delta\n * stays 0 (no surprise full-circle spin) rather than looping the wheel.\n */\nexport const longestArcHue: ArcHueFn = (h1, h2, t, period) => {\n const half = period / 2;\n const short = mod(h2.sub(h1).add(half), period).sub(half);\n const delta = short.sub(sign(short).mul(period));\n\n return h1.add(delta.mul(t));\n};\n\n/**\n * `increasing` — hue counts strictly UP (wrapping period→0). Delta in [0, period),\n * so a multi-stop ramp marches one way around the wheel without reversing.\n */\nexport const increasingArcHue: ArcHueFn = (h1, h2, t, period) => {\n const delta = mod(h2.sub(h1), period);\n\n return h1.add(delta.mul(t));\n};\n\n/**\n * `decreasing` — hue counts strictly DOWN. The upward delta in [0, period) has a\n * full period subtracted (unless the hues are equal, guarded by the epsilon), so\n * the result lands in (-period, 0].\n */\nexport const decreasingArcHue: ArcHueFn = (h1, h2, t, period) => {\n const up = mod(h2.sub(h1), period);\n const delta = up.sub(step(EQUAL_HUE_EPSILON, up).mul(period));\n\n return h1.add(delta.mul(t));\n};\n\n/** Resolves a `HueInterpolation` keyword to its arc function. */\nexport const hueArcInterpolators: Record<HueInterpolation, ArcHueFn> = {\n shorter: shortestArcHue,\n longer: longestArcHue,\n increasing: increasingArcHue,\n decreasing: decreasingArcHue,\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { abs, clamp, fract, max, min, mix, step, vec3, vec4 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { linearToSrgb, srgbToLinear } from './transfer.js';\nimport type { ColorSpaceImpl } from './types.js';\n\nconst EPSILON = 1e-10;\n\n/** Hocevar branchless hue (turns [0,1)) from gamma RGB — shared shape with HSV. */\nfunction gammaRgbHue(c: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const p = mix(vec4(c.b, c.g, -1 / 3, 2 / 3), vec4(c.g, c.b, 0, -1 / 3), step(c.b, c.g));\n const q = mix(vec4(p.x, p.y, p.w, c.r), vec4(c.r, p.y, p.z, p.x), step(p.x, c.r));\n const chroma = q.x.sub(min(q.w, q.y));\n\n return abs(q.z.add(q.w.sub(q.y).div(chroma.mul(6).add(EPSILON))));\n}\n\n/** gamma sRGB -> HSL (h, s, l). */\nfunction gammaRgbToHsl(c: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const maxChannel = max(c.r, max(c.g, c.b));\n const minChannel = min(c.r, min(c.g, c.b));\n const lightness = maxChannel.add(minChannel).mul(0.5);\n const chroma = maxChannel.sub(minChannel);\n // s = chroma / (1 - |2L - 1|)\n const saturation = chroma.div(abs(lightness.mul(2).sub(1)).oneMinus().add(EPSILON));\n\n return vec3(gammaRgbHue(c), saturation, lightness);\n}\n\n/** HSL (h, s, l) -> gamma sRGB. */\nfunction hslToGammaRgb(hsl: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const hue = hsl.x;\n const saturation = hsl.y;\n const lightness = hsl.z;\n\n const chroma = abs(lightness.mul(2).sub(1)).oneMinus().mul(saturation);\n // Per-channel triangle-wave hue ramp, same basis as Hocevar's hsv2rgb.\n const ramp = abs(\n fract(vec3(hue).add(vec3(1, 2 / 3, 1 / 3)))\n .mul(6)\n .sub(vec3(3)),\n );\n const hueRgb = clamp(ramp.sub(vec3(1)), 0, 1); // pure-hue color at full chroma\n\n return hueRgb.sub(0.5).mul(chroma).add(lightness);\n}\n\nexport const hslSpace: ColorSpaceImpl = {\n // Clamp into sRGB before the gamma transfer: HSL is an sRGB-gamut concept, and\n // the sRGB OETF's pow() can't be WGSL const-evaluated on the negative channels\n // of an out-of-sRGB (wide-gamut) stop color — that crashed the shader compile.\n fromLinear: (rgb) => gammaRgbToHsl(linearToSrgb(clamp(rgb, 0, 1))),\n toLinear: (hsl) => srgbToLinear(hslToGammaRgb(hsl)),\n lerp: (a, b, t, hue) => vec3(hue(a.x, b.x, t, 1), mix(a.y, b.y, t), mix(a.z, b.z, t)),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { mix, pow, step } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * sRGB-encoded channel in [0,1] -> linear-sRGB. Standard sRGB EOTF.\n * Mirrors three's `convertSRGBToLinear` (e.g. 0.5 -> 0.21404114).\n */\nexport function srgbChannelToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;\n}\n\n/** TSL: vec3 sRGB-encoded -> linear-sRGB (branchless via step/mix). */\nexport function srgbToLinear(srgb: TSLNode): ShaderNodeObject<Node> {\n // pow(srgb, 1) normalizes the TSLNode union into a chainable node (no-op).\n const value = pow(srgb, 1);\n const lowSegment = value.div(12.92);\n const highSegment = pow(value.add(0.055).div(1.055), 2.4);\n\n // step(0.04045, value) == 1 where value >= 0.04045 -> pick the high segment.\n return mix(lowSegment, highSegment, step(0.04045, value));\n}\n\n/** TSL: vec3 linear-sRGB -> sRGB-encoded (branchless via step/mix). Inverse OETF. */\nexport function linearToSrgb(linear: TSLNode): ShaderNodeObject<Node> {\n const value = pow(linear, 1);\n const lowSegment = value.mul(12.92);\n const highSegment = pow(value, 1 / 2.4)\n .mul(1.055)\n .sub(0.055);\n\n return mix(lowSegment, highSegment, step(0.0031308, value));\n}\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { abs, clamp, fract, min, mix, step, vec3, vec4 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { linearToSrgb, srgbToLinear } from './transfer.js';\nimport type { ColorSpaceImpl } from './types.js';\n\nconst EPSILON = 1e-10;\n\n// Sam Hocevar's branchless RGB->HSV. Reference (GLSL):\n// vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);\n// vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n// vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n// float d = q.x - min(q.w, q.y);\n// return vec3(abs(q.z + (q.w - q.y)/(6.0*d + e)), d/(q.x + e), q.x);\nfunction gammaRgbToHsv(c: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const p = mix(vec4(c.b, c.g, -1 / 3, 2 / 3), vec4(c.g, c.b, 0, -1 / 3), step(c.b, c.g));\n const q = mix(vec4(p.x, p.y, p.w, c.r), vec4(c.r, p.y, p.z, p.x), step(p.x, c.r));\n const chroma = q.x.sub(min(q.w, q.y));\n const hue = abs(q.z.add(q.w.sub(q.y).div(chroma.mul(6).add(EPSILON))));\n const saturation = chroma.div(q.x.add(EPSILON));\n\n return vec3(hue, saturation, q.x);\n}\n\n// Hocevar's branchless HSV->RGB. Reference (GLSL):\n// vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);\n// vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n// return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\nfunction hsvToGammaRgb(hsv: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const hue = hsv.x;\n const saturation = hsv.y;\n const value = hsv.z;\n const ramp = abs(\n fract(vec3(hue).add(vec3(1, 2 / 3, 1 / 3)))\n .mul(6)\n .sub(vec3(3)),\n );\n\n return mix(vec3(1), clamp(ramp.sub(vec3(1)), 0, 1), saturation).mul(value);\n}\n\nexport const hsvSpace: ColorSpaceImpl = {\n // Clamp into sRGB before the gamma transfer: HSV is an sRGB-gamut concept, and\n // the sRGB OETF's pow() can't be WGSL const-evaluated on the negative channels\n // of an out-of-sRGB (wide-gamut) stop color — that crashed the shader compile.\n fromLinear: (rgb) => gammaRgbToHsv(linearToSrgb(clamp(rgb, 0, 1))),\n toLinear: (hsv) => srgbToLinear(hsvToGammaRgb(hsv)),\n lerp: (a, b, t, hue) => vec3(hue(a.x, b.x, t, 1), mix(a.y, b.y, t), mix(a.z, b.z, t)),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { atan2, cbrt, cos, length, mix, sin, step, vec2, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { ColorSpaceImpl } from './types.js';\n\nconst TWO_PI = Math.PI * 2;\n\n// D65 reference white (CIE 1931 2°).\nconst WHITE_X = 0.95047;\nconst WHITE_Y = 1.0;\nconst WHITE_Z = 1.08883;\n\n// CIELAB nonlinearity constants.\nconst EPSILON = 216 / 24389; // ~0.008856\nconst KAPPA = 24389 / 27; // ~903.3\n\n/** CIELAB forward nonlinearity f(t), branchless via step/mix. */\nfunction labForward(ratio: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const linearPart = ratio.mul(KAPPA).add(16).div(116);\n const cubeRootPart = cbrt(ratio);\n\n return mix(linearPart, cubeRootPart, step(EPSILON, ratio));\n}\n\n/** CIELAB inverse nonlinearity, branchless via step/mix on f^3. */\nfunction labInverse(f: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const cubed = f.mul(f).mul(f);\n const linearPart = f.mul(116).sub(16).div(KAPPA);\n\n return mix(linearPart, cubed, step(EPSILON, cubed));\n}\n\n/** linear-sRGB -> CIELAB LCh (L, C, h). h in radians. */\nfunction linearToLch(rgb: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const r = rgb.r;\n const g = rgb.g;\n const b = rgb.b;\n\n // linear-sRGB -> CIE XYZ (D65).\n const x = r.mul(0.4123907993).add(g.mul(0.3575843394)).add(b.mul(0.1804807884));\n const y = r.mul(0.2126390059).add(g.mul(0.7151686788)).add(b.mul(0.0721923154));\n const z = r.mul(0.0193308187).add(g.mul(0.1191947798)).add(b.mul(0.9505321522));\n\n const fx = labForward(x.div(WHITE_X));\n const fy = labForward(y.div(WHITE_Y));\n const fz = labForward(z.div(WHITE_Z));\n\n const lightness = fy.mul(116).sub(16);\n const greenRed = fx.sub(fy).mul(500);\n const blueYellow = fy.sub(fz).mul(200);\n\n const chroma = length(vec2(greenRed, blueYellow));\n const hue = atan2(blueYellow, greenRed);\n\n return vec3(lightness, chroma, hue);\n}\n\n/** CIELAB LCh (L, C, h) -> linear-sRGB. */\nfunction lchToLinear(lch: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lightness = lch.x;\n const chroma = lch.y;\n const hue = lch.z;\n\n const greenRed = chroma.mul(cos(hue));\n const blueYellow = chroma.mul(sin(hue));\n\n const fy = lightness.add(16).div(116);\n const fx = fy.add(greenRed.div(500));\n const fz = fy.sub(blueYellow.div(200));\n\n const x = labInverse(fx).mul(WHITE_X);\n const y = labInverse(fy).mul(WHITE_Y);\n const z = labInverse(fz).mul(WHITE_Z);\n\n // CIE XYZ (D65) -> linear-sRGB.\n const r = x.mul(3.2409699419).sub(y.mul(1.5373831776)).sub(z.mul(0.4986107603));\n const g = x.mul(-0.9692436363).add(y.mul(1.8759675015)).add(z.mul(0.0415550574));\n const b = x.mul(0.0556300797).sub(y.mul(0.2039769589)).add(z.mul(1.0569715142));\n\n return vec3(r, g, b);\n}\n\nexport const lchSpace: ColorSpaceImpl = {\n fromLinear: linearToLch,\n toLinear: lchToLinear,\n lerp: (a, b, t, hue) => vec3(mix(a.x, b.x, t), mix(a.y, b.y, t), hue(a.z, b.z, t, TWO_PI)),\n};\n","import { mix, vec3 } from 'three/tsl';\n\nimport type { ColorSpaceImpl } from './types.js';\n\n/** Identity space: interpolate raw linear-sRGB values with no conversion. */\nexport const linearSpace: ColorSpaceImpl = {\n fromLinear: (rgb) => vec3(rgb),\n toLinear: (coords) => vec3(coords),\n lerp: (a, b, t) => mix(a, b, t),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { cbrt, mix, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { ColorSpaceImpl } from './types.js';\n\n/** linear-sRGB -> OKLab (L, a, b). */\nexport function linearToOklab(rgb: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const r = rgb.r;\n const g = rgb.g;\n const b = rgb.b;\n\n // Step 1: linear RGB -> LMS (cone response) via matrix M1.\n const longCone = r.mul(0.4122214708).add(g.mul(0.5363325363)).add(b.mul(0.0514459929));\n const mediumCone = r.mul(0.2119034982).add(g.mul(0.6806995451)).add(b.mul(0.1073969566));\n const shortCone = r.mul(0.0883024619).add(g.mul(0.2817188376)).add(b.mul(0.6299787005));\n\n // Step 2: the perceptual cube-root nonlinearity.\n const longRoot = cbrt(longCone);\n const mediumRoot = cbrt(mediumCone);\n const shortRoot = cbrt(shortCone);\n\n // Step 3: LMS' -> OKLab via matrix M2.\n const lightness = longRoot\n .mul(0.2104542553)\n .add(mediumRoot.mul(0.793617785))\n .sub(shortRoot.mul(0.0040720468));\n const greenRed = longRoot\n .mul(1.9779984951)\n .sub(mediumRoot.mul(2.428592205))\n .add(shortRoot.mul(0.4505937099));\n const blueYellow = longRoot\n .mul(0.0259040371)\n .add(mediumRoot.mul(0.7827717662))\n .sub(shortRoot.mul(0.808675766));\n\n return vec3(lightness, greenRed, blueYellow);\n}\n\n/** OKLab (L, a, b) -> linear-sRGB. */\nexport function oklabToLinear(lab: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lightness = lab.x;\n const greenRed = lab.y;\n const blueYellow = lab.z;\n\n // Inverse of step 3: OKLab -> LMS'.\n const longRoot = lightness.add(greenRed.mul(0.3963377774)).add(blueYellow.mul(0.2158037573));\n const mediumRoot = lightness.sub(greenRed.mul(0.1055613458)).sub(blueYellow.mul(0.0638541728));\n const shortRoot = lightness.sub(greenRed.mul(0.0894841775)).sub(blueYellow.mul(1.291485548));\n\n // Inverse of step 2: cube (x^3) to undo the cube root.\n const longCone = longRoot.mul(longRoot).mul(longRoot);\n const mediumCone = mediumRoot.mul(mediumRoot).mul(mediumRoot);\n const shortCone = shortRoot.mul(shortRoot).mul(shortRoot);\n\n // Inverse of step 1: LMS -> linear RGB.\n const r = longCone\n .mul(4.0767416621)\n .sub(mediumCone.mul(3.3077115913))\n .add(shortCone.mul(0.2309699292));\n const g = longCone\n .mul(-1.2684380046)\n .add(mediumCone.mul(2.6097574011))\n .sub(shortCone.mul(0.3413193965));\n const b = longCone\n .mul(-0.0041960863)\n .sub(mediumCone.mul(0.7034186147))\n .add(shortCone.mul(1.707614701));\n\n return vec3(r, g, b);\n}\n\nexport const oklabSpace: ColorSpaceImpl = {\n fromLinear: linearToOklab,\n toLinear: oklabToLinear,\n lerp: (a, b, t) => mix(a, b, t),\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { atan2, cos, length, mix, sin, vec2, vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { linearToOklab, oklabToLinear } from './oklab.js';\nimport type { ColorSpaceImpl } from './types.js';\n\nconst TWO_PI = Math.PI * 2;\n\n/** linear-sRGB -> OKLch (L, C, h). h in radians [-π, π]. */\nfunction linearToOklch(rgb: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lab = linearToOklab(rgb);\n const lightness = lab.x;\n const greenRed = lab.y;\n const blueYellow = lab.z;\n\n const chroma = length(vec2(greenRed, blueYellow));\n const hue = atan2(blueYellow, greenRed);\n\n return vec3(lightness, chroma, hue);\n}\n\n/** OKLch (L, C, h) -> linear-sRGB. */\nfunction oklchToLinear(lch: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const lightness = lch.x;\n const chroma = lch.y;\n const hue = lch.z;\n\n const greenRed = chroma.mul(cos(hue));\n const blueYellow = chroma.mul(sin(hue));\n\n return oklabToLinear(vec3(lightness, greenRed, blueYellow));\n}\n\nexport const oklchSpace: ColorSpaceImpl = {\n fromLinear: linearToOklch,\n toLinear: oklchToLinear,\n lerp: (a, b, t, hue) => vec3(mix(a.x, b.x, t), mix(a.y, b.y, t), hue(a.z, b.z, t, TWO_PI)),\n};\n","import { hslSpace } from './hsl.js';\nimport { hsvSpace } from './hsv.js';\nimport { lchSpace } from './lch.js';\nimport { linearSpace } from './linear.js';\nimport { oklabSpace } from './oklab.js';\nimport { oklchSpace } from './oklch.js';\nimport type { ColorSpace, ColorSpaceImpl } from './types.js';\n\n/** Maps each ColorSpace to its TSL implementation. */\nexport const colorSpaces: Record<ColorSpace, ColorSpaceImpl> = {\n linear: linearSpace,\n oklab: oklabSpace,\n oklch: oklchSpace,\n lch: lchSpace,\n hsl: hslSpace,\n hsv: hsvSpace,\n};\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { vec3 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\nimport { hueArcInterpolators } from './hue.js';\nimport { colorSpaces } from './registry.js';\nimport type { ColorSpace, HueInterpolation } from './types.js';\n\n/**\n * Blend two linear-sRGB colors in `colorSpace`: convert both endpoints into the\n * space, interpolate, convert back to linear-sRGB. `hueInterpolation` chooses\n * the hue-wheel direction for cylindrical spaces (default `'shorter'`; inert for\n * rectangular spaces). The result is NOT clamped — extended (out-of-sRGB) values\n * are preserved so a wide-gamut (P3) output can display them; an sRGB output\n * clamps per-channel at the framebuffer, identical to the prior behavior.\n */\nexport function mixColor(\n colorA: TSLNode,\n colorB: TSLNode,\n t: TSLNode,\n colorSpace: ColorSpace = 'oklab',\n hueInterpolation: HueInterpolation = 'shorter',\n): ShaderNodeObject<Node> {\n const space = colorSpaces[colorSpace];\n const hue = hueArcInterpolators[hueInterpolation];\n const a = space.fromLinear(vec3(colorA));\n const b = space.fromLinear(vec3(colorB));\n\n return space.toLinear(space.lerp(a, b, t, hue));\n}\n","import { srgbChannelToLinear } from './transfer.js';\n\n/**\n * OKLab (L, a, b) -> extended linear-sRGB. CPU mirror of the TSL `oklabToLinear`\n * in `oklab.ts` (same M2^-1 / cube / M1^-1 matrices). The result is NOT clamped:\n * colors outside sRGB return channels below 0 or above 1, which a wide-gamut\n * output can render and an sRGB output clamps at the framebuffer.\n */\nexport function oklabToLinearSrgb(\n lightness: number,\n greenRed: number,\n blueYellow: number,\n): [number, number, number] {\n const longRoot = lightness + 0.3963377774 * greenRed + 0.2158037573 * blueYellow;\n const mediumRoot = lightness - 0.1055613458 * greenRed - 0.0638541728 * blueYellow;\n const shortRoot = lightness - 0.0894841775 * greenRed - 1.291485548 * blueYellow;\n\n const longCone = longRoot * longRoot * longRoot;\n const mediumCone = mediumRoot * mediumRoot * mediumRoot;\n const shortCone = shortRoot * shortRoot * shortRoot;\n\n const red = 4.0767416621 * longCone - 3.3077115913 * mediumCone + 0.2309699292 * shortCone;\n const green = -1.2684380046 * longCone + 2.6097574011 * mediumCone - 0.3413193965 * shortCone;\n const blue = -0.0041960863 * longCone - 0.7034186147 * mediumCone + 1.707614701 * shortCone;\n\n return [red, green, blue];\n}\n\n/** OKLch (L, C, h-in-degrees) -> extended linear-sRGB. */\nexport function oklchToLinearSrgb(\n lightness: number,\n chroma: number,\n hueDegrees: number,\n): [number, number, number] {\n const hueRadians = (hueDegrees * Math.PI) / 180;\n const greenRed = chroma * Math.cos(hueRadians);\n const blueYellow = chroma * Math.sin(hueRadians);\n\n return oklabToLinearSrgb(lightness, greenRed, blueYellow);\n}\n\n/** Parse `50%` -> 0.5 or a bare number. `scale` is the value of 100% (default 1). */\nfunction parseComponent(token: string, scale: number): number {\n const trimmed = token.trim();\n\n if (trimmed.endsWith('%')) {\n return (parseFloat(trimmed.slice(0, -1)) / 100) * scale;\n }\n\n return parseFloat(trimmed);\n}\n\n/** Split `oklch(...)`/`oklab(...)` inner text into component tokens, dropping `/ alpha`. */\nfunction functionArgs(input: string, prefix: string): string[] {\n const inner = input.slice(prefix.length, input.lastIndexOf(')'));\n const beforeAlpha = inner.split('/')[0] ?? '';\n\n return beforeAlpha\n .trim()\n .split(/[\\s,]+/)\n .filter((token) => token.length > 0);\n}\n\n/**\n * Parse a color string to **extended** linear-sRGB. Accepts `#rrggbb`,\n * `oklab(L a b)`, and `oklch(L C H)` (CSS Color 4 syntax: L/C may be percentages,\n * H may carry a `deg` suffix, an optional `/ alpha` is parsed and dropped).\n * Throws on any other syntax.\n */\nexport function parseColorString(input: string): [number, number, number] {\n const value = input.trim();\n\n if (value.startsWith('#')) {\n const hex = value.slice(1);\n\n return [\n srgbChannelToLinear(parseInt(hex.slice(0, 2), 16) / 255),\n srgbChannelToLinear(parseInt(hex.slice(2, 4), 16) / 255),\n srgbChannelToLinear(parseInt(hex.slice(4, 6), 16) / 255),\n ];\n }\n\n if (value.startsWith('oklch(')) {\n const [lightnessToken, chromaToken, hueToken] = functionArgs(value, 'oklch(');\n\n if (lightnessToken === undefined || chromaToken === undefined || hueToken === undefined) {\n throw new Error(`Invalid oklch() color: \"${input}\"`);\n }\n\n const lightness = parseComponent(lightnessToken, 1);\n const chroma = parseComponent(chromaToken, 0.4);\n const hueDegrees = parseFloat(hueToken.replace(/deg$/, ''));\n\n return oklchToLinearSrgb(lightness, chroma, hueDegrees);\n }\n\n if (value.startsWith('oklab(')) {\n const [lightnessToken, aToken, bToken] = functionArgs(value, 'oklab(');\n\n if (lightnessToken === undefined || aToken === undefined || bToken === undefined) {\n throw new Error(`Invalid oklab() color: \"${input}\"`);\n }\n\n return oklabToLinearSrgb(\n parseComponent(lightnessToken, 1),\n parseComponent(aToken, 0.4),\n parseComponent(bToken, 0.4),\n );\n }\n\n throw new Error(`Unsupported color syntax: \"${input}\". Use #rrggbb, oklch(...), or oklab(...).`);\n}\n","import { mx_noise_float } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * 2D simplex noise sampled at a point. Returns a scalar TSL node in\n * approximately [-1, 1] (MaterialX's mx_noise_float is roughly that range).\n *\n * @param p — Vec2 TSL node (typically `uv()` or a scaled/offset uv).\n *\n * Built on top of three's `mx_noise_float`; we wrap it so consumers have a\n * stable import path through `@lovo/matter` and we can swap the\n * implementation if a different noise primitive proves better in practice.\n *\n * Returns `ShaderNodeObject<Node>` (chainable) rather than the broader\n * `TSLNode` union, so callers can `.add(...)`/`.mul(...)` without casting.\n */\nexport function simplexNoise(p: TSLNode): ShaderNodeObject<Node> {\n return mx_noise_float(p);\n}\n","import { add, mul } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\nimport { simplexNoise } from '../noise/noise.js';\n\nexport interface FractalNoiseOptions {\n /** Number of octaves to sum. JS-side number — fixed at TSL build time, not a uniform. Default: 4. */\n octaves?: number;\n /** Per-octave frequency multiplier. JS-side number. Default: 2. */\n lacunarity?: number;\n /** Per-octave amplitude multiplier. JS-side number. Default: 0.5. */\n gain?: number;\n}\n\n/**\n * Fractal Brownian Motion — sum of N octaves of 2D simplex noise.\n *\n * Each octave samples noise at a higher frequency (× `lacunarity`) and lower\n * amplitude (× `gain`) than the previous one, AND at a translated coordinate\n * so the octaves sample uncorrelated regions of noise space. Without the\n * per-octave translation, octaves at related frequencies tend to pile up\n * peaks and troughs at the same input coordinates, producing visibly muddy\n * \"spotty\" output. With it, the octaves look like independent noise patterns\n * layered together — Inigo Quilez's classic FBM technique.\n *\n * `octaves`, `lacunarity`, and `gain` are JavaScript numbers (NOT TSL\n * uniforms) because the loop must be unrolled at TSL-build time — TSL has\n * no dynamic-length loop primitive that maps cleanly to all backends.\n * Animatable parameters that *do* survive on the GPU are the input UV\n * (which the caller can scale/translate per frame) and `time`.\n *\n * Returns `ShaderNodeObject<Node>` (chainable) for cast-free call sites.\n *\n * @param p — Vec2 or Vec3 TSL node (UV-space position).\n * @returns scalar TSL node, normalized to roughly [-1..1] regardless of\n * octave count thanks to the amplitude-sum division at the end.\n */\nexport function fractalNoise(p: TSLNode, opts: FractalNoiseOptions = {}): ShaderNodeObject<Node> {\n const octaves = opts.octaves ?? 4;\n const lacunarity = opts.lacunarity ?? 2;\n const gain = opts.gain ?? 0.5;\n\n let sum: ShaderNodeObject<Node> = simplexNoise(p);\n let amplitude = 1;\n let frequency = 1;\n let total = amplitude;\n\n for (let i = 1; i < octaves; i += 1) {\n frequency *= lacunarity;\n amplitude *= gain;\n total += amplitude;\n // Per-octave decorrelation: translate the sample point by a growing\n // offset so this octave reads from a totally different region of noise\n // space than the previous one. Magnitude 100 is well past simplex\n // noise's ~1-unit feature size, so adjacent octaves are fully\n // decorrelated. The scalar broadcasts across all components of `p`\n // (works for vec2 and vec3 inputs alike).\n //\n // Build the chain functionally from `p`: gotcha #12 doesn't apply\n // because `p` is uv-rooted, but the TSLNode union still requires\n // functional form on this hop.\n const pAtFreq = add(mul(p, frequency), i * 100);\n const layer = simplexNoise(pAtFreq).mul(amplitude);\n\n sum = sum.add(layer);\n }\n\n // Normalize to approximate [-1..1] regardless of octave count / gain.\n return sum.div(total);\n}\n","import { mx_worley_noise_float } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * 2D voronoi (Worley) noise — distance to the nearest jittered cell point,\n * normalized roughly to [0, 1]. Higher values = farther from any cell point\n * (cell interiors); lower values = near a cell boundary.\n *\n * Built on three's `mx_worley_noise_float`. Combine with `colorRamp` for\n * a multi-color cellular pattern; threshold via `step`/`smoothstep` for\n * hard cell shapes.\n *\n * Returns `ShaderNodeObject<Node>` (chainable) for cast-free call sites.\n *\n * @param p — Vec2 TSL node, typically `uv() * scale`.\n */\nexport function voronoi(p: TSLNode): ShaderNodeObject<Node> {\n return mx_worley_noise_float(p);\n}\n","import type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\n/**\n * Quantize a scalar TSL node to `steps` discrete levels.\n *\n * quantize(t, 4) → values in {0, 0.25, 0.5, 0.75, 1.0}\n *\n * `steps` is a JS-side number (loop-equivalent at TSL build time, baked in).\n * If you need an animatable step count, rebuild the TSL fragment.\n */\nexport function quantize(t: ShaderNodeObject<Node>, steps: number): ShaderNodeObject<Node> {\n if (steps <= 1) {\n // Edge case: single step → constant 0. Return as-is wrapped in mul(0).\n return t.mul(0);\n }\n const denominator = steps - 1;\n\n // floor(t * (steps-1) + 0.5) / (steps-1)\n // Using floor(x + 0.5) instead of round() for TSL portability.\n return t.mul(denominator).add(0.5).floor().div(denominator);\n}\n","import { length } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * Signed distance field for a circle centered at the origin.\n *\n * sdfCircle(p, r) = length(p) - r\n *\n * Negative inside the circle, zero on the boundary, positive outside.\n * Combine with `smoothstep(-edge, +edge, sdf)` to render a soft-edged disk.\n *\n * @param p — Vec2 TSL node (typically a UV-space offset from the center).\n * @param radius — JS-side scalar OR a scalar TSL node.\n */\nexport function signedDistanceFieldCircle(\n p: TSLNode,\n radius: TSLNode | number,\n): ShaderNodeObject<Node> {\n return length(p).sub(radius);\n}\n","import { add } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * Naive vector addition: returns `p + by`.\n *\n * displace(p, by) = p + by\n *\n * Thin wrapper that names the spatial intent of shifting a sample point.\n *\n * **SDF caveat:** when using this to translate an SDF render, pass the\n * NEGATED translation — `sdfCircle(displace(p, v.mul(-1)), r)` renders the\n * disk at position `+v` because SDF translation evaluates as\n * `length(p - center) - r`. Adding `+v` to the sample point shifts the\n * rendered shape in the OPPOSITE direction.\n *\n * @param p — Vec2 TSL node (the position being displaced).\n * @param by — Vec2 TSL node (the displacement vector).\n */\nexport function displace(p: TSLNode, by: TSLNode): ShaderNodeObject<Node> {\n return add(p, by);\n}\n","import { length, sin, smoothstep, sub } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\nimport { elapsedTime } from '../time/time.js';\n\nexport interface CursorRippleOptions {\n /** Decay radius (UV space). Beyond this, the ripple is ~0. Default: 0.4. */\n reach?: number;\n /** Wavelength controls the ripple spacing. Default: 30. Larger = wider rings. */\n frequency?: number;\n /** Time multiplier on the wave phase. Default: 6. Larger = faster oscillation. */\n speed?: number;\n /** Output amplitude. Default: 0.5. Final result is in roughly [-amplitude, +amplitude]. */\n amplitude?: number;\n}\n\n/**\n * A radial ripple emanating from `center`. Returns a scalar TSL node in\n * roughly [-amplitude, +amplitude] that decays to ~0 outside `reach`.\n *\n * ripple = sin(d*frequency - time*speed) * amplitude * smoothstep(reach, 0, d)\n *\n * Compose into a wave field by adding it to the underlying base wave.\n *\n * Note: `frequency` / `speed` / `reach` / `amplitude` are JS-side numbers\n * (baked into the TSL fragment at material-build time). The animatable\n * cursor position is the only live uniform consumed.\n *\n * @param p — Vec2 TSL node (typically `uv()`).\n * @param center — Vec2 TSL node (cursor uniform, in UV space).\n */\nexport function cursorRipple(\n p: TSLNode,\n center: TSLNode,\n opts: CursorRippleOptions = {},\n): ShaderNodeObject<Node> {\n const reach = opts.reach ?? 0.4;\n const frequency = opts.frequency ?? 30;\n const speed = opts.speed ?? 6;\n const amplitude = opts.amplitude ?? 0.5;\n\n // d = length(p - center). Use functional `sub(p, center)` because both\n // are typed as the broad TSLNode union (no chain receiver). Per gotcha #12,\n // building from a raw `uniform()` receiver silently produces wrong GPU\n // values, so the functional form is also safer for `center` being a uniform.\n const d = length(sub(p, center));\n // `time` is the engine-gated TSL node (from primitives/time/time.ts);\n // chains rooted in `time` automatically respect `prefers-reduced-motion` and\n // the runtime override set via `setReducedMotionPolicy`.\n const wave = sin(d.mul(frequency).sub(elapsedTime.mul(speed)));\n const decay = smoothstep(reach, 0, d);\n\n return wave.mul(amplitude).mul(decay);\n}\n","// Engine-gated `time` — equals the TSL built-in `time` multiplied by the\n// reduced-motion scale uniform. Components consuming `time` from `@lovo/matter`\n// automatically respect `prefers-reduced-motion` and the policy override set\n// via `setReducedMotionPolicy`.\n//\n// If you want raw uncapped time (e.g. for a debug overlay), import `time`\n// from `three/tsl` directly.\nimport { time as _builtinTime } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport { getReducedMotionTimeScale } from '../../runtime/reduced-motion/reduced-motion.js';\n\nexport const elapsedTime: ShaderNodeObject<Node> = _builtinTime.mul(getReducedMotionTimeScale());\n","import { uniform } from 'three/tsl';\n\nexport type ReducedMotionPolicy = 'auto' | 'off' | 'slow' | 'paused';\n\n/**\n * Public surface exposed to package consumers. `recompute` is intentionally\n * absent — it is engine-internal and should not be callable from outside.\n */\nexport interface ReducedMotionWatcher {\n /** Current time scale: 0, 0.3, or 1. */\n scale(): number;\n /** Subscribe to scale changes. Returns unsubscribe. */\n subscribe(cb: (scale: number) => void): () => void;\n /** Tear down media-query listener. */\n dispose(): void;\n}\n\n/**\n * Engine-internal extension of the public watcher. Only `setReducedMotionPolicy`\n * calls `recompute`; it is never part of the consumer-visible type.\n */\ninterface InternalWatcher extends ReducedMotionWatcher {\n recompute(): void;\n}\n\ninterface PolicyState {\n policy: ReducedMotionPolicy;\n watchers: Set<InternalWatcher>;\n}\n\nconst state: PolicyState = {\n policy: 'auto',\n watchers: new Set(),\n};\n\n/**\n * Override Matter's default behavior of honoring `prefers-reduced-motion`.\n * - 'auto' — follow the OS media query (default)\n * - 'off' — full speed regardless of OS setting\n * - 'slow' — 30% speed regardless of OS setting\n * - 'paused' — 0 (animation effectively frozen) regardless of OS setting\n */\nexport function setReducedMotionPolicy(policy: ReducedMotionPolicy): void {\n if (state.policy === policy) return;\n state.policy = policy;\n for (const watcher of state.watchers) watcher.recompute();\n}\n\nexport function getReducedMotionPolicy(): ReducedMotionPolicy {\n return state.policy;\n}\n\nconst computeScale = (mqlMatches: boolean): number => {\n switch (state.policy) {\n case 'off':\n return 1;\n case 'slow':\n return 0.3;\n case 'paused':\n return 0;\n case 'auto':\n return mqlMatches ? 0.3 : 1;\n }\n};\n\n/**\n * Create a watcher that tracks `prefers-reduced-motion: reduce` and the\n * global Matter policy override. Strict-mode-safe — callers create+dispose\n * one per mount cycle.\n */\nexport function createReducedMotionWatcher(): ReducedMotionWatcher {\n // SSR safety: bail to the no-op watcher if matchMedia is missing.\n // SSR watcher: scale() respects policy override but does not emit\n // subscription events (the engine has no way to notify SSR-created\n // watchers because they are not added to state.watchers — but in\n // practice CLAUDE.md gotcha #10 requires `ssr: false` for any component\n // that touches the matter engine).\n if (typeof matchMedia !== 'function') {\n return {\n scale: () => computeScale(false),\n subscribe: (listener) => {\n // No-op: SSR watchers are not in state.watchers and will never\n // receive policy-change notifications.\n void listener;\n\n return () => {\n // SSR no-op unsubscribe\n };\n },\n /** SSR watcher does not emit policy-change notifications. */\n dispose: () => {\n // SSR no-op dispose\n },\n };\n }\n\n const mediaQueryList = matchMedia('(prefers-reduced-motion: reduce)');\n const subscriptions = new Set<(scale: number) => void>();\n let lastComputedScale = computeScale(mediaQueryList.matches);\n\n const onChange = () => {\n const next = computeScale(mediaQueryList.matches);\n\n if (next !== lastComputedScale) {\n lastComputedScale = next;\n for (const listener of subscriptions) listener(next);\n }\n };\n\n mediaQueryList.addEventListener('change', onChange);\n\n const watcher: InternalWatcher = {\n scale: () => lastComputedScale,\n subscribe(listener) {\n subscriptions.add(listener);\n\n return () => subscriptions.delete(listener);\n },\n recompute() {\n const next = computeScale(mediaQueryList.matches);\n\n if (next !== lastComputedScale) {\n lastComputedScale = next;\n for (const listener of subscriptions) listener(next);\n }\n },\n dispose() {\n mediaQueryList.removeEventListener('change', onChange);\n subscriptions.clear();\n state.watchers.delete(watcher);\n },\n };\n\n state.watchers.add(watcher);\n\n return watcher;\n}\n\nlet globalScaleUniform: ReturnType<typeof uniform<number>> | null = null;\nlet globalWatcher: ReducedMotionWatcher | null = null;\n\n/**\n * Returns the engine-shared TSL uniform that `time` is multiplied by. Lazily\n * initialized on first read; reused across all materials. Mutating `.value`\n * imperatively when policy changes is safe — TSL re-reads the uniform every\n * frame.\n */\nexport function getReducedMotionTimeScale(): ReturnType<typeof uniform<number>> {\n if (globalScaleUniform === null) {\n globalWatcher = createReducedMotionWatcher();\n globalScaleUniform = uniform(globalWatcher.scale());\n globalWatcher.subscribe((s) => {\n if (globalScaleUniform === null) return;\n globalScaleUniform.value = s;\n });\n }\n\n return globalScaleUniform;\n}\n\n// Keep a typed reference for tests that may want to re-init between tests.\nexport const resetReducedMotionForTests = () => {\n globalWatcher?.dispose();\n globalWatcher = null;\n globalScaleUniform = null;\n};\n","import type { WebGPURenderer } from 'three/webgpu';\n\n/**\n * Internal shape of three's per-renderer frame clock. `nodeFrame` is not part\n * of three's public types — it lives at `renderer._nodes.nodeFrame` — so we\n * reach it through guarded `unknown` traversal rather than a typed access.\n */\ninterface NodeFrameClock {\n time?: number;\n deltaTime?: number;\n lastTime?: number;\n}\n\nfunction getNodeFrame(renderer: WebGPURenderer): NodeFrameClock | undefined {\n const candidate: unknown = renderer;\n\n if (!(typeof candidate === 'object' && candidate !== null && '_nodes' in candidate)) {\n return undefined;\n }\n // After the `'_nodes' in candidate` guard, TS narrows to `{ _nodes: unknown }`.\n const nodes: unknown = candidate._nodes;\n\n if (!(typeof nodes === 'object' && nodes !== null && 'nodeFrame' in nodes)) {\n return undefined;\n }\n // After the `'nodeFrame' in nodes` guard, TS narrows to `{ nodeFrame: unknown }`.\n const frame: unknown = nodes.nodeFrame;\n\n if (typeof frame !== 'object' || frame === null) return undefined;\n\n // `frame` is narrowed to a non-null object; all NodeFrameClock fields are\n // optional so the narrowed type is directly assignable.\n return frame;\n}\n\n/**\n * Zero the renderer's animation clock so the next rendered frame is t=0.\n *\n * `elapsedTime` (and three's built-in `time`) accumulate real frame deltas from\n * the moment the renderer starts, which includes a nondeterministic WebGPU\n * init + shader-compile warmup. Resetting the per-renderer `nodeFrame` clock\n * makes every shader start from a fixed phase, so the first visible frame\n * matches the deterministic poster/snapshot frame.\n *\n * Per-renderer by construction: each ShaderScene owns one renderer, so resetting\n * here isolates scenes from one another. No-ops safely if three's internal\n * shape ever changes.\n */\nexport function resetRendererClock(renderer: WebGPURenderer): void {\n const nodeFrame = getNodeFrame(renderer);\n\n if (!nodeFrame) return;\n nodeFrame.time = 0;\n nodeFrame.deltaTime = 0;\n nodeFrame.lastTime = undefined;\n}\n","import { float, hash, screenCoordinate } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\ntype TSLScalar = TSLNode | number;\n\nexport function grain(intensity: TSLScalar, timeOffset: TSLScalar = 0): ShaderNodeObject<Node> {\n const pixel = screenCoordinate.xy.floor();\n // Convert to uint up front so all seed arithmetic stays in exact integer\n // space — float32 loses integer precision above 2^24 (~16.7M), which a\n // pixel-index seed on a 4K+ screen can reach.\n const column = pixel.x.toUint();\n const row = pixel.y.toUint();\n\n // Build the seed by NESTING hashes rather than summing a single linear term.\n // A single linear seed (a*x + b*y + c*t) makes the grain a 1-D function with\n // a fixed gradient axis (a, b): advancing t shifts that function along the\n // axis, and even folding t in by XOR leaves a partial pseudo-shift along it —\n // both read as grain \"drifting\" in one direction. Nesting scrambles each axis\n // before they meet, so there is no shared gradient axis: every frame is an\n // independent, isotropic field that boils in place with no directional drift.\n const frameHash = hash(float(timeOffset)).mul(0xffffff).toUint();\n const rowHash = hash(row.add(frameHash)).mul(0xffffff).toUint();\n const seed = column.add(rowHash);\n\n return hash(seed).sub(0.5).mul(intensity);\n}\n","import type { ShaderNodeObject } from 'three/tsl';\nimport { floor, fract, screenCoordinate, vec2, vec3, vec4 } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nimport type { TSLNode } from '../color-ramp/color-ramp.js';\n\n/**\n * Ordered Bayer dithering, built recursively from the 2x2 base pattern.\n *\n * `bayer2` is the canonical 2x2 ordered-dither cell in closed form\n * (`fract(x/2 + y·y·0.75)`); `bayer4`/`bayer8` refine it by adding a quarter of\n * the next-finer cell sampled at half the frequency. `bayer8` yields a value in\n * `[0, 1)` that tiles an 8x8 threshold map across the pixel grid.\n */\nfunction bayer2(coord: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n const cell = floor(coord);\n\n return fract(cell.x.mul(0.5).add(cell.y.mul(cell.y).mul(0.75)));\n}\n\nfunction bayer4(coord: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n return bayer2(coord.mul(0.5)).mul(0.25).add(bayer2(coord));\n}\n\nfunction bayer8(coord: ShaderNodeObject<Node>): ShaderNodeObject<Node> {\n return bayer4(coord.mul(0.5)).mul(0.25).add(bayer2(coord));\n}\n\n/**\n * Add a sub-LSB ordered dither to break up 8-bit quantization banding (most\n * visible on smooth gradients and on wide-gamut/P3 output, where the same 256\n * levels span a wider gamut so each step is a coarser perceptual jump).\n *\n * `amount` is the noise magnitude in the color's units (default ~1/255, roughly\n * one 8-bit step). The dither is an ordered Bayer 8x8 pattern keyed on the\n * per-pixel `screenCoordinate`, so the grain is a crisp one device-pixel cell\n * regardless of geometry or zoom, and is deterministic (no temporal shimmer —\n * important for static scenes).\n *\n * For correctness the dither belongs in display-encoded space, immediately\n * before 8-bit quantization. In a Matter-managed scene that placement is handled\n * for you (`ShaderScene` applies it as a final output stage after the color\n * transfer). This primitive is the entry point for Mode 2 (your own r3f canvas),\n * where you apply it to your shader's output yourself.\n */\nexport function dither(color: TSLNode, amount = 1 / 255): ShaderNodeObject<Node> {\n // Bayer cell in [0, 1); center to [-0.5, 0.5) so the dither is unbiased.\n const threshold = bayer8(vec2(screenCoordinate.xy)).sub(0.5);\n\n // Dither only the color channels and pass the source alpha through unchanged.\n // Collapsing to a vec3 here would let the consuming material default alpha to\n // 1, turning an otherwise-transparent output (e.g. ShaderScene's empty frames\n // before the shader mesh mounts) into opaque black — a flash over whatever\n // sits behind the canvas. `vec4(color).a` reads the original alpha for a vec4\n // input and defaults to 1 for a vec3 (three pads the missing component with\n // 1.0), so both arities are preserved correctly.\n return vec4(vec3(color).add(threshold.mul(amount)), vec4(color).a);\n}\n","export interface VisibilityWatcher {\n isVisible(): boolean;\n /** Subscribe to changes. Receives the new visibility state. Returns unsubscribe. */\n subscribe(cb: (visible: boolean) => void): () => void;\n dispose(): void;\n}\n\n/**\n * Watch `document.visibilityState`. Strict-mode-safe — callers create+dispose\n * one per mount cycle.\n *\n * SSR: if `document` is unavailable, returns a no-op watcher whose\n * `isVisible()` always returns `true` and whose `subscribe` does nothing.\n */\nexport function createVisibilityWatcher(): VisibilityWatcher {\n if (typeof document === 'undefined') {\n return {\n isVisible: () => true,\n subscribe: () => () => {\n // SSR no-op unsubscribe\n },\n dispose: () => {\n // SSR no-op dispose\n },\n };\n }\n\n const subscriptions = new Set<(visible: boolean) => void>();\n const onChange = () => {\n const isVisible = document.visibilityState === 'visible';\n\n for (const listener of subscriptions) listener(isVisible);\n };\n\n document.addEventListener('visibilitychange', onChange);\n\n return {\n isVisible: () => document.visibilityState === 'visible',\n subscribe(listener) {\n subscriptions.add(listener);\n\n return () => subscriptions.delete(listener);\n },\n dispose() {\n document.removeEventListener('visibilitychange', onChange);\n subscriptions.clear();\n },\n };\n}\n","export interface IntersectionWatcher {\n isInView(): boolean;\n /** Subscribe to changes. Receives the new in-view state. Returns unsubscribe. */\n subscribe(cb: (inView: boolean) => void): () => void;\n dispose(): void;\n}\n\n/**\n * Watch a canvas's viewport intersection. Pauses tied to this watcher should\n * be resumed when the canvas is *any* fraction visible. Strict-mode-safe.\n *\n * SSR: if `IntersectionObserver` is unavailable, returns a no-op watcher whose\n * `isInView()` always returns `true` and whose `subscribe` does nothing.\n */\nexport function createIntersectionWatcher(canvas: HTMLCanvasElement): IntersectionWatcher {\n if (typeof IntersectionObserver === 'undefined') {\n return {\n isInView: () => true,\n subscribe: () => () => {\n // SSR no-op unsubscribe\n },\n dispose: () => {\n // SSR no-op dispose\n },\n };\n }\n\n const subscriptions = new Set<(inView: boolean) => void>();\n let inView = true;\n const observer = new IntersectionObserver(\n (entries) => {\n const next = entries.some((entry) => entry.isIntersecting);\n\n if (next === inView) return;\n inView = next;\n for (const listener of subscriptions) listener(inView);\n },\n { threshold: 0 },\n );\n\n observer.observe(canvas);\n\n return {\n isInView: () => inView,\n subscribe(listener) {\n subscriptions.add(listener);\n\n return () => subscriptions.delete(listener);\n },\n dispose() {\n observer.disconnect();\n subscriptions.clear();\n },\n };\n}\n","export interface SchedulerTick {\n delta: number;\n elapsed: number;\n now: number;\n}\n\nexport type SchedulerClient = (tick: SchedulerTick) => void;\n\nexport class FrameScheduler {\n private readonly clients = new Set<SchedulerClient>();\n private rafId: number | null = null;\n private running = false;\n private paused = false;\n private flushPending = false;\n private startedAt = 0;\n private lastTickAt = 0;\n\n // Reference-counted idle voting. The scheduler is idle only when at least\n // one component has voted idle AND no component has voted animated. This\n // prevents a static component (e.g. LinearGradient speed=0) from halting\n // the loop while an animated overlay (e.g. Grain) is still running.\n private idleVotes = 0;\n private animatedVotes = 0;\n\n /** True when all participating components prefer idle and none need animation. */\n get idle(): boolean {\n return this.idleVotes > 0 && this.animatedVotes === 0;\n }\n\n /** Activate the scheduler. The rAF loop starts on the first client added. */\n start(): void {\n this.running = true;\n this.paused = false;\n this.maybeQueue();\n }\n\n /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */\n stop(): void {\n this.running = false;\n this.cancel();\n }\n\n /** Temporarily skip ticks without losing client registrations. */\n pause(): void {\n this.paused = true;\n }\n\n /** Resume after pause(). */\n resume(): void {\n this.paused = false;\n if (this.running) this.maybeQueue();\n }\n\n /** Register a client to be called every frame. */\n add(client: SchedulerClient): void {\n this.clients.add(client);\n if (this.running) this.maybeQueue();\n }\n\n /** Unregister a client. */\n remove(client: SchedulerClient): void {\n this.clients.delete(client);\n }\n\n /** Permanent teardown: stop the loop and drop all clients. */\n dispose(): void {\n this.stop();\n this.clients.clear();\n }\n\n /**\n * Cast a vote on whether the scheduler should be idle.\n *\n * `setIdle(true)` increments the idle-vote count; the returned cleanup\n * decrements it. `setIdle(false)` increments the animated-vote count;\n * its cleanup decrements that. The scheduler halts (after one flush tick)\n * only when `idleVotes > 0 && animatedVotes === 0`.\n *\n * Callers are responsible for calling the returned cleanup on unmount.\n * Use `requestRender()` or cast a `setIdle(false)` vote to wake the loop\n * without permanently registering an animated preference.\n */\n setIdle(idle: boolean): () => void {\n if (idle) {\n const wasIdle = this.idle;\n\n this.idleVotes += 1;\n const nowIdle = this.idle;\n\n if (!wasIdle && nowIdle) this.onBecameIdle();\n\n return () => {\n const prevIdle = this.idle;\n\n this.idleVotes = Math.max(0, this.idleVotes - 1);\n const afterIdle = this.idle;\n\n if (prevIdle && !afterIdle) this.onBecameAnimated();\n };\n } else {\n const wasIdle = this.idle;\n\n this.animatedVotes += 1;\n if (wasIdle) this.onBecameAnimated();\n\n return () => {\n const prevIdle = this.idle;\n\n this.animatedVotes = Math.max(0, this.animatedVotes - 1);\n const nowIdle = this.idle;\n\n if (!prevIdle && nowIdle) this.onBecameIdle();\n };\n }\n }\n\n /** Force a single tick while idle. Useful for prop-change invalidation. */\n requestRender(): void {\n if (!this.idle) return;\n this.flushPending = true;\n this.maybeQueue();\n }\n\n private onBecameIdle(): void {\n this.flushPending = true;\n this.maybeQueue();\n }\n\n private onBecameAnimated(): void {\n this.flushPending = false;\n this.maybeQueue();\n }\n\n private maybeQueue(): void {\n if (this.rafId !== null) return;\n if (!this.running) return;\n if (this.clients.size === 0) return;\n if (this.idle && !this.flushPending) return;\n this.rafId = requestAnimationFrame(this.frame);\n }\n\n private cancel(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n private readonly frame = (now: number): void => {\n this.rafId = null;\n if (!this.running || this.paused) return;\n\n if (this.startedAt === 0) {\n this.startedAt = now;\n this.lastTickAt = now;\n }\n const delta = (now - this.lastTickAt) / 1000;\n const elapsed = (now - this.startedAt) / 1000;\n\n this.lastTickAt = now;\n\n const tick: SchedulerTick = { delta, elapsed, now };\n\n for (const client of this.clients) {\n client(tick);\n }\n\n this.flushPending = false;\n this.maybeQueue();\n };\n}\n"],"mappings":"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","names":["import_three","import_tsl","import_tsl","import_tsl","import_tsl","EPSILON","import_tsl","EPSILON","import_tsl","import_tsl","import_tsl","TWO_PI","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","import_tsl","_builtinTime","import_tsl","import_tsl"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -314,24 +314,42 @@ declare function cursorRipple(p: TSLNode, center: TSLNode, opts?: CursorRippleOp
|
|
|
314
314
|
|
|
315
315
|
declare const elapsedTime: ShaderNodeObject<Node>;
|
|
316
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Zero the renderer's animation clock so the next rendered frame is t=0.
|
|
319
|
+
*
|
|
320
|
+
* `elapsedTime` (and three's built-in `time`) accumulate real frame deltas from
|
|
321
|
+
* the moment the renderer starts, which includes a nondeterministic WebGPU
|
|
322
|
+
* init + shader-compile warmup. Resetting the per-renderer `nodeFrame` clock
|
|
323
|
+
* makes every shader start from a fixed phase, so the first visible frame
|
|
324
|
+
* matches the deterministic poster/snapshot frame.
|
|
325
|
+
*
|
|
326
|
+
* Per-renderer by construction: each ShaderScene owns one renderer, so resetting
|
|
327
|
+
* here isolates scenes from one another. No-ops safely if three's internal
|
|
328
|
+
* shape ever changes.
|
|
329
|
+
*/
|
|
330
|
+
declare function resetRendererClock(renderer: WebGPURenderer): void;
|
|
331
|
+
|
|
317
332
|
type TSLScalar = TSLNode | number;
|
|
318
|
-
declare function
|
|
333
|
+
declare function grain(intensity: TSLScalar, timeOffset?: TSLScalar): ShaderNodeObject<Node>;
|
|
319
334
|
|
|
320
335
|
/**
|
|
321
|
-
* Add sub-LSB dither to break up 8-bit quantization banding (most
|
|
322
|
-
* wide-gamut/P3 output, where the same 256
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* magnitude in the color's units (default ~1/255, roughly
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
336
|
+
* Add a sub-LSB ordered dither to break up 8-bit quantization banding (most
|
|
337
|
+
* visible on smooth gradients and on wide-gamut/P3 output, where the same 256
|
|
338
|
+
* levels span a wider gamut so each step is a coarser perceptual jump).
|
|
339
|
+
*
|
|
340
|
+
* `amount` is the noise magnitude in the color's units (default ~1/255, roughly
|
|
341
|
+
* one 8-bit step). The dither is an ordered Bayer 8x8 pattern keyed on the
|
|
342
|
+
* per-pixel `screenCoordinate`, so the grain is a crisp one device-pixel cell
|
|
343
|
+
* regardless of geometry or zoom, and is deterministic (no temporal shimmer —
|
|
344
|
+
* important for static scenes).
|
|
345
|
+
*
|
|
346
|
+
* For correctness the dither belongs in display-encoded space, immediately
|
|
347
|
+
* before 8-bit quantization. In a Matter-managed scene that placement is handled
|
|
348
|
+
* for you (`ShaderScene` applies it as a final output stage after the color
|
|
349
|
+
* transfer). This primitive is the entry point for Mode 2 (your own r3f canvas),
|
|
350
|
+
* where you apply it to your shader's output yourself.
|
|
333
351
|
*/
|
|
334
|
-
declare function dither(color: TSLNode,
|
|
352
|
+
declare function dither(color: TSLNode, amount?: number): ShaderNodeObject<Node>;
|
|
335
353
|
|
|
336
354
|
type ReducedMotionPolicy = 'auto' | 'off' | 'slow' | 'paused';
|
|
337
355
|
/**
|
|
@@ -453,4 +471,4 @@ declare class FrameScheduler {
|
|
|
453
471
|
private readonly frame;
|
|
454
472
|
}
|
|
455
473
|
|
|
456
|
-
export { type ColorRampStop, type ColorSpace, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FractalNoiseOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type HueInterpolation, type IntersectionWatcher, type OutputGamut, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vector2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, dither, elapsedTime,
|
|
474
|
+
export { type ColorRampStop, type ColorSpace, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FractalNoiseOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type HueInterpolation, type IntersectionWatcher, type OutputGamut, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vector2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, dither, elapsedTime, fractalNoise, getReducedMotionPolicy, getReducedMotionTimeScale, grain, mixColor, oklabToLinearSrgb, oklchToLinearSrgb, parseColorString, quantize, resetRendererClock, setReducedMotionPolicy, signedDistanceFieldCircle, simplexNoise, srgbChannelToLinear, voronoi };
|