From d1ce6cbcde332e51246d1a18e0dd34328f0a94d5 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 24 Oct 2025 16:27:28 +0100 Subject: [PATCH 1/5] wip --- examples/anisotropic-fbm-streaks/main.js | 375 ++++++++++++++++++++++- package.json | 1 + pnpm-lock.yaml | 151 +-------- 3 files changed, 381 insertions(+), 146 deletions(-) diff --git a/examples/anisotropic-fbm-streaks/main.js b/examples/anisotropic-fbm-streaks/main.js index 83f55d8..bf8222a 100644 --- a/examples/anisotropic-fbm-streaks/main.js +++ b/examples/anisotropic-fbm-streaks/main.js @@ -15,6 +15,7 @@ import { } from "three/tsl"; import * as THREE from "three/webgpu"; import { simplexNoise3 } from "../../src/index.js"; +import { animate, createTimeline, stagger, utils } from "animejs"; // Scene setup const scene = new THREE.Scene(); @@ -44,7 +45,7 @@ const ridgedSharpness = uniform(0.85); const warpStrength = uniform(0.15); const fbmLacunarity = uniform(2.0); const fbmGain = uniform(0.5); -const animate = uniform(1); +const animate_shader = uniform(1); // TSL shader that generates anisotropic ridged fBm with vertical streak integration const streakTextureTSL = Fn(() => { @@ -57,7 +58,7 @@ const streakTextureTSL = Fn(() => { const baseP = vec3( uv0.x.mul(scaleX).mul(aspect), uv0.y.mul(scaleY), - time.mul(animate.mul(0.1)) + time.mul(animate_shader.mul(0.1)) ).toVar(); // Domain warp (mostly X) to introduce curvy drips and clumps @@ -121,7 +122,375 @@ gui.add(ridgedSharpness, "value", 0.0, 1.0, 0.01).name("ridgedSharpness"); gui.add(warpStrength, "value", 0.0, 2.0, 0.01).name("warpStrength"); gui.add(fbmLacunarity, "value", 0.1, 4.0, 0.1).name("fbmLacunarity"); gui.add(fbmGain, "value", 0.25, 1.0, 0.01).name("fbmGain"); -gui.add(animate, "value", 0, 1, 1).name("animate"); +gui.add(animate_shader, "value", 0, 1, 1).name("animate"); + +// ═══════════════════════════════════════════════════════════════════ +// 🎼 ORCHESTRAL SYMPHONY OF PARAMETER ANIMATIONS +// ═══════════════════════════════════════════════════════════════════ + +// Helper to create a breathing oscillation +function createBreathingPattern( + uniform, + min, + max, + duration, + easing = "inOutSine" +) { + animate(uniform, { + value: [min, max, min], + duration: duration, + ease: easing, + loop: true, + }); +} + +// Helper to create evolving random walks +function createOrganicWalk(uniform, min, max, baseDuration) { + const walk = () => { + const target = utils.random(min, max); + const duration = utils.random(baseDuration * 0.7, baseDuration * 1.3); + const easing = utils.randomPick([ + "inOutSine", + "inOutQuad", + "inOutCubic", + "inOutExpo", + ]); + + animate(uniform, { + value: target, + duration: duration, + ease: easing, + onComplete: walk, + }); + }; + walk(); +} + +// Helper to create harmonic oscillations (multiple frequencies) +function createHarmonicOscillation( + uniform, + base, + amplitude, + fundamentalPeriod +) { + const harmonics = { + value: 0, + harmonic1: 0, + harmonic2: 0, + harmonic3: 0, + }; + + // Fundamental frequency + animate(harmonics, { + harmonic1: [0, Math.PI * 2], + duration: fundamentalPeriod, + ease: "linear", + loop: true, + }); + + // 3rd harmonic (3x frequency) + animate(harmonics, { + harmonic2: [0, Math.PI * 2], + duration: fundamentalPeriod / 3, + ease: "linear", + loop: true, + }); + + // 5th harmonic (5x frequency) + animate(harmonics, { + harmonic3: [0, Math.PI * 2], + duration: fundamentalPeriod / 5, + ease: "linear", + loop: true, + }); + + // Composite waveform + const updateComposite = () => { + const wave1 = Math.sin(harmonics.harmonic1) * 1.0; + const wave2 = Math.sin(harmonics.harmonic2) * 0.3; + const wave3 = Math.sin(harmonics.harmonic3) * 0.15; + uniform.value = base + amplitude * (wave1 + wave2 + wave3); + requestAnimationFrame(updateComposite); + }; + updateComposite(); +} + +// Helper to create chaotic modulation +function createChaoticModulation(uniform, center, range) { + const chaos = { phase: 0, modDepth: 0 }; + + // Slowly varying modulation depth + animate(chaos, { + modDepth: [0, 1, 0], + duration: 15000, + ease: "inOutQuad", + loop: true, + }); + + // Fast chaotic phase + const modulatePhase = () => { + animate(chaos, { + phase: utils.random(-Math.PI, Math.PI), + duration: utils.random(300, 800), + ease: utils.randomPick(["inOutSine", "inOutQuad", "inOutCubic"]), + onComplete: modulatePhase, + onUpdate: () => { + uniform.value = center + Math.sin(chaos.phase) * range * chaos.modDepth; + }, + }); + }; + modulatePhase(); +} + +// ═══════════════════════════════════════════════════════════════════ +// 🎵 MOVEMENT 1: The Slow Dance - Scale Parameters +// ═══════════════════════════════════════════════════════════════════ +// scaleX and scaleY perform a slow, breathing waltz +// They move in counterpoint - when one expands, the other contracts + +const scalePhase = { x: 0, y: Math.PI }; // Start in counterpoint + +animate(scalePhase, { + x: [0, Math.PI * 8], // Many cycles + y: [Math.PI, Math.PI * 9], + duration: 60000, // 1 minute full cycle + ease: "linear", + loop: true, + onUpdate: () => { + // scaleX: 0.1 to 2.0 with secondary modulation + scaleX.value = + 0.8 + 0.7 * Math.sin(scalePhase.x) + 0.3 * Math.sin(scalePhase.x * 3); + + // scaleY: inverse relationship, 2.0 to 8.0 + scaleY.value = + 5.0 + 3.0 * Math.cos(scalePhase.y) + 1.0 * Math.cos(scalePhase.y * 2); + }, +}); + +// ═══════════════════════════════════════════════════════════════════ +// 🎵 MOVEMENT 2: The Pulse - Ridged Sharpness +// ═══════════════════════════════════════════════════════════════════ +// Sharp, rhythmic pulses that occasionally break into irregular patterns + +let pulseMode = "regular"; +const pulseState = { value: 0.85, intensity: 0 }; + +const switchPulseMode = () => { + pulseMode = utils.random(0, 1) > 0.7 ? "irregular" : "regular"; + + if (pulseMode === "regular") { + // Regular breathing pulse + animate(pulseState, { + value: [0.3, 0.95, 0.3], + duration: 8000, + ease: "inOutCubic", + onUpdate: () => { + ridgedSharpness.value = pulseState.value; + }, + onComplete: switchPulseMode, + }); + } else { + // Irregular staccato bursts + const burstCount = Math.floor(utils.random(3, 8)); + let burstsDone = 0; + + const doBurst = () => { + const target = utils.random(0.2, 0.98); + animate(pulseState, { + value: target, + duration: utils.random(400, 1200), + ease: utils.randomPick([ + "inOutQuad", + "outElastic(1, .5)", + "inOutBack(2)", + ]), + onUpdate: () => { + ridgedSharpness.value = pulseState.value; + }, + onComplete: () => { + burstsDone++; + if (burstsDone < burstCount) doBurst(); + else switchPulseMode(); + }, + }); + }; + doBurst(); + } +}; +switchPulseMode(); + +// ═══════════════════════════════════════════════════════════════════ +// 🎵 MOVEMENT 3: The Shimmer - Warp Strength +// ═══════════════════════════════════════════════════════════════════ +// High frequency tremolo with slow amplitude modulation + +const warpOsc = { + phase: 0, + amplitude: 0.5, + frequency: 1.0, +}; + +// Fast oscillation +animate(warpOsc, { + phase: [0, Math.PI * 40], // Many fast cycles + duration: 20000, + ease: "linear", + loop: true, +}); + +// Slow amplitude breathing +animate(warpOsc, { + amplitude: [0.2, 1.2, 0.2], + duration: 25000, + ease: "inOutQuad", + loop: true, +}); + +// Frequency variation for shimmer +animate(warpOsc, { + frequency: [0.8, 1.5, 0.8], + duration: 18000, + ease: "inOutSine", + loop: true, +}); + +// Composite update +const updateWarp = () => { + warpStrength.value = + 0.5 + + 0.4 * Math.sin(warpOsc.phase * warpOsc.frequency) * warpOsc.amplitude + + 0.15 * Math.sin(warpOsc.phase * 2.7) * (1 - warpOsc.amplitude * 0.3); + requestAnimationFrame(updateWarp); +}; +updateWarp(); + +// ═══════════════════════════════════════════════════════════════════ +// 🎵 MOVEMENT 4: The Journey - FBM Parameters +// ═══════════════════════════════════════════════════════════════════ +// Lacunarity and Gain explore parameter space together +// Creating evolving fractal textures + +const fbmJourney = { + lacunarity: 2.0, + gain: 0.5, + phase: 0, + chaosFactor: 0, +}; + +// Main journey - circular path through parameter space +animate(fbmJourney, { + phase: [0, Math.PI * 2], + duration: 45000, + ease: "linear", + loop: true, +}); + +// Chaos injection - occasional disturbances +const injectChaos = () => { + animate(fbmJourney, { + chaosFactor: [0, utils.random(0.3, 0.7), 0], + duration: utils.random(3000, 7000), + ease: "inOutSine", + onComplete: () => { + setTimeout(injectChaos, utils.random(5000, 15000)); + }, + }); +}; +injectChaos(); + +// Update FBM parameters +const updateFBM = () => { + // Lissajous-like curves for interesting paths + const lacBase = 2.0 + 1.0 * Math.sin(fbmJourney.phase); + const lacChaos = utils.random(-0.5, 0.5) * fbmJourney.chaosFactor; + fbmLacunarity.value = Math.max(0.5, Math.min(3.5, lacBase + lacChaos)); + + const gainBase = 0.5 + 0.3 * Math.cos(fbmJourney.phase * 1.618); // Golden ratio + const gainChaos = utils.random(-0.2, 0.2) * fbmJourney.chaosFactor; + fbmGain.value = Math.max(0.25, Math.min(0.95, gainBase + gainChaos)); + + requestAnimationFrame(updateFBM); +}; +updateFBM(); + +// ═══════════════════════════════════════════════════════════════════ +// 🎵 MOVEMENT 5: The Cascade - Integrate Samples +// ═══════════════════════════════════════════════════════════════════ +// Steps up and down in discrete rhythms, sometimes smooth, sometimes jagged + +let cascadeDirection = 1; +const cascadeState = { value: 12 }; + +const doCascade = () => { + const isSmooth = utils.random(0, 1) > 0.5; + const steps = Math.floor(utils.random(2, 6)); + + if (isSmooth) { + // Smooth glide + const target = + cascadeDirection > 0 ? utils.random(16, 24) : utils.random(3, 10); + + animate(cascadeState, { + value: target, + duration: utils.random(5000, 10000), + ease: "inOutQuad", + onUpdate: () => { + integrateSamples.value = Math.round(cascadeState.value); + }, + onComplete: () => { + cascadeDirection *= -1; + setTimeout(doCascade, utils.random(2000, 5000)); + }, + }); + } else { + // Stepwise cascade + let stepsDone = 0; + const doStep = () => { + const delta = cascadeDirection * utils.random(2, 5); + cascadeState.value = Math.max( + 2, + Math.min(24, cascadeState.value + delta) + ); + + animate(cascadeState, { + value: cascadeState.value, + duration: utils.random(800, 1500), + ease: utils.randomPick(["inQuad", "outQuad", "linear"]), + onUpdate: () => { + integrateSamples.value = Math.round(cascadeState.value); + }, + onComplete: () => { + stepsDone++; + if (stepsDone < steps) { + setTimeout(doStep, utils.random(200, 600)); + } else { + cascadeDirection *= -1; + setTimeout(doCascade, utils.random(3000, 7000)); + } + }, + }); + }; + doStep(); + } +}; +setTimeout(doCascade, 2000); + +// ═══════════════════════════════════════════════════════════════════ +// 🎼 MAESTRO - Global tempo variations +// ═══════════════════════════════════════════════════════════════════ +// Occasionally speeds up or slows down the perception of change + +console.log("🎼 Symphony initialized - watch the parameters dance in harmony"); +console.log("🎵 Movements:"); +console.log(" 1. The Slow Dance (scaleX, scaleY) - counterpoint breathing"); +console.log(" 2. The Pulse (ridgedSharpness) - regular/irregular rhythms"); +console.log(" 3. The Shimmer (warpStrength) - high frequency tremolo"); +console.log( + " 4. The Journey (fbmLacunarity, fbmGain) - exploring parameter space" +); +console.log(" 5. The Cascade (integrateSamples) - discrete rhythmic steps"); + +// ═══════════════════════════════════════════════════════════════════ function onResize() { camera.aspect = window.innerWidth / window.innerHeight; diff --git a/package.json b/package.json index 2e93aa1..f28d97d 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "vite-plugin-dts": "^4.5.4" }, "dependencies": { + "animejs": "^4.2.2", "three": "github:mrdoob/three.js" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3eb219f..a7b063b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,31 +8,19 @@ importers: .: dependencies: + animejs: + specifier: ^4.2.2 + version: 4.2.2 three: specifier: github:mrdoob/three.js version: https://codeload.github.com/mrdoob/three.js/tar.gz/71c6a2b701c94ad62f1494b6dad2f2292999b847 devDependencies: - '@tweakpane/core': - specifier: ^2.0.5 - version: 2.0.5 - '@tweakpane/plugin-essentials': - specifier: ^0.2.1 - version: 0.2.1(tweakpane@4.0.5) '@types/three': specifier: ^0.180.0 version: 0.180.0 fast-glob: specifier: ^3.3.3 version: 3.3.3 - tsl-uniform-ui-vite-plugin: - specifier: bhushan6/tsl-Uniform-UI-Vite-Plugin#pull/1/head&path:package - version: https://codeload.github.com/bhushan6/tsl-Uniform-UI-Vite-Plugin/tar.gz/e10bb39f1a89591ffde93b361a7e18be7e16ac59#path:package - tweakpane: - specifier: ^4.0.5 - version: 4.0.5 - tweakpane-plugin-file-import: - specifier: ^1.1.1 - version: 1.1.1(tweakpane@4.0.5) vite: specifier: ^7.1.5 version: 7.1.5 @@ -42,18 +30,6 @@ importers: packages: - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -67,14 +43,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} @@ -246,19 +214,9 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@microsoft/api-extractor-model@7.30.7': resolution: {integrity: sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==} @@ -420,14 +378,6 @@ packages: '@rushstack/ts-command-line@5.0.3': resolution: {integrity: sha512-bgPhQEqLVv/2hwKLYv/XvsTWNZ9B/+X1zJ7WgQE9rO5oiLzrOZvkIW4pk13yOQBhHyjcND5qMOa6p83t+Z66iQ==} - '@tweakpane/core@2.0.5': - resolution: {integrity: sha512-punBgD5rKCF5vcNo6BsSOXiDR/NSs9VM7SG65QSLJIxfRaGgj54ree9zQW6bO3pNFf3AogiGgaNODUVQRk9YqQ==} - - '@tweakpane/plugin-essentials@0.2.1': - resolution: {integrity: sha512-VbFU1/uD+CJNFQdfLXUOLjeG5HyUZH97Ox9CxmyVetg1hqjVun3C83HAGFULyhKzl8tSgii8jr304r8QpdHwzQ==} - peerDependencies: - tweakpane: ^4.0.0 - '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} @@ -508,6 +458,9 @@ packages: alien-signals@0.4.14: resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + animejs@4.2.2: + resolution: {integrity: sha512-Ys3RuvLdAeI14fsdKCQy7ytu4057QX6Bb7m4jwmfd6iKmUmLquTwk1ut0e4NtRQgCeq/s2Lv5+oMBjz6c7ZuIg==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -637,14 +590,6 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -806,18 +751,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tsl-uniform-ui-vite-plugin@https://codeload.github.com/bhushan6/tsl-Uniform-UI-Vite-Plugin/tar.gz/e10bb39f1a89591ffde93b361a7e18be7e16ac59#path:package: - resolution: {path: package, tarball: https://codeload.github.com/bhushan6/tsl-Uniform-UI-Vite-Plugin/tar.gz/e10bb39f1a89591ffde93b361a7e18be7e16ac59} - version: 0.7.2 - - tweakpane-plugin-file-import@1.1.1: - resolution: {integrity: sha512-7QbLToRTPYprzrZWJ1BWefIDJOyxj/9IHzVE/wZwK1+z1oUFtZ+ptzMxKm6apeZNoQTG7zS6opEaJi5tJ0igaQ==} - peerDependencies: - tweakpane: ^4.0.0 - - tweakpane@4.0.5: - resolution: {integrity: sha512-rxEXdSI+ArlG1RyO6FghC4ZUX8JkEfz8F3v1JuteXSV0pEtHJzyo07fcDG+NsJfN5L39kSbCYbB9cBGHyuI/tQ==} - typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -890,22 +823,6 @@ packages: snapshots: - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/generator@7.28.3': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-globals@7.28.0': {} - '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} @@ -914,24 +831,6 @@ snapshots: dependencies: '@babel/types': 7.28.4 - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - - '@babel/traverse@7.28.4': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -1023,20 +922,8 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - '@microsoft/api-extractor-model@7.30.7': dependencies: '@microsoft/tsdoc': 0.15.1 @@ -1185,12 +1072,6 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@tweakpane/core@2.0.5': {} - - '@tweakpane/plugin-essentials@0.2.1(tweakpane@4.0.5)': - dependencies: - tweakpane: 4.0.5 - '@tweenjs/tween.js@23.1.3': {} '@types/argparse@1.0.38': {} @@ -1284,6 +1165,8 @@ snapshots: alien-signals@0.4.14: {} + animejs@4.2.2: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -1410,10 +1293,6 @@ snapshots: jju@1.4.0: {} - js-tokens@4.0.0: {} - - jsesc@3.1.0: {} - json-schema-traverse@1.0.0: {} jsonfile@6.2.0: @@ -1578,20 +1457,6 @@ snapshots: dependencies: is-number: 7.0.0 - tsl-uniform-ui-vite-plugin@https://codeload.github.com/bhushan6/tsl-Uniform-UI-Vite-Plugin/tar.gz/e10bb39f1a89591ffde93b361a7e18be7e16ac59#path:package: - dependencies: - '@babel/parser': 7.28.4 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 - transitivePeerDependencies: - - supports-color - - tweakpane-plugin-file-import@1.1.1(tweakpane@4.0.5): - dependencies: - tweakpane: 4.0.5 - - tweakpane@4.0.5: {} - typescript@5.8.2: {} ufo@1.6.1: {} From 5f057118aa95c687d536674e169dd5ae3dc47f2c Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 24 Oct 2025 16:30:24 +0100 Subject: [PATCH 2/5] wip inspector updates --- examples/anisotropic-fbm-streaks/main.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/examples/anisotropic-fbm-streaks/main.js b/examples/anisotropic-fbm-streaks/main.js index bf8222a..e2fe534 100644 --- a/examples/anisotropic-fbm-streaks/main.js +++ b/examples/anisotropic-fbm-streaks/main.js @@ -113,15 +113,18 @@ material.colorNode = streakTextureTSL(); const mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material); scene.add(mesh); -// Inspector GUI -const gui = renderer.inspector.createParameters("Anisotropic FBM Streaks"); -gui.add(scaleX, "value", 0.1, 5.0, 0.1).name("scaleX"); -gui.add(scaleY, "value", 0.5, 10.0, 0.1).name("scaleY"); -gui.add(integrateSamples, "value", 1, 24, 1).name("integrateSamples"); -gui.add(ridgedSharpness, "value", 0.0, 1.0, 0.01).name("ridgedSharpness"); -gui.add(warpStrength, "value", 0.0, 2.0, 0.01).name("warpStrength"); -gui.add(fbmLacunarity, "value", 0.1, 4.0, 0.1).name("fbmLacunarity"); -gui.add(fbmGain, "value", 0.25, 1.0, 0.01).name("fbmGain"); +// Inspector GUI with live updates - .listen() makes them track animated values! +const gui = renderer.inspector.createParameters("🎼 Orchestral Symphony"); +gui.add(scaleX, "value", 0.1, 5.0, 0.1).name("scaleX").listen(); +gui.add(scaleY, "value", 0.5, 10.0, 0.1).name("scaleY").listen(); +gui.add(integrateSamples, "value", 1, 24, 1).name("integrateSamples").listen(); +gui + .add(ridgedSharpness, "value", 0.0, 1.0, 0.01) + .name("ridgedSharpness") + .listen(); +gui.add(warpStrength, "value", 0.0, 2.0, 0.01).name("warpStrength").listen(); +gui.add(fbmLacunarity, "value", 0.1, 4.0, 0.1).name("fbmLacunarity").listen(); +gui.add(fbmGain, "value", 0.25, 1.0, 0.01).name("fbmGain").listen(); gui.add(animate_shader, "value", 0, 1, 1).name("animate"); // ═══════════════════════════════════════════════════════════════════ From 7a9d10f013c1e6053e5c647f4095d9ebd108087d Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 24 Oct 2025 16:34:59 +0100 Subject: [PATCH 3/5] wip inspector open default and right --- examples/anisotropic-fbm-streaks/main.js | 57 ++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/examples/anisotropic-fbm-streaks/main.js b/examples/anisotropic-fbm-streaks/main.js index e2fe534..4b9a545 100644 --- a/examples/anisotropic-fbm-streaks/main.js +++ b/examples/anisotropic-fbm-streaks/main.js @@ -37,6 +37,59 @@ renderer.setClearColor(0x000000); renderer.inspector = new Inspector(); new OrbitControls(camera, renderer.domElement); +// Inspector: open by default and dock to the right with dynamic width +// - Panel width: up to 500px, but leave at least 500px for the canvas +// - Applies on load and resize +const profiler = renderer.inspector.profiler; + +// Inject CSS overrides to dock the inspector to the right side +(() => { + const style = document.createElement("style"); + style.textContent = ` + #profiler-panel { + top: 0; bottom: 0; right: 0; left: auto; + width: 360px; height: auto; + transform: translateX(100%); + border-top: none; + border-left: 2px solid var(--profiler-border); + } + #profiler-panel.visible { transform: translateX(0); } + .panel-resizer { display: none; } + `; + document.head.appendChild(style); +})(); + +function computeInspectorWidth() { + const full = window.innerWidth; + // Ensure at least 500px for canvas; inspector max 500px + return Math.max(0, Math.min(500, full - 500)); +} + +function layoutWithInspector(open = true) { + // Attach inspector shell to the same parent as the canvas if not already + const shell = renderer.inspector.domElement; + const parent = renderer.domElement.parentElement || document.body; + if (shell.parentElement === null) parent.appendChild(shell); + + const panel = profiler.panel; + const inspectorWidth = computeInspectorWidth(); + + // Open panel by default + if (open) panel.classList.add("visible"); + + panel.style.width = inspectorWidth + "px"; + + const canvasWidth = Math.max(1, window.innerWidth - inspectorWidth); + renderer.domElement.style.width = canvasWidth + "px"; + renderer.setSize(canvasWidth, window.innerHeight); + + camera.aspect = canvasWidth / window.innerHeight; + camera.updateProjectionMatrix(); +} + +// Initial layout: open inspector and size canvas accordingly +layoutWithInspector(true); + // Controls - define uniforms outside the shader function so Inspector can access them const scaleX = uniform(0.25); const scaleY = uniform(4.0); @@ -496,9 +549,7 @@ console.log(" 5. The Cascade (integrateSamples) - discrete rhythmic steps"); // ═══════════════════════════════════════════════════════════════════ function onResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); + layoutWithInspector(false); } window.addEventListener("resize", onResize); From de40e090d19f867c8a62cc0a7e140ffc62920cb5 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 24 Oct 2025 16:38:25 +0100 Subject: [PATCH 4/5] wip fps always --- examples/anisotropic-fbm-streaks/main.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/examples/anisotropic-fbm-streaks/main.js b/examples/anisotropic-fbm-streaks/main.js index 4b9a545..303b6bd 100644 --- a/examples/anisotropic-fbm-streaks/main.js +++ b/examples/anisotropic-fbm-streaks/main.js @@ -1,3 +1,4 @@ +import { animate, utils } from "animejs"; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { Inspector } from "three/addons/inspector/Inspector.js"; import { @@ -15,7 +16,6 @@ import { } from "three/tsl"; import * as THREE from "three/webgpu"; import { simplexNoise3 } from "../../src/index.js"; -import { animate, createTimeline, stagger, utils } from "animejs"; // Scene setup const scene = new THREE.Scene(); @@ -65,6 +65,16 @@ function computeInspectorWidth() { return Math.max(0, Math.min(500, full - 500)); } +function layoutTogglePosition() { + const panel = profiler.panel; + const inspectorWidth = computeInspectorWidth(); + const isVisible = panel.classList.contains("visible"); + // Keep FPS toggle always visible and above panel + profiler.toggleButton.style.zIndex = "1002"; + profiler.toggleButton.style.right = + (isVisible ? inspectorWidth + 15 : 15) + "px"; +} + function layoutWithInspector(open = true) { // Attach inspector shell to the same parent as the canvas if not already const shell = renderer.inspector.domElement; @@ -85,11 +95,23 @@ function layoutWithInspector(open = true) { camera.aspect = canvasWidth / window.innerHeight; camera.updateProjectionMatrix(); + + layoutTogglePosition(); } // Initial layout: open inspector and size canvas accordingly layoutWithInspector(true); +// Keep FPS button visible even when panel is open +(() => { + const origToggle = profiler.togglePanel.bind(profiler); + profiler.togglePanel = () => { + origToggle(); + profiler.toggleButton.classList.remove("hidden"); + layoutTogglePosition(); + }; +})(); + // Controls - define uniforms outside the shader function so Inspector can access them const scaleX = uniform(0.25); const scaleY = uniform(4.0); From 5d4225ccd0dd6907cc8994e9ae83267879f50301 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 6 Nov 2025 14:12:36 +0000 Subject: [PATCH 5/5] Remove unused animation helper functions Co-authored-by: tom --- examples/anisotropic-fbm-streaks/main.js | 129 ----------------------- 1 file changed, 129 deletions(-) diff --git a/examples/anisotropic-fbm-streaks/main.js b/examples/anisotropic-fbm-streaks/main.js index 303b6bd..983e0c1 100644 --- a/examples/anisotropic-fbm-streaks/main.js +++ b/examples/anisotropic-fbm-streaks/main.js @@ -206,120 +206,6 @@ gui.add(animate_shader, "value", 0, 1, 1).name("animate"); // 🎼 ORCHESTRAL SYMPHONY OF PARAMETER ANIMATIONS // ═══════════════════════════════════════════════════════════════════ -// Helper to create a breathing oscillation -function createBreathingPattern( - uniform, - min, - max, - duration, - easing = "inOutSine" -) { - animate(uniform, { - value: [min, max, min], - duration: duration, - ease: easing, - loop: true, - }); -} - -// Helper to create evolving random walks -function createOrganicWalk(uniform, min, max, baseDuration) { - const walk = () => { - const target = utils.random(min, max); - const duration = utils.random(baseDuration * 0.7, baseDuration * 1.3); - const easing = utils.randomPick([ - "inOutSine", - "inOutQuad", - "inOutCubic", - "inOutExpo", - ]); - - animate(uniform, { - value: target, - duration: duration, - ease: easing, - onComplete: walk, - }); - }; - walk(); -} - -// Helper to create harmonic oscillations (multiple frequencies) -function createHarmonicOscillation( - uniform, - base, - amplitude, - fundamentalPeriod -) { - const harmonics = { - value: 0, - harmonic1: 0, - harmonic2: 0, - harmonic3: 0, - }; - - // Fundamental frequency - animate(harmonics, { - harmonic1: [0, Math.PI * 2], - duration: fundamentalPeriod, - ease: "linear", - loop: true, - }); - - // 3rd harmonic (3x frequency) - animate(harmonics, { - harmonic2: [0, Math.PI * 2], - duration: fundamentalPeriod / 3, - ease: "linear", - loop: true, - }); - - // 5th harmonic (5x frequency) - animate(harmonics, { - harmonic3: [0, Math.PI * 2], - duration: fundamentalPeriod / 5, - ease: "linear", - loop: true, - }); - - // Composite waveform - const updateComposite = () => { - const wave1 = Math.sin(harmonics.harmonic1) * 1.0; - const wave2 = Math.sin(harmonics.harmonic2) * 0.3; - const wave3 = Math.sin(harmonics.harmonic3) * 0.15; - uniform.value = base + amplitude * (wave1 + wave2 + wave3); - requestAnimationFrame(updateComposite); - }; - updateComposite(); -} - -// Helper to create chaotic modulation -function createChaoticModulation(uniform, center, range) { - const chaos = { phase: 0, modDepth: 0 }; - - // Slowly varying modulation depth - animate(chaos, { - modDepth: [0, 1, 0], - duration: 15000, - ease: "inOutQuad", - loop: true, - }); - - // Fast chaotic phase - const modulatePhase = () => { - animate(chaos, { - phase: utils.random(-Math.PI, Math.PI), - duration: utils.random(300, 800), - ease: utils.randomPick(["inOutSine", "inOutQuad", "inOutCubic"]), - onComplete: modulatePhase, - onUpdate: () => { - uniform.value = center + Math.sin(chaos.phase) * range * chaos.modDepth; - }, - }); - }; - modulatePhase(); -} - // ═══════════════════════════════════════════════════════════════════ // 🎵 MOVEMENT 1: The Slow Dance - Scale Parameters // ═══════════════════════════════════════════════════════════════════ @@ -553,21 +439,6 @@ const doCascade = () => { }; setTimeout(doCascade, 2000); -// ═══════════════════════════════════════════════════════════════════ -// 🎼 MAESTRO - Global tempo variations -// ═══════════════════════════════════════════════════════════════════ -// Occasionally speeds up or slows down the perception of change - -console.log("🎼 Symphony initialized - watch the parameters dance in harmony"); -console.log("🎵 Movements:"); -console.log(" 1. The Slow Dance (scaleX, scaleY) - counterpoint breathing"); -console.log(" 2. The Pulse (ridgedSharpness) - regular/irregular rhythms"); -console.log(" 3. The Shimmer (warpStrength) - high frequency tremolo"); -console.log( - " 4. The Journey (fbmLacunarity, fbmGain) - exploring parameter space" -); -console.log(" 5. The Cascade (integrateSamples) - discrete rhythmic steps"); - // ═══════════════════════════════════════════════════════════════════ function onResize() {