feat(mix-visualizer): Phase 10 tuning — smooth waveform, bouncy collision, 8 knobs

Smooth the loudness contour (~50 ms envelope at preprocessing + decode-time, plus
smootherstep render reconstruction); retune wax↔waveform collision to bouncy/sub-unity
(no explosion/stuck/jitter); split the bubbles knob into fluid-amount + fluid-viscosity
(cohesion via uniform-only smin/wobble); retune scroll/gravity/heat/width ranges; make
the colour rotation visible and boost OKLab chroma; the controls bar now holds its
layout and hides only its knobs via a Visible parameter.
This commit is contained in:
daniel-c-harvey
2026-06-17 05:12:15 -04:00
parent ba1a1cd8ec
commit 4e34696719
9 changed files with 500 additions and 183 deletions
@@ -202,28 +202,32 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
// ── Bridge pushes. Each is a no-op until the module handle exists. ───────────────────────────
/// <summary>
/// Push the seven control values to the module from the shared state. Used to seed on first render
/// and to re-push when the controls bar signals a change (lava reframe Wave R4). Each value is its
/// own dedicated dial now — no more R2 temp-remapping:
/// Push the eight control values to the module from the shared state. Used to seed on first render
/// and to re-push when the controls bar signals a change. Each value is its own dedicated dial:
/// <list type="bullet">
/// <item>scroll speed [0,1] is mapped to a visible time-span via <see cref="MixZoomMapping"/> and
/// pushed through <c>setScrollSpeed</c> (higher speed → tighter window → faster scroll);</item>
/// <item>gradient rotation speed → <c>setGradientRotationSpeed</c> (inert until Wave R3);</item>
/// <item>gravity / heat / blob density / collision strength → their dedicated lava-physics dials;</item>
/// <item>scroll speed [0,1] is mapped onto the useful zoom band via
/// <see cref="MixZoomMapping.ScrollKnobToSeconds"/> and pushed through <c>setScrollSpeed</c>
/// (higher speed → tighter window → faster scroll);</item>
/// <item>gradient rotation speed → <c>setGradientRotationSpeed</c> (live OKLab anchor rotation);</item>
/// <item>gravity / heat / collision strength → their dedicated lava-physics dials;</item>
/// <item>fluid amount → <c>setFluidAmount</c> (blob count + volume); fluid viscosity →
/// <c>setFluidViscosity</c> (cohesion / sphere-restoration) — the Phase 10 split of the
/// former single density knob;</item>
/// <item>waveform width → the ribbon-extent uniform.</item>
/// </list>
/// </summary>
private async Task PushControlsAsync()
{
if (_handle is null) return;
// Scroll speed is a normalized [0,1] axis; map it to the visible time-span the renderer scrolls
// through. The log map keeps the even-to-the-hand feel the old zoom slider had.
var visibleSeconds = MixZoomMapping.FractionToSeconds(ControlState.ScrollSpeed);
// Scroll speed is a normalized [0,1] axis; map it onto the useful zoom band (Phase 10 retune —
// the knob's full travel now covers the 60%100% zoom range, dropping the dead slow/wide end).
var visibleSeconds = MixZoomMapping.ScrollKnobToSeconds(ControlState.ScrollSpeed);
await _handle.InvokeVoidAsync("setScrollSpeed", visibleSeconds);
await _handle.InvokeVoidAsync("setGradientRotationSpeed", ControlState.GradientRotationSpeed);
await _handle.InvokeVoidAsync("setLavaGravity", ControlState.LavaGravity);
await _handle.InvokeVoidAsync("setLavaHeat", ControlState.LavaHeat);
await _handle.InvokeVoidAsync("setBlobDensity", ControlState.BlobDensity);
await _handle.InvokeVoidAsync("setFluidAmount", ControlState.FluidAmount);
await _handle.InvokeVoidAsync("setFluidViscosity", ControlState.FluidViscosity);
await _handle.InvokeVoidAsync("setCollisionStrength", ControlState.CollisionStrength);
await _handle.InvokeVoidAsync("setWaveformWidth", ControlState.WaveformWidth);
}