@wave3d/core 0.2.2 → 0.4.1

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.
Files changed (37) hide show
  1. package/README.md +47 -0
  2. package/dist/config/model.d.ts +138 -1
  3. package/dist/config/model.js +144 -1
  4. package/dist/config/model.js.map +1 -1
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.js +2 -2
  7. package/dist/presets.js +15 -0
  8. package/dist/presets.js.map +1 -1
  9. package/dist/renderer/WaveRenderer.d.ts +59 -0
  10. package/dist/renderer/WaveRenderer.js +422 -8
  11. package/dist/renderer/WaveRenderer.js.map +1 -1
  12. package/dist/renderer/interaction.d.ts +119 -0
  13. package/dist/renderer/interaction.js +474 -0
  14. package/dist/renderer/interaction.js.map +1 -0
  15. package/dist/renderer/palette.js +14 -0
  16. package/dist/renderer/palette.js.map +1 -1
  17. package/dist/renderer/shaders.js +313 -1
  18. package/dist/renderer/shaders.js.map +1 -1
  19. package/dist/shell/createWave.d.ts +9 -1
  20. package/dist/shell/createWave.js +7 -1
  21. package/dist/shell/createWave.js.map +1 -1
  22. package/dist/shell/poster.d.ts +12 -0
  23. package/dist/shell/poster.js +4 -3
  24. package/dist/shell/poster.js.map +1 -1
  25. package/dist/standalone/wave3d.standalone.js +1016 -223
  26. package/dist/standalone.d.ts +2 -2
  27. package/dist/standalone.js +2 -2
  28. package/dist/studio/StudioWaveRenderer.d.ts +15 -0
  29. package/dist/studio/StudioWaveRenderer.js +32 -1
  30. package/dist/studio/StudioWaveRenderer.js.map +1 -1
  31. package/dist/studio/index.d.ts +2 -2
  32. package/dist/studio/index.js +2 -2
  33. package/dist/studio/randomize.d.ts +7 -4
  34. package/dist/studio/randomize.js +52 -5
  35. package/dist/studio/randomize.js.map +1 -1
  36. package/package.json +1 -1
  37. package/skills/wave3d/SKILL.md +60 -6
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, StudioConfig, Vec2, Vec3, WaveConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
1
+ import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, StudioConfig, Vec2, Vec3, WaveConfig, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, WavePressConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves } from "./config/model.js";
2
2
  import { WaveRenderer, WaveRendererOptions } from "./renderer/WaveRenderer.js";
3
+ import { PosterFit } from "./shell/poster.js";
3
4
  import { FallbackReason, SnapshotOptions, WaveHandle, WaveOptions, WaveState, createWave, mountWave } from "./shell/createWave.js";
4
- export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, type SnapshotOptions, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, type WaveOptions, type WaveRenderer, type WaveRendererOptions, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
5
+ export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, InteractionSource, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, type PosterFit, SceneConfig, SceneInteractionBinding, SceneInteractionConfig, SceneInteractionTarget, type SnapshotOptions, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, WaveHoverConfig, WaveInteractionBinding, WaveInteractionConfig, WaveInteractionTarget, type WaveOptions, WavePressConfig, type WaveRenderer, type WaveRendererOptions, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
1
+ import { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves } from "./config/model.js";
2
2
  import { createWave, mountWave } from "./shell/createWave.js";
3
- export { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
3
+ export { DEFAULT_LIGHT_POSITION, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeSceneInteraction, normalizeWave, normalizeWaveInteraction, resizeWaves };
package/dist/presets.js CHANGED
@@ -504,6 +504,21 @@ const PRESETS = {
504
504
  c.transparentBackground = true;
505
505
  return c;
506
506
  },
507
+ Spain: () => {
508
+ const c = PRESETS["Hero"]();
509
+ const w = c.waves[0];
510
+ w.paletteSource = "spain";
511
+ w.blendMode = "normal";
512
+ w.hueShift = 0;
513
+ w.colorContrast = 1.18;
514
+ w.colorSaturation = 1.25;
515
+ w.creaseLight = 1.6;
516
+ c.grain = .3;
517
+ c.background = "#1a0608";
518
+ c.backgroundMode = "color";
519
+ c.transparentBackground = false;
520
+ return c;
521
+ },
507
522
  "Vaporwave Sunset": () => {
508
523
  const c = PRESETS["Hero"]();
509
524
  const w = c.waves[0];
@@ -1 +1 @@
1
- {"version":3,"file":"presets.js","names":[],"sources":["../src/presets.ts"],"sourcesContent":["/**\n * Built-in presets: each a complete studio config (scene + one or more waves) in the wave model.\n * IP-clean — no copyrighted assets. The studio layers its own extra presets (and its historical\n * \"Stripe *\" display names) on top; see apps/studio/src/presets.ts.\n */\nimport { createDefaultConfig, makeStops, makeWaveSpread } from \"./config/model\";\nimport type { StudioConfig, NoiseBand } from \"./config/model\";\n\nconst RAD = 180 / Math.PI;\n\n/** Build a preset from a set of wave parameters. rotation/hue are given in RADIANS and\n * converted to degrees. All presets are solid-theme, so they reuse the hero palette +\n * surfaceColor fibers (600/0.2) and sheen 0, like the hero. camTarget/zoom frame the\n * wave (we pan the look-at to centre each one). */\nfunction buildPreset(p: {\n speed: number;\n contrast: number;\n sat: number;\n hueRad: number;\n dispX: number;\n dispZ: number;\n dispAmt: number;\n pos: [number, number, number];\n rotRad: [number, number, number];\n scale: [number, number, number];\n twF: [number, number, number];\n twP: [number, number, number];\n glow: [number, number, number];\n grain: number;\n blur: number;\n zoom: number;\n camTarget: [number, number];\n noiseBands?: NoiseBand[];\n twistMotion?: boolean;\n}): StudioConfig {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.speed = p.speed;\n w.colorContrast = p.contrast;\n w.colorSaturation = p.sat;\n w.hueShift = p.hueRad * RAD;\n w.displaceFrequency = { x: p.dispX, y: p.dispZ };\n w.displaceAmount = p.dispAmt;\n w.position = { x: p.pos[0], y: p.pos[1], z: p.pos[2] };\n w.rotation = { x: p.rotRad[0] * RAD, y: p.rotRad[1] * RAD, z: p.rotRad[2] * RAD };\n w.scale = { x: p.scale[0], y: p.scale[1], z: p.scale[2] };\n w.twistFrequency = { x: p.twF[0], y: p.twF[1], z: p.twF[2] };\n w.twistPower = { x: p.twP[0], y: p.twP[1], z: p.twP[2] };\n w.creaseLight = p.glow[0];\n w.creaseSharpness = p.glow[1];\n w.creaseSoftness = p.glow[2];\n if (p.noiseBands) w.noiseBands = p.noiseBands;\n if (p.twistMotion) w.twistMotion = true;\n c.grain = p.grain;\n c.blur = p.blur;\n c.cameraPosition = { x: 100, y: 0, z: 5000 };\n c.cameraTarget = { x: p.camTarget[0], y: p.camTarget[1], z: 0 };\n c.cameraZoom = p.zoom;\n return c;\n}\n\n/** Presets: each a complete studio config (scene + one or more waves) in the wave model. */\nexport const PRESETS: Record<string, () => StudioConfig> = {\n // The app's default wave: a centred, full-frame ribbon (window-independent framing).\n // Shown first and named \"Hero\"; several presets below derive from it.\n Hero: () =>\n buildPreset({\n speed: 0.04,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [380, -301.7, -11.1],\n rotRad: [-0.44959, -0.11759, 1.874407],\n scale: [9, 8, 5],\n twF: [-0.65, 0.41, -0.58],\n twP: [3.63, 0.7, 3.95],\n glow: [1.98, 0.806, 0.834],\n grain: 1.1,\n blur: 0.02,\n zoom: 0.55,\n camTarget: [-420, -200], // user-tuned default framing\n }),\n // Stripe's real hero, recreated faithfully: an orthographic ×10 scene that overflows the\n // frame, so only the twisted crop shows. This is the model's plain default config.\n \"Wave 2\": () => createDefaultConfig(),\n // camTarget on the waves below is a first-pass centring; tune per-wave. NOTE: Wave 4 also\n // uses a variant vertex shader (animated twist-X wobble) we don't fully replicate — its\n // STATIC frame is close, the motion differs.\n \"Wave 3\": () =>\n buildPreset({\n speed: 0.08,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [-200.7, -65.4, -11.1],\n rotRad: [-2.875593, 3.095927, -2.925927],\n scale: [3, 3, 3],\n twF: [0.059, 0.32, -0.397],\n twP: [3.63, 0.44, 5.99],\n glow: [3.86, 0.923, 1],\n grain: 1.2,\n blur: 0.02,\n zoom: 1.3,\n camTarget: [-104, 13], // centred; zoomed in (wide/flat wave)\n }),\n \"Wave 4\": () =>\n buildPreset({\n speed: 0.0525,\n contrast: 0.969,\n sat: 1.383,\n hueRad: 0.0376991,\n dispX: 0.005,\n dispZ: 0.0212,\n dispAmt: 6.68,\n pos: [206.1, -438, -11.1],\n rotRad: [-0.666018, -0.031416, 0.779115],\n scale: [6.0501, 8.3983, 6.9854],\n twF: [-0.424, 0.024, -1.312],\n twP: [1.81, 0.94, 4.76],\n glow: [1.55, 1.174, 0.972],\n grain: 0.576,\n blur: 0,\n zoom: 0.9316,\n camTarget: [194, -402], // centred on the wave\n twistMotion: true, // variant vertex shader — animated twist-X wobble\n noiseBands: [\n {\n startX: 0.856,\n endX: 1,\n startY: 0,\n endY: 0.913,\n feather: 0.5,\n strength: 0.346,\n frequency: 1018,\n colorAttenuation: 1,\n parabolaPower: 0,\n },\n {\n startX: 0.038,\n endX: 0.538,\n startY: 0.105,\n endY: 1,\n feather: 0.3315,\n strength: 1,\n frequency: 190,\n colorAttenuation: 0,\n parabolaPower: 2.11,\n },\n ],\n }),\n // The dark-background hero: identical geometry/camera to the default hero, but theme\n // \"wireframe\" → the line shader on a dark page background, with grain 1.2. Same palette.\n Wireframe: () => {\n const c = createDefaultConfig();\n c.waves[0].theme = \"wireframe\";\n c.grain = 1.2;\n c.background = \"#0a2540\"; // dark navy page background\n c.transparentBackground = false;\n return c;\n },\n \"Neon Dark Multistrand\": () => {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.theme = \"wireframe\"; // line shader on the near-black background — neon wireframe look\n w.blendMode = \"additive\";\n w.palette = makeStops([\"#00f5d4\", \"#00bbf9\", \"#9b5de5\", \"#f15bb5\", \"#fee440\"]);\n w.creaseLight = 1.0;\n c.background = \"#05060c\";\n c.transparentBackground = false; // fill the dark bg so the neon lines read on black (not the page)\n c.waves = makeWaveSpread(w, 3); // three overlapping neon waves\n c.waveCount = 3;\n return c;\n },\n \"Mesh Gradient\": () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a84ff\", x: 0.06, y: 0.9, influence: 0.68 },\n { color: \"#64d2ff\", x: 0.88, y: 0.92, influence: 0.72 },\n { color: \"#bf5af2\", x: 0.5, y: 0.64, influence: 0.58 },\n { color: \"#ff375f\", x: 0.1, y: 0.14, influence: 0.7 },\n { color: \"#ff9f0a\", x: 0.84, y: 0.12, influence: 0.74 },\n { color: \"#30d158\", x: 0.94, y: 0.5, influence: 0.54 },\n ];\n w.meshGradientSoftness = 0.68;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.06;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.14;\n c.grain = 0.3;\n c.blur = 0.008;\n c.background = \"#070914\";\n c.backgroundMode = \"color\";\n c.transparentBackground = false;\n return c;\n },\n \"Solar Bloom\": () => {\n // Radial gradient: a warm core blooming out to a deep-indigo edge. usePaletteTexture off so\n // our own stops map along the radial gradCoord instead of sampling the baked hero LUT.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"radial\";\n w.gradientShift = 0.14;\n w.palette = [\n { color: \"#fff3c4\", pos: 0 }, // warm-white core\n { color: \"#ffd166\", pos: 0.22 }, // gold\n { color: \"#ff8c42\", pos: 0.42 }, // orange\n { color: \"#ff5d8f\", pos: 0.62 }, // coral-pink\n { color: \"#a64dff\", pos: 0.82 }, // violet\n { color: \"#241246\", pos: 1 }, // deep indigo edge\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.01;\n // Deep warm radial vignette behind the bloom.\n c.background = \"#0a0714\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"radial\";\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#2a1330\", \"#08040f\"]);\n c.transparentBackground = false;\n return c;\n },\n Holographic: () => {\n // Conic gradient: an iridescent oil-slick sweep. The palette wraps (first ≈ last stop) so\n // the conic seam is invisible.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"conic\";\n w.gradientShift = 0.08;\n w.palette = [\n { color: \"#8ef6e4\", pos: 0 }, // mint (seam)\n { color: \"#6ec3ff\", pos: 0.18 }, // sky\n { color: \"#9b8cff\", pos: 0.36 }, // periwinkle\n { color: \"#ff8ad8\", pos: 0.54 }, // pink\n { color: \"#ffd98e\", pos: 0.72 }, // peach\n { color: \"#a0f0c8\", pos: 0.88 }, // seafoam\n { color: \"#8ef6e4\", pos: 1 }, // mint again (seamless wrap)\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.04;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.12;\n c.grain = 0.28;\n c.blur = 0.01;\n // Subtle deep teal → violet wash behind the iridescence.\n c.background = \"#05060c\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"linear\";\n c.backgroundGradientAngle = 135;\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#04121a\", \"#0a0518\"]);\n c.transparentBackground = false;\n return c;\n },\n Aurora: () => {\n // Mesh gradient: a moody aurora — teals/greens drifting into violet over a night-sky base\n // (distinct from the brighter iOS-style \"Mesh Gradient\").\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a1f3c\", x: 0.08, y: 0.12, influence: 0.62 },\n { color: \"#1fddb0\", x: 0.3, y: 0.7, influence: 0.78 },\n { color: \"#57f5a3\", x: 0.58, y: 0.86, influence: 0.7 },\n { color: \"#3a86ff\", x: 0.82, y: 0.55, influence: 0.62 },\n { color: \"#a15cff\", x: 0.5, y: 0.32, influence: 0.7 },\n { color: \"#071433\", x: 0.92, y: 0.08, influence: 0.6 },\n ];\n w.meshGradientSoftness = 0.72;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.008;\n // Dark night-sky MESH backdrop (also shows off the mesh background type).\n c.background = \"#03060f\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"mesh\";\n c.backgroundMeshPoints = [\n { color: \"#02040c\", x: 0.15, y: 0.85, influence: 0.7 },\n { color: \"#08243a\", x: 0.5, y: 0.5, influence: 0.75 },\n { color: \"#0a0f2e\", x: 0.85, y: 0.7, influence: 0.7 },\n { color: \"#04121a\", x: 0.7, y: 0.2, influence: 0.6 },\n { color: \"#000208\", x: 0.12, y: 0.12, influence: 0.6 },\n ];\n c.backgroundMeshSoftness = 0.75;\n c.transparentBackground = false;\n return c;\n },\n Palestine: () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.paletteSource = \"palestine\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1;\n w.colorSaturation = 1;\n c.grain = 0.35;\n c.background = \"#f2efe8\";\n c.transparentBackground = true;\n return c;\n },\n \"Vaporwave Sunset\": () => {\n // The Hero wave re-posed/re-framed, plus the vaporwave palette.\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.position.x = 525;\n w.rotation.x = -0.64 * RAD;\n w.rotation.z = 1.68 * RAD;\n w.paletteSource = \"vaporwave\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.08;\n w.colorSaturation = 1.15;\n w.creaseLight = 1.25;\n c.cameraZoom = 1.1;\n c.cameraTarget = { x: 150, y: 360, z: 0 };\n c.background = \"#09051f\";\n c.transparentBackground = false;\n return c;\n },\n Kaleidoscope: () => {\n const c = PRESETS[\"Wave 3\"]();\n const w = c.waves[0];\n w.paletteSource = \"kaleidoscope\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.12;\n c.grain = 0.5;\n return c;\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"presets.js","names":[],"sources":["../src/presets.ts"],"sourcesContent":["/**\n * Built-in presets: each a complete studio config (scene + one or more waves) in the wave model.\n * IP-clean — no copyrighted assets. The studio layers its own extra presets (and its historical\n * \"Stripe *\" display names) on top; see apps/studio/src/presets.ts.\n */\nimport { createDefaultConfig, makeStops, makeWaveSpread } from \"./config/model\";\nimport type { StudioConfig, NoiseBand } from \"./config/model\";\n\nconst RAD = 180 / Math.PI;\n\n/** Build a preset from a set of wave parameters. rotation/hue are given in RADIANS and\n * converted to degrees. All presets are solid-theme, so they reuse the hero palette +\n * surfaceColor fibers (600/0.2) and sheen 0, like the hero. camTarget/zoom frame the\n * wave (we pan the look-at to centre each one). */\nfunction buildPreset(p: {\n speed: number;\n contrast: number;\n sat: number;\n hueRad: number;\n dispX: number;\n dispZ: number;\n dispAmt: number;\n pos: [number, number, number];\n rotRad: [number, number, number];\n scale: [number, number, number];\n twF: [number, number, number];\n twP: [number, number, number];\n glow: [number, number, number];\n grain: number;\n blur: number;\n zoom: number;\n camTarget: [number, number];\n noiseBands?: NoiseBand[];\n twistMotion?: boolean;\n}): StudioConfig {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.speed = p.speed;\n w.colorContrast = p.contrast;\n w.colorSaturation = p.sat;\n w.hueShift = p.hueRad * RAD;\n w.displaceFrequency = { x: p.dispX, y: p.dispZ };\n w.displaceAmount = p.dispAmt;\n w.position = { x: p.pos[0], y: p.pos[1], z: p.pos[2] };\n w.rotation = { x: p.rotRad[0] * RAD, y: p.rotRad[1] * RAD, z: p.rotRad[2] * RAD };\n w.scale = { x: p.scale[0], y: p.scale[1], z: p.scale[2] };\n w.twistFrequency = { x: p.twF[0], y: p.twF[1], z: p.twF[2] };\n w.twistPower = { x: p.twP[0], y: p.twP[1], z: p.twP[2] };\n w.creaseLight = p.glow[0];\n w.creaseSharpness = p.glow[1];\n w.creaseSoftness = p.glow[2];\n if (p.noiseBands) w.noiseBands = p.noiseBands;\n if (p.twistMotion) w.twistMotion = true;\n c.grain = p.grain;\n c.blur = p.blur;\n c.cameraPosition = { x: 100, y: 0, z: 5000 };\n c.cameraTarget = { x: p.camTarget[0], y: p.camTarget[1], z: 0 };\n c.cameraZoom = p.zoom;\n return c;\n}\n\n/** Presets: each a complete studio config (scene + one or more waves) in the wave model. */\nexport const PRESETS: Record<string, () => StudioConfig> = {\n // The app's default wave: a centred, full-frame ribbon (window-independent framing).\n // Shown first and named \"Hero\"; several presets below derive from it.\n Hero: () =>\n buildPreset({\n speed: 0.04,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [380, -301.7, -11.1],\n rotRad: [-0.44959, -0.11759, 1.874407],\n scale: [9, 8, 5],\n twF: [-0.65, 0.41, -0.58],\n twP: [3.63, 0.7, 3.95],\n glow: [1.98, 0.806, 0.834],\n grain: 1.1,\n blur: 0.02,\n zoom: 0.55,\n camTarget: [-420, -200], // user-tuned default framing\n }),\n // Stripe's real hero, recreated faithfully: an orthographic ×10 scene that overflows the\n // frame, so only the twisted crop shows. This is the model's plain default config.\n \"Wave 2\": () => createDefaultConfig(),\n // camTarget on the waves below is a first-pass centring; tune per-wave. NOTE: Wave 4 also\n // uses a variant vertex shader (animated twist-X wobble) we don't fully replicate — its\n // STATIC frame is close, the motion differs.\n \"Wave 3\": () =>\n buildPreset({\n speed: 0.08,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [-200.7, -65.4, -11.1],\n rotRad: [-2.875593, 3.095927, -2.925927],\n scale: [3, 3, 3],\n twF: [0.059, 0.32, -0.397],\n twP: [3.63, 0.44, 5.99],\n glow: [3.86, 0.923, 1],\n grain: 1.2,\n blur: 0.02,\n zoom: 1.3,\n camTarget: [-104, 13], // centred; zoomed in (wide/flat wave)\n }),\n \"Wave 4\": () =>\n buildPreset({\n speed: 0.0525,\n contrast: 0.969,\n sat: 1.383,\n hueRad: 0.0376991,\n dispX: 0.005,\n dispZ: 0.0212,\n dispAmt: 6.68,\n pos: [206.1, -438, -11.1],\n rotRad: [-0.666018, -0.031416, 0.779115],\n scale: [6.0501, 8.3983, 6.9854],\n twF: [-0.424, 0.024, -1.312],\n twP: [1.81, 0.94, 4.76],\n glow: [1.55, 1.174, 0.972],\n grain: 0.576,\n blur: 0,\n zoom: 0.9316,\n camTarget: [194, -402], // centred on the wave\n twistMotion: true, // variant vertex shader — animated twist-X wobble\n noiseBands: [\n {\n startX: 0.856,\n endX: 1,\n startY: 0,\n endY: 0.913,\n feather: 0.5,\n strength: 0.346,\n frequency: 1018,\n colorAttenuation: 1,\n parabolaPower: 0,\n },\n {\n startX: 0.038,\n endX: 0.538,\n startY: 0.105,\n endY: 1,\n feather: 0.3315,\n strength: 1,\n frequency: 190,\n colorAttenuation: 0,\n parabolaPower: 2.11,\n },\n ],\n }),\n // The dark-background hero: identical geometry/camera to the default hero, but theme\n // \"wireframe\" → the line shader on a dark page background, with grain 1.2. Same palette.\n Wireframe: () => {\n const c = createDefaultConfig();\n c.waves[0].theme = \"wireframe\";\n c.grain = 1.2;\n c.background = \"#0a2540\"; // dark navy page background\n c.transparentBackground = false;\n return c;\n },\n \"Neon Dark Multistrand\": () => {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.theme = \"wireframe\"; // line shader on the near-black background — neon wireframe look\n w.blendMode = \"additive\";\n w.palette = makeStops([\"#00f5d4\", \"#00bbf9\", \"#9b5de5\", \"#f15bb5\", \"#fee440\"]);\n w.creaseLight = 1.0;\n c.background = \"#05060c\";\n c.transparentBackground = false; // fill the dark bg so the neon lines read on black (not the page)\n c.waves = makeWaveSpread(w, 3); // three overlapping neon waves\n c.waveCount = 3;\n return c;\n },\n \"Mesh Gradient\": () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a84ff\", x: 0.06, y: 0.9, influence: 0.68 },\n { color: \"#64d2ff\", x: 0.88, y: 0.92, influence: 0.72 },\n { color: \"#bf5af2\", x: 0.5, y: 0.64, influence: 0.58 },\n { color: \"#ff375f\", x: 0.1, y: 0.14, influence: 0.7 },\n { color: \"#ff9f0a\", x: 0.84, y: 0.12, influence: 0.74 },\n { color: \"#30d158\", x: 0.94, y: 0.5, influence: 0.54 },\n ];\n w.meshGradientSoftness = 0.68;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.06;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.14;\n c.grain = 0.3;\n c.blur = 0.008;\n c.background = \"#070914\";\n c.backgroundMode = \"color\";\n c.transparentBackground = false;\n return c;\n },\n \"Solar Bloom\": () => {\n // Radial gradient: a warm core blooming out to a deep-indigo edge. usePaletteTexture off so\n // our own stops map along the radial gradCoord instead of sampling the baked hero LUT.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"radial\";\n w.gradientShift = 0.14;\n w.palette = [\n { color: \"#fff3c4\", pos: 0 }, // warm-white core\n { color: \"#ffd166\", pos: 0.22 }, // gold\n { color: \"#ff8c42\", pos: 0.42 }, // orange\n { color: \"#ff5d8f\", pos: 0.62 }, // coral-pink\n { color: \"#a64dff\", pos: 0.82 }, // violet\n { color: \"#241246\", pos: 1 }, // deep indigo edge\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.01;\n // Deep warm radial vignette behind the bloom.\n c.background = \"#0a0714\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"radial\";\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#2a1330\", \"#08040f\"]);\n c.transparentBackground = false;\n return c;\n },\n Holographic: () => {\n // Conic gradient: an iridescent oil-slick sweep. The palette wraps (first ≈ last stop) so\n // the conic seam is invisible.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"conic\";\n w.gradientShift = 0.08;\n w.palette = [\n { color: \"#8ef6e4\", pos: 0 }, // mint (seam)\n { color: \"#6ec3ff\", pos: 0.18 }, // sky\n { color: \"#9b8cff\", pos: 0.36 }, // periwinkle\n { color: \"#ff8ad8\", pos: 0.54 }, // pink\n { color: \"#ffd98e\", pos: 0.72 }, // peach\n { color: \"#a0f0c8\", pos: 0.88 }, // seafoam\n { color: \"#8ef6e4\", pos: 1 }, // mint again (seamless wrap)\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.04;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.12;\n c.grain = 0.28;\n c.blur = 0.01;\n // Subtle deep teal → violet wash behind the iridescence.\n c.background = \"#05060c\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"linear\";\n c.backgroundGradientAngle = 135;\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#04121a\", \"#0a0518\"]);\n c.transparentBackground = false;\n return c;\n },\n Aurora: () => {\n // Mesh gradient: a moody aurora — teals/greens drifting into violet over a night-sky base\n // (distinct from the brighter iOS-style \"Mesh Gradient\").\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a1f3c\", x: 0.08, y: 0.12, influence: 0.62 },\n { color: \"#1fddb0\", x: 0.3, y: 0.7, influence: 0.78 },\n { color: \"#57f5a3\", x: 0.58, y: 0.86, influence: 0.7 },\n { color: \"#3a86ff\", x: 0.82, y: 0.55, influence: 0.62 },\n { color: \"#a15cff\", x: 0.5, y: 0.32, influence: 0.7 },\n { color: \"#071433\", x: 0.92, y: 0.08, influence: 0.6 },\n ];\n w.meshGradientSoftness = 0.72;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.008;\n // Dark night-sky MESH backdrop (also shows off the mesh background type).\n c.background = \"#03060f\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"mesh\";\n c.backgroundMeshPoints = [\n { color: \"#02040c\", x: 0.15, y: 0.85, influence: 0.7 },\n { color: \"#08243a\", x: 0.5, y: 0.5, influence: 0.75 },\n { color: \"#0a0f2e\", x: 0.85, y: 0.7, influence: 0.7 },\n { color: \"#04121a\", x: 0.7, y: 0.2, influence: 0.6 },\n { color: \"#000208\", x: 0.12, y: 0.12, influence: 0.6 },\n ];\n c.backgroundMeshSoftness = 0.75;\n c.transparentBackground = false;\n return c;\n },\n Palestine: () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.paletteSource = \"palestine\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1;\n w.colorSaturation = 1;\n c.grain = 0.35;\n c.background = \"#f2efe8\";\n c.transparentBackground = true;\n return c;\n },\n Spain: () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.paletteSource = \"spain\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.18;\n w.colorSaturation = 1.25;\n w.creaseLight = 1.6; // moderate crease-light: rich crimson without washing to salmon (Hero's is 1.98)\n c.grain = 0.3;\n c.background = \"#1a0608\"; // deep oxblood stage\n c.backgroundMode = \"color\";\n c.transparentBackground = false; // opaque, so the dark stage makes the flag pop\n return c;\n },\n \"Vaporwave Sunset\": () => {\n // The Hero wave re-posed/re-framed, plus the vaporwave palette.\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.position.x = 525;\n w.rotation.x = -0.64 * RAD;\n w.rotation.z = 1.68 * RAD;\n w.paletteSource = \"vaporwave\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.08;\n w.colorSaturation = 1.15;\n w.creaseLight = 1.25;\n c.cameraZoom = 1.1;\n c.cameraTarget = { x: 150, y: 360, z: 0 };\n c.background = \"#09051f\";\n c.transparentBackground = false;\n return c;\n },\n Kaleidoscope: () => {\n const c = PRESETS[\"Wave 3\"]();\n const w = c.waves[0];\n w.paletteSource = \"kaleidoscope\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.12;\n c.grain = 0.5;\n return c;\n },\n};\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { StudioConfig, WaveConfig } from "../config/model.js";
2
2
  import { WaveGeometry } from "./WaveGeometry.js";
3
+ import { InteractionController } from "./interaction.js";
3
4
  import * as THREE from "three";
4
5
 
5
6
  //#region src/renderer/WaveRenderer.d.ts
@@ -70,6 +71,12 @@ declare class WaveRenderer {
70
71
  private readonly postPass;
71
72
  /** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */
72
73
  private bloomPass?;
74
+ private ditherPass?;
75
+ private innerLightPass?;
76
+ private halftonePass?;
77
+ private heatmapPass?;
78
+ private paperTexturePass?;
79
+ private halftoneCmykPass?;
73
80
  protected readonly container: HTMLElement;
74
81
  private readonly respectReducedMotion;
75
82
  private readonly skipIntroRamp;
@@ -91,6 +98,15 @@ declare class WaveRenderer {
91
98
  private readonly clipSphere;
92
99
  private readonly clipTmpA;
93
100
  private readonly clipTmpB;
101
+ /** Created by syncInteraction() when interaction turns on, disposed when it turns off. */
102
+ protected interaction?: InteractionController;
103
+ /** Extra ortho-zoom MULTIPLIER from a cameraZoom binding (1 = none); applied in applyZoom().
104
+ * Protected so the studio's writeCameraToConfig() can divide it back out (keep it out of config). */
105
+ protected interactionZoom: number;
106
+ /** Extra time-offset DELTA from a timeOffset binding (0 = none); added in updateTime(). */
107
+ private interactionTimeOffset;
108
+ /** Scene-binding out-params: appliers write into this, applyBindings() reads it back. */
109
+ private readonly interactionSceneOut;
94
110
  private readonly timer;
95
111
  private time;
96
112
  private rafId;
@@ -162,6 +178,25 @@ declare class WaveRenderer {
162
178
  * created lazily the first time bloom is enabled and disposed when turned back off. It sits
163
179
  * right after the scene RenderPass so it blooms the wave before the grain/blur pass. */
164
180
  private applyBloom;
181
+ /** Insert / tune / remove the dithering pass — a self-contained "layered" post shader (an ordered
182
+ * Bayer dither, in the spirit of paper-design/shaders). Like bloom, dither 0 removes the pass
183
+ * entirely so cost and pixels match dither-off, and it's created lazily on first enable. It is
184
+ * appended AFTER OutputPass so it runs last and quantizes display-space colour (tone-mapped +
185
+ * sRGB) — dithering the linear composer buffer would crush the steps in the shadows. */
186
+ private applyDither;
187
+ /** Insert / tune / remove the innerLight pass — volumetric light streaks scattered from the bright
188
+ * wave toward a light point (innerLightX/Y in UV). Scene zone (index 1) so it scatters the raw wave
189
+ * like bloom. innerLight 0 removes the pass entirely; created lazily on first enable. */
190
+ private applyInnerLight;
191
+ /** Insert / tune / remove the halftone pass — a rotated dot screen (dot size scales with local
192
+ * brightness) over the finished image. halftone 0 removes the pass; created lazily on enable. */
193
+ private applyHalftone;
194
+ /** Heatmap: recolour the final image by luminance → thermal palette. Finish zone. */
195
+ private applyHeatmap;
196
+ /** Paper texture: fibrous substrate shading multiplied over the image. Finish zone. */
197
+ private applyPaperTexture;
198
+ /** CMYK halftone: four rotated dot screens (cyan/magenta/yellow/black). Finish zone. */
199
+ private applyHalftoneCmyk;
165
200
  private onResize;
166
201
  private onContextLost;
167
202
  private onContextRestored;
@@ -179,6 +214,30 @@ declare class WaveRenderer {
179
214
  private updateTime;
180
215
  /** Render exactly one frame at the current time. */
181
216
  renderOnce(): void;
217
+ /** Create/dispose the interaction controller as config toggles interaction on/off. Called from
218
+ * refresh(); the compiled define set (POINTER_FX etc.) is handled separately by waveDefines(). */
219
+ private syncInteraction;
220
+ /** Per-frame interaction write: dynamic pointer-field uniforms + bindings. No-op without a
221
+ * controller. While capturing it writes the REST state instead (pointer field zeroed, every bound
222
+ * param at its authored base) — merely skipping the write would freeze whatever live hover/scroll
223
+ * state the previous frame left in the uniforms, so exports wouldn't be deterministic. */
224
+ private applyInteraction;
225
+ /** Write the capture-frame interaction state: exactly what this config renders with no input —
226
+ * pointer presence + ripple envelopes zeroed (vPointerFall gates every hover effect to 0) and each
227
+ * bound param at its authored base. Live controller state is left untouched, so the frame after
228
+ * the capture resumes mid-gesture; the trailing renderOnce() in captureImage restores the preview.
229
+ * interactionZoom is deliberately NOT reset — captureImage strips it from camera.zoom itself, and
230
+ * the post-capture restore depends on it being unchanged. */
231
+ private applyInteractionRest;
232
+ /** Write the dynamic pointer-field uniforms to every wave that HAS a pointer field. Position /
233
+ * presence are PER WAVE (each trails the cursor at its own hover smoothing); ripple origins/ages
234
+ * are shared. Per-wave amplitudes were already pushed statically in refresh(). */
235
+ private applyPointerField;
236
+ /** Evaluate bindings via the applier tables: value = mix(from ?? base, to, smoothedSource). Scene
237
+ * bindings drive scene params; each wave's bindings drive that wave's uniforms. */
238
+ private applyBindings;
239
+ /** Feed a `custom:<name>` interaction input (developer API). No-op when interaction is off. */
240
+ setInteractionInput(name: string, value: number): void;
182
241
  /** Re-evaluate play/pause after `config.paused` changes. */
183
242
  refreshPlayback(): void;
184
243
  /** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /