Grand Diomande Research · Full HTML Reader

LUME x Duncan Fewkes: Complete Implementation Handoff for Codex

LUME is a real-time depth-camera visualization bar. Physical enclosure (500x120x85mm ASA 3D-printed shell) houses an Orbbec Femto Mega depth camera (640x576 @ 30fps), UMA-8 USB microphone array, and a GMKtec K11 mini-PC (Ryzen 9 8945HS, Radeon 780M ~9 TFLOPS, 32GB DDR5, 1TB NVMe). The K11 captures depth + audio, streams over UDP to Unity, which renders interactive particle/fluid visuals on a 1920x440 IPS bar display (60Hz, 500 nits, mini HDMI + USB-C) mounted flush on top of the bar shell.

Embodied Trajectory Systems technical note experiment writeup candidate score 40 .md

Full Public Reader

LUME x Duncan Fewkes: Complete Implementation Handoff for Codex

Part 1: What LUME Is

LUME is a real-time depth-camera visualization bar. Physical enclosure (500x120x85mm ASA 3D-printed shell) houses an Orbbec Femto Mega depth camera (640x576 @ 30fps), UMA-8 USB microphone array, and a GMKtec K11 mini-PC (Ryzen 9 8945HS, Radeon 780M ~9 TFLOPS, 32GB DDR5, 1TB NVMe). The K11 captures depth + audio, streams over UDP to Unity, which renders interactive particle/fluid visuals on a 1920x440 IPS bar display (60Hz, 500 nits, mini HDMI + USB-C) mounted flush on top of the bar shell.

The visual aesthetic is directly inspired by Duncan Fewkes (@duncan.fewkes on Instagram), who builds similar installations commercially under the brand HOLOVIS. His target hardware is an A6000 at 60fps. We have 83 of his reels archived (spanning 2025-10-20 to 2026-04-25) with 69 Gemini visual analyses on disk. This document specifies exactly what to build from that corpus, with concrete numeric values for every preset.

Working directory: `Desktop/lume-commerce/software/demo/unity/lume_pcloud/`

---

Part 2: What's Already Built (17 Components, 3 Compute Shaders, 5 Wire Formats)

All source files at `Desktop/lume-commerce/software/demo/unity/lume_pcloud/Assets/`

2A: Core Pipeline Components

FileExec OrderWhat It DoesKey Globals Published
`Scripts/LumeDisplayController.cs`-100Auto-detects 1920x440 bar display in Display.displays[]. Sets camera to 25 deg vertical FOV (gives ~100 deg horizontal at 4.36:1 aspect). Near-black background (0.01, 0.01, 0.02). Two modes: Production (K11 primary, fullscreen) vs Development (Mac4 secondary, windowed preview)None
`Scripts/LumeUdpReceiver.cs`defaultBinds UDP :9700. Dispatches by 4-byte magic: LUME (0x4C554D45) = cloud points, LUMD (0x4C554D44) = raw u16 depth, LUMF (0x4C554D46) = audio FFT, LUMM (0x4C554D4D) = mocopi skeleton, LUMC (0x4C554D43) = reservedNone (feeds other components)
`Scripts/LumeDepthReprojector.cs`defaultGPU compute shader dispatch: u16 raw depth buffer to world-space float4 positions via pinhole unproject. Exposes DepthRawBuffer, W, H, Scale, FrameCounter for downstream consumers`_LumeWorldInv` (Matrix4x4), `_LumeIntrinsics` (Vector4: fx,fy,cx,cy), `_LumeDepthDims` (Vector4: w,h,scale,0)
`Scripts/LumePointRenderer.cs`defaultGPU instanced billboard renderer. Uses GraphicsBuffer (NOT ComputeBuffer -- Unity 6 VFX Graph requires GraphicsBuffer). Exposes `LiveCount` property for overlay/VFX consumers to know how many valid points exist. Supports Cloud mode (pre-projected) and Depth mode (GPU-reprojected)`PositionsBuffer`, `ColorsBuffer` via Shader.SetGlobalBuffer
`Scripts/LumeOpticalFlow.cs`0Two-kernel compute: CSFrameDiff (8x8 atomic accumulation for scalar motion magnitude) + CSLucasKanade (5x5 dense optical flow on linearized depth). Allocates RG16F flow render texture at depth resolution. AsyncGPUReadback for CPU-side scalar. EMA smoothing + decay-on-stall`_FlowField` (Texture2D RG16F, bilinear clamp), `_InnerSpread` (float, 0-1 scalar motion magnitude), `_FlowLkActive` (float, 0 or 1 gate)
`Scripts/LumeFluidSim.cs`1002D Eulerian fluid simulation, 256x256 grid. Keijiro StableFluids-style. Kernels: advection, Jacobi pressure solve (20 iterations), curl-noise injection. Reads `_FlowField` RG16F as velocity source. Audio turbulence from `_AudioLevels`. Delta-time multiplied by `_LumeTimeScale` for SuperHot coupling. Has `ApplyPreset(LumeFluidSimPreset)` public API. Three static factory presets: Calm, Reactive, Storm`_FluidDensity` (Texture2D, global)
`Scripts/LumeMotionGate.cs`150SuperHot mode from Duncan's E568. Reads `_InnerSpread` global, applies EMA-smoothed power-curve remap to [minScale=0.10, maxScale=1.00]. `applyToUnityTimeScale` toggle OFF by default (audio-safe -- don't freeze AudioSource). powerCurve=2.0, smoothing=0.05`_LumeTimeScale` (float, 0.1-1.0)
`Scripts/LumeAudioFftReceiver.cs`200LUMF UDP receiver bound to :9701. Parses 84-byte fixed datagram: magic (4B), timestamp (8B), RMS (4B), spectral centroid (4B), onset flag (1B), pad (3B), 4 EQ bands (16B: bass/low-mid/high-mid/treble as float32), 8 MFCC coefficients (32B), reserved (12B). Overrides `_AudioLevels`/`_OutlineFlash`/`_InnerSpread` when fresh, yields silently when stale. Beat-spike + transient-edge boost`_AudioLevels` (Vector4: rms, centroid, onset, reserved), `_OutlineFlash` (float), overrides `_InnerSpread`
`Scripts/LumeAudioReactor.cs`0Wave 1 fallback: uses AudioSource.GetSpectrumData when LUMF UDP is absent. Public setters: SetInnerSpread(), etc. DO NOT MODIFY this class (parallel session contract)Same globals as LumeAudioFftReceiver but lower fidelity
`Scripts/LumeTransientForcePusher.cs`220Third audio channel per Duncan's E535. [RequireComponent(VisualEffect)]. Rising-edge onset detection from LumeAudioFftReceiver.TryGetLatest(). Fires `SendEvent("OnTransient")` on VFX Graph + writes decaying Vector3 `ImpulseForce` parameter. Default: magnitude=6, decay=8/s = 125ms half-life. Fallback: `_OutlineFlash` > 1.2 threshold when LUMF absentWrites to VFX Graph params, not global shader uniforms
`Scripts/LumeMocopiReceiver.cs`205LUMM UDP receiver bound to :9702. Parses 772-byte skeleton datagram: magic (4B), timestamp (8B), 27 bones x (quaternion 16B + position 12B) = 756B. Smoothed via `TryGetSmoothed()`None (feeds LumeMocopiAnimator)
`Scripts/LumeMocopiAnimator.cs`210Maps 27 mocopi bones to Unity Humanoid avatar rig. Applies transforms each LateUpdate. [DefaultExecutionOrder(210)] ensures it runs after receiver (205)None
`Scripts/LumeSkeletonFluidInjector.cs`defaultReads bone positions/velocities from LumeMocopiAnimator, injects fluid density at bone positions and velocity at bone velocities into LumeFluidSimNone
`Scripts/LumeVfxRuntimeBridge.cs`210[RequireComponent(VisualEffect)]. Pushes ALL relevant globals into VFX Graph VisualEffect instance every LateUpdate. Caches Has* presence flags on enable. Re-binds Positions buffer only on renderer reallocation. Pushes: _AudioLevels, _OutlineFlash, _InnerSpread, _FlowField, PositionsBuffer, ImpulseForce, LumfActive, FluidDensityNone (writes to VFX Graph, not global)
`Scripts/LumeCalibrationPanel.cs`defaultF12 IMGUI panel. 26 tunables across 7 component sections. Runtime reflection on [SerializeField] private fields. Per-install JSON persistence at Application.persistentDataPath/lume-calibration.json. Auto-save on hide/disable, load on enable. Window ID 8120. DraggableNone
`Scripts/LumeProceduralTunnel.cs`defaultGenerates 6144-point cylindrical helix. Pins Cloud mode. Used for demo backdropNone

2B: Preset Infrastructure (Classes Built, Zero .asset Instances)

FileCreateAssetMenu PathFields
`Scripts/Vfx/LumeVfxPreset.cs`"LUME/VFX Preset"`intensity` float [Range(0,3)] default 1, `density` float [Range(0,1)] default 0.5, `count` int [Range(100,200000)] default 30000, `gravity` float [Range(-20,20)] default 0, `initialVelocity` float [Range(0,50)] default 1, `lifetime` float [Range(0.1,30)] default 3, `spawnRate` float [Range(1,5000)] default 500, `baseColor` Color default (0.30,0.35,0.85,1.0) blue-purple, `particleTex` Texture2D nullable, `materialOverride` Material nullable
`Scripts/Vfx/LumeFluidSimPreset.cs`"LUME/Fluid Sim Preset"`presetName` string, `viscosity` float [Range(0.00001,0.01)], `diffusion` float [Range(0.000001,0.001)], `dt` float [Range(0.001,0.05)], `flowInjectionScale` float [Range(0,5)], `audioTurbulenceScale` float [Range(0,3)]. Static factories: `Calm()` (visc=0.005, diff=0.0001, flow=0.3, audio=0.1), `Reactive()` (visc=0.00005, diff=0.000005, flow=2.0, audio=1.5), `Storm()` (visc=0.00001, diff=0.000001, flow=4.0, audio=2.5, dt=0.025)
`Scripts/Vfx/LumeAvatarPreset.cs`"LUME/Avatar Preset"Read the file for exact fields -- skeleton/avatar appearance params
`Scripts/Vfx/LumeLightingPreset.cs`"LUME/Lighting Preset"Read the file for exact fields -- key/fill/rim light setup
`Scripts/Vfx/LumeEnvironmentPreset.cs`"LUME/Environment Preset"Read the file for exact fields -- skybox/fog/background

2C: Editor/Operator Panel (scaffold, ugly, empty lists)

File: `Scripts/Vfx/LumeVfxEditor.cs` (~13KB, fully featured scaffold)

  • Toggle: backtick key (CHANGE TO F1)
  • Hidden by default
  • Procedural Canvas built in Awake(): left-side black panel, 340px wide
  • 6 Dropdown slots: VFX01, VFX02, Avatar, Lighting, Environment, FluidSim
  • Each backed by `List<ScriptableObject>` -- these lists are PUBLIC + SERIALIZED but currently empty
  • Auto-cycle toggle: switches VFX01 every N bars when `LumeRuntime.currentBpm > 0`
  • `ApplyAll()` dispatches to per-category apply methods:
  • VFX: pushes `baseColor` via MaterialPropertyBlock to LumePointRenderer
  • FluidSim: sets `LumeRuntime.activeFluidPresetId` (LumeFluidSim reads this)
  • Avatar: swaps prefab on "Avatar" GameObject
  • Lighting: sets Key/Fill/Rim light colors + intensities
  • Environment: sets skybox material + fog color/density
  • Load/Save scene buttons (stubbed for Wave 2)

2D: Compute Shaders

FileKernelsGridPurpose
`Shaders/LumeFluidSim.compute`Advect, Project (Jacobi x20), InjectFlow, InjectAudioTurbulence256x2562D Eulerian fluid. Reads _FlowField, _AudioLevels. Writes density/velocity RTs
`Shaders/LumeOpticalFlow.compute`CSFrameDiff (8x8 atomic accum), CSCopyDepth (memcpy), CSLucasKanade (5x5 dense LK)depth resolutionFrame-diff scalar + vector flow field. Outputs RG16F flow texture
`Shaders/LumeDepthReproject.compute`CSReproject (8x8)depth resolutionu16 raw depth buffer to world-space float4 via pinhole intrinsics

2E: Vertex/Fragment Shaders

FilePurpose
`Shaders/LumePointInstanced.shader`Instanced billboard renderer. Samples `_FlowField` for vector-localized inner spread + hue bias by atan2(flow). `_FlowLkActive` gates fallback. Duncan-lite dissolve/twinkle pass added

2F: Python Publishers

FilePortMagicDatagram SizePurpose
`software/demo/pointcloud_pub.py`:9700LUME (cloud) or LUMD (raw depth)LUME: 16B header + N12B points. LUMD: 40B header + N2B pixelsDepth publisher. `--raw-depth` flag for Wave 2+. `--synthetic` for test data. `--host` and `--port` configurable
`software/demo/audio_pub.py`:9701LUMF84 bytes fixedAudio FFT publisher. `--synthetic` for test. `--mic` for real audio. Hann FFT, flux onset detector. 60fps
`software/demo/mocopi_bridge.py`:9702LUMM772 bytesSony mocopi skeleton bridge. Reads binary :12351 from XYN Motion Studio, repacks as LUMM
`software/demo/lume_packet_inspector.py`:9700 + :9701allvariesDebug tool. Binds both ports, prints rolling diagnostics, drop estimates, intrinsics drift

2G: 17 Global Shader Uniforms

UniformTypeProducerConsumers
`_AudioLevels`Vector4 (rms, centroid, onset, reserved)LumeAudioFftReceiver or LumeAudioReactorShaders, VFX Graph
`_OutlineFlash`floatLumeAudioFftReceiverShaders (outline brightness)
`_InnerSpread`float (0-1)LumeOpticalFlow or LumeAudioFftReceiverLumeMotionGate, shaders
`_FlowField`Texture2D RG16FLumeOpticalFlowLumeFluidSim, LumePointInstanced shader
`_FlowLkActive`float (0 or 1)LumeOpticalFlowLumePointInstanced shader (fallback gate)
`_FluidDensity`Texture2DLumeFluidSimShaders, VFX Graph
`_LumeTimeScale`float (0.1-1.0)LumeMotionGateLumeFluidSim (dt multiplier)
`_LumeWorldInv`Matrix4x4LumeDepthReprojectorShaders (world to depth UV projection)
`_LumeIntrinsics`Vector4 (fx,fy,cx,cy)LumeDepthReprojectorShaders (pinhole model)
`_LumeDepthDims`Vector4 (w,h,scale,0)LumeDepthReprojectorShaders (buffer dimensions)
`PositionsBuffer`GraphicsBufferLumePointRendererVFX Graph, LumeFlowParticleOverlay
`ColorsBuffer`GraphicsBufferLumePointRendererVFX Graph

2H: Test Surface

From `Desktop/lume-commerce/software/demo/`:

bash
python3 -m pytest tests/ -q              # 33 fast tests (sub-second)
python3 -m pytest tests/ -m slow -v      # 4 integration tests (~5s)
LUME_RUN_SLOW=1 python3 -m pytest tests/ # all 37

Tests cover: struct sizes, magic round-trip, FFT bounds, onset edge cases, end-to-end loopback, LUMF/LUMD/LUME golden wire bytes, packet inspector e2e, integration streams.

---

Part 3: Duncan's 7-Layer Visual System

### Layer 1: Depth Point Cloud -- SHIPPED
Femto Mega captures at 640x576 @ 30fps. Raw u16 depth reprojected to world-space float4 via GPU compute. Rendered as instanced billboards. 92,160 live points per frame (640*576/4 subsampled). GraphicsBuffer path ready for VFX Graph.

### Layer 2: Optical Flow -- SHIPPED
CRITICAL RULE: Depth MUST be linearized BEFORE optical flow. Z-near bias in raw depth kills far-field motion detection (Duncan E460). Our implementation does this correctly in CSCopyDepth.

Lucas-Kanade 5x5 dense flow produces RG16F velocity texture. Scalar magnitude (frame-diff) published as `_InnerSpread`. Cost: ~0.5ms GPU.

### Layer 3: Fluid Simulation -- SHIPPED (needs 6 more presets)
2D Eulerian, 256x256 grid. Advection + Jacobi pressure solve (20 iterations). Flow field injects velocity. Audio injects turbulence. Publishes `_FluidDensity`. Time-step multiplied by `_LumeTimeScale` (SuperHot coupling). `ApplyPreset()` public API exists.

Currently ships 3 presets. Duncan has 6 named presets (see Part 5B).

On the 4.36:1 bar display, the 256x256 square grid sampled in screen space will appear horizontally stretched. This is a known visual artifact -- tune after seeing it on the actual display.

### Layer 4: VFX Graph Particles -- NOT SHIPPED
The particle swarm that makes it look like Duncan. This is the single biggest visual gap. The runtime bridge is fully wired (pushes 8 params + events into VisualEffect). The `.vfx` node graph needs authoring in Unity's VFX Graph editor (GUI-only task, cannot be done from CLI).

Full node graph recipe is at `Assets/VFX/README-LumeFlowParticles.md` with step-by-step instructions for Spawn, Initialize, Update, Output blocks, 7 exposed parameters, and the OnTransient event.

Current workaround: `Assets/Scripts/LumeFlowParticleOverlay.cs` + `Assets/Shaders/LumeFlowParticleOverlay.shader` provide a code-driven 26K particle overlay. This is explicitly labeled TEMPORARY. Do not delete it until the real .vfx is authored and verified.

Layer 5: Three-Channel Audio Reactivity -- SHIPPED

Duncan's canonical three-channel model (defined in E579, refined across E535/E512/E589):

ChannelNameWhat It DrivesSignal SourceVisual PurposeOur Implementation
1Outline (form)Fresnel power multiplied by RMS. Flash = step(beat_threshold, rms). Emissive bright outline around body silhouetteRMS + 4 EQ bands from LUMF"Where you are" -- shows the user their position`_OutlineFlash` global float + `_AudioLevels.x` (RMS)
2Inner (action)Motion magnitude from optical flow on linearised depth modulates inverse fresnel. High motion = color spreads inward from silhouette edgeOptical flow scalar from CSFrameDiff"What you're doing" -- shows the user their movement`_InnerSpread` global float + `_FlowField` texture
3Impulse (kick)AddForce event in VFX Graph fires on transient onset ONLY (not smoothed RMS). Velocity kick applied to existing particles. Rising-edge detection prevents re-fireAudio transient detector (peak/onset flag in LUMF byte 20)"When it hits" -- punctuates the beat`LumeTransientForcePusher.cs`: SendEvent("OnTransient") + ImpulseForce param

### Layer 6: SuperHot Motion Gate -- SHIPPED
From Duncan's E568. Sim time scales with body motion: move fast and visuals are alive, stand still and they freeze. His approach: "utility script hooks user motion to fluid sim timescale with a fairly quick smoothing/falloff." Our implementation: `LumeMotionGate.cs`, power curve from `_InnerSpread` to `_LumeTimeScale`.

Default tuning: minScale=0.10, maxScale=1.00, powerCurve=2.0, EMA smoothing=0.05. Tunable via F12 calibration panel.

### Layer 7: Mocopi Skeleton -- SHIPPED (code), NOT TESTED (hardware)
27-bone Sony mocopi skeleton mapped to Unity Humanoid avatar. Bones inject into fluid sim via LumeSkeletonFluidInjector. Blocked on mocopi hardware pairing with K11. The code path is complete and compiles.

---

Part 4: Duncan's Design Rules (MUST Follow -- Violations Produce Visually Wrong Results)

These come directly from Duncan's own reel captions, Inspector dumps, and self-critiques across 72 reels:

1. Audio drives POSITION directly, never force/velocity. "Audio response boosting position directly (rather than feeding into force levels) makes it nice and snappy without blowing up the fluid sim." -- E512

2. Apply EQ BEFORE auto-gain, or use fixed dynamic range. If you auto-gain first, the EQ bands all compress to the same level and you lose the bass/treble separation that drives the visual layering -- E512

3. Bass = large, low-frequency positional offsets (slow wave bulges). Mid+High = small, high-frequency timer-cycled offsets -- E512

4. Three separate audio channels, not one mixed signal. Outline, Inner, and Impulse are independent visual responses. Mixing them into one channel collapses the visual vocabulary -- E579

5. Particles MUST NOT cast shadows. Self-collision/self-shadowing on 100K+ particles is a visual mess and a perf cliff -- E488

6. Pure gravity + collision = visually dead. ALWAYS layer fluid-sim kick + turbulence on top of gravity. "Without the fluid sim the gravity drop just looks like sand falling." -- E488, E598

7. ShortThrow fluid preset for full-screen + multi-person installations. Shorter velocity propagation = better isolation between multiple people in the frame -- E512

8. Every preset enum's last entry MUST be None (off state). This allows cleanly disabling any layer -- E516

9. Coupled sim systems: pick ONE direction or feedback loop goes crazy. "Making the motion of the Wobbly Lad input to fluid sim meant I had to disable the fluid sim force contribution to the motion, otherwise it would feedback and he'd go apeshit." -- E549, E556

10. Per-install calibration is a hard product requirement. "Need to rebalance threshold etc values for the actual usage distance -- probably easiest to expose in settings and debug menu to allow for tweak values for each install individually." -- E568

11. holdDuration = 0.25s for clone snapshots, then ease-in to gravity via AnimationCurve (not constant) -- E491

12. Speed-to-brightness is the "twinkle" parameter. Power curve + threshold, tune by ear. `brightness = pow(speed / maxSpeed, k)` where k = 2-4 -- E490

13. Audio-transient response must read clearly without losing form coherence. "Needs more definite/large change in motion, but without it going crazy and losing coherence." -- E535

14. Depth linearization BEFORE optical flow. Z-near bias in raw depth kills far-field motion detection -- E460

15. Emission Scale range is 0.00 to 0.05, NOT 0 to 1. The entire useful visual range for emissive particles/surfaces is in that tiny band. Going above 0.05 blows out to white -- E593

16. One rotational symmetry is enough; don't compound symmetries. 16-clone radial is the max, and even that can look busy -- E479

17. "Don't quit at the first disappointing test" -- E488. Iterate parameters before declaring a technique failed.

18. The negative-space look (Lighting=None + emissive depth) is its own valid aesthetic. Not a bug, it's EmissiveOnly mode at 160-170fps -- E516

---

Part 5: Concrete Preset Values to Generate

5A: VFX Presets (LumeVfxPreset) -- 13 presets

Create at `Assets/Resources/LumePresets/Vfx/`. Naming: `e###-slug.asset`

Write an editor script at `Assets/Editor/LumePresetGenerator.cs` with menu item `Lume > Generate Duncan Presets` that creates all assets programmatically via:

csharp
var preset = ScriptableObject.CreateInstance<LumeVfxPreset>();
// set fields
AssetDatabase.CreateAsset(preset, "Assets/Resources/LumePresets/Vfx/e598-dissolving-clones.asset");

---

e598-dissolving-clones.asset

csharp
intensity = 2.5f;
density = 0.6f;
count = 150000;
gravity = -2.0f;
initialVelocity = 2.0f;
lifetime = 8.0f;
spawnRate = 3000f;
baseColor = new Color(0.70f, 0.30f, 0.90f, 0.80f);  // purple dissolve

Source: E598 (4,081 likes, highest engagement reel). Frozen poses captured on beat-trigger (transient channel), layered with live mesh. Gravity eases in over ~2.5s via AnimationCurve (not constant drop). holdDuration = 0.25s for the snapshot. CRITICAL: always layer fluid-sim kick + turbulence on top of gravity or it looks like sand falling. Speed-to-brightness mapping on dissolving particles. Dual-channel palette: red (frozen snapshot layer) + purple/orange (live depth mesh). Particles MUST NOT cast shadows.

---

e568-superhot-cubes.asset

csharp
intensity = 2.0f;
density = 0.5f;
count = 150000;
gravity = 0.0f;
initialVelocity = 0.5f;
lifetime = 30.0f;  // near-infinite in frozen-buffer mode
spawnRate = 500f;
baseColor = new Color(0.40f, 0.60f, 0.95f, 1.0f);  // cool blue base

Source: E568. SuperHot motion-gated timescale. LOD variant uses cube-shaped particles (DepthCubes enum). Per-particle hue cycling: `hue = baseHue + speedScalar * shiftAmount` -- fast particles get hue-shifted, slow stay near base. Frozen velocity buffer mode: velocity field frozen while particles continue flowing through it = laminar streams along etched-in paths. Duncan's Motion Settings for this mode: Optical Flow Imagine X/Y (3-9), Drag Max Intensity X/Y (0.0-0.1+), Drag Scale ~0.006, Image Threshold (0-255, 50 = more concentrated), Image Invert (0/1), Motion Blur (0/1). Per-install calibration is mandatory.

---

e560-frozen-sim-buffers.asset

csharp
intensity = 1.5f;
density = 0.4f;
count = 100000;
gravity = 0.0f;
initialVelocity = 1.0f;
lifetime = 30.0f;  // infinite (frozen field)
spawnRate = 200f;
baseColor = new Color(0.50f, 0.50f, 0.80f, 0.90f);  // inherits palette when frozen

Source: E560. "Accidental art" -- discovered when stopping Unity editor playback. The fluid sim stops updating but the density/velocity RenderTextures persist because they exist in the asset database. Particles keep flowing through the frozen velocity field, creating laminar streams along etched-in paths. "Frozen forms in the motion paths that you don't get while the sim is updating." To implement: add a `FreezeVelocityField` bool toggle to LumeFluidSim that stops dispatching the compute shader but keeps the RTs alive.

---

e549-kelp-seagrass.asset

csharp
intensity = 1.0f;
density = 0.7f;
count = 40000;  // strip/ribbon particles, not quads
gravity = -0.3f;
initialVelocity = 0.5f;
lifetime = 15.0f;  // continuous sway
spawnRate = 100f;
baseColor = new Color(0.15f, 0.65f, 0.40f, 0.85f);  // green/teal organic

Source: E549. VFX Graph particle strips (ribbon particles) with per-strip stiffness and parent-to-child force propagation along the chain. Audio coupling: FFT bin index mapped to Y position on strip -- bass drives bottom leaves, treble drives top leaves. Audio levels must be tuned LOW or the kelp whips around wildly. Fish boids variant: audio params on cruising speed + separation, also tuned low so fish don't zip away. CRITICAL: unidirectional fluid coupling ONLY (body motion -> fluid sim, but NOT fluid sim -> body motion, or it feedback-loops and "goes apeshit"). Self-shadowed HDRP lit material (we'll approximate in URP). Duncan's self-critique: "Pretty huge fail when I finally check some footage of real kelp and realise mine looks bugger all like actual kelp."

---

e545-pinscreen.asset

csharp
intensity = 1.5f;
density = 1.0f;  // full grid coverage
count = 10000;   // instanced cube grid ~100x100
gravity = 0.0f;
initialVelocity = 0.0f;  // static grid, Z pushed by depth
lifetime = 30.0f;
spawnRate = 0f;  // permanent mesh grid, not ephemeral particles
baseColor = new Color(0.90f, 0.90f, 0.90f, 1.0f);  // white plastic front

Source: E545. Not traditional particles -- instanced cube grid locked in XY positions. Z-position pushed by two sources: (1) reprojected depth camera pushes cubes FORWARD, (2) fluid sim dye buffer pushes cubes BACKWARD from mid-plane. Depth camera overrides fluid push (depth > dye). The dye scalar is multi-purpose: drives BOTH geometric z-displacement AND HDRP thickness (transmission/absorption colour). Two-tone material: front-facing = white plastic, rear-facing = gold accent. Transparent variant uses HDRP thickness for transmission color. Less "swarmy", more "architectural." A fundamentally different rendering approach than the other presets.

---

e535-particle-motion-kick.asset

csharp
intensity = 2.5f;
density = 0.5f;
count = 120000;
gravity = -0.5f;
initialVelocity = 3.0f;
lifetime = 4.0f;
spawnRate = 2000f;
baseColor = new Color(0.85f, 0.50f, 0.20f, 1.0f);  // warm orange sparks

Source: E535. THE impulse channel preset. This is where Duncan first tested the third audio channel. forceMagnitude=6, decayPerSec=8 (125ms half-life). Rising-edge detection prevents re-fire (only fires on the onset's leading edge, not while the flag stays high). Impulse bypasses fluid sim entirely -- fires directly into VFX Graph via SendEvent("OnTransient") + ImpulseForce param. "WIP testing quick 'overamp' method for audio transients to add a kick to the particle motion. Not very noticeable so needs more definite/large change in motion, but without it going crazy and losing coherence."

---

e512-swirly-particles.asset

csharp
intensity = 1.5f;
density = 0.5f;
count = 150000;
gravity = 0.0f;
initialVelocity = 1.5f;
lifetime = 5.0f;
spawnRate = 1500f;
baseColor = new Color(0.45f, 0.35f, 0.85f, 0.90f);  // hue-cycling purple base

Source: E512. THE audio coupling rules reference reel. Floor-spawn vortex particles. 3-layer hue stack (all active simultaneously): (1) global slow cycle = 1 full revolution per 30 seconds applied to ALL particles, (2) per-particle spawn color from RGB luma at body position, (3) per-particle speed-driven shift each frame. Two noise axis modes: `XZ_Twist_Contained` (gentle, contained swirls) vs `Y_Lift_With_LowFreqBulges` (overflowing upward bulges). Speed-to-brightness = "twinkle" (power curve + threshold, tune by ear). ShortThrow fluid preset works better for this at full-screen scale + multiple people.

---

e589-dat-funk.asset

csharp
intensity = 2.0f;
density = 0.6f;
count = 120000;
gravity = -0.3f;
initialVelocity = 2.0f;
lifetime = 3.5f;
spawnRate = 2500f;
baseColor = new Color(0.80f, 0.25f, 0.60f, 1.0f);  // magenta-pink funk

Source: E589. Dance-reactive mode. Surface shader reacts to BOTH audio AND motion simultaneously: brightness flashes on beat (audio channel 1), fresnel pulled down where motion occurs = color spreads from silhouette edge inward (channel 2). Full 3-source hue stack active. Size/glow fade by speed. "Music on/off" toggle proven as a real operator feature in E587 -- audio reactivity should be gatable/toggleable per install. "Outline (form) reacts to audio: brightness flashes on beat -- shows you 'where you are.' Inner color (action) reacts to motion: hue/spread driven by motion vectors -- shows you 'what you're doing.'"

---

e593-blobby-guys.asset

csharp
intensity = 2.0f;
density = 0.8f;
count = 80000;  // source particles for marching cubes
gravity = 0.0f;
initialVelocity = 0.5f;
lifetime = 10.0f;
spawnRate = 500f;
baseColor = new Color(0.10f, 0.80f, 0.90f, 0.70f);  // teal/slime

Source: E593. Marching cubes metaball morph. Source particles feed MC mesh reconstruction -- output is continuous isosurface, not discrete particles. Material variants: slime, water, pink energy, oily, chrome/mercury. Emission HDR colors: bright blue RGB(109,255,255) = (0.43,1.0,1.0), green RGB(133,255,0) = (0.52,1.0,0.0), magenta RGB(255,0,255) = (1.0,0.0,1.0). CRITICAL: Emission Scale operating range is 0.00-0.05 ONLY (not 0-1). Shader: Custom/Thickness_Apply with IOR=1.33 (water), Translucency=0.003, Fresnel Multiplier=0.0 (OFF by default). Inverse mode (E468): alpha proportional to (1-thickness) = neon-edge look. Twin-mesh nested MC (E427): inner gold-leaf shell + outer glass, same source particles but two different ISO thresholds (_InnerThreshold and _OuterThreshold). Compute-thickness-to-alpha with tight power curve: `pow(thickness, k)` where k=6-10. Bistable: letters/surfaces stay dark then suddenly glow when thickness crosses threshold. NOTE: Marching cubes compute shader is NOT YET BUILT in our pipeline. This preset will only affect color/count until LumeMarchingCubes.compute + .cs are implemented.

---

e544-logo-testing.asset

csharp
intensity = 1.5f;
density = 0.5f;
count = 60000;
gravity = 0.0f;
initialVelocity = 0.5f;
lifetime = 8.0f;
spawnRate = 500f;
baseColor = new Color(0.90f, 0.85f, 0.70f, 1.0f);  // white/gold volumetric

Source: E544. Logo letters as simultaneous: blendshape inflation target, reflection probe hit-target, particle spawn surface, SDF for "punch through" reveals. Wordmark feedback loop (E489): BlendShape inflation driven by FFT bands -> inflation generates motion vectors -> motion vectors injected into fluid sim -> fluid sim pushes nearby particles. The wordmark BECOMES part of the audio-reactivity loop. Uses InvertedObstacle fluid preset: fluid flows INSIDE body silhouette only. ShortThrow for cymatics (E528): particles stuck on letters bouncing internally. HolePunch depth mode: renders silhouette on top of all particles (no z-test) -- hurts depth cues but you never lose track of yourself. "Interactive text displayed and reacts volumetrically to the user's movements." NOTE: Wordmark-as-blendshape requires modeling LUME wordmark as a real Mesh (currently just a Blender deboss in CAD). Deferred until product copy finalized.

---

e606-maximalism.asset

csharp
intensity = 3.0f;
density = 0.8f;
count = 200000;
gravity = -1.0f;
initialVelocity = 5.0f;
lifetime = 6.0f;
spawnRate = 5000f;
baseColor = new Color(0.60f, 0.20f, 1.0f, 1.0f);  // saturated purple

Source: E606. Maximum everything. High particle count, additive blend, saturated colors, all three audio channels active at high gain, fluid sim at Reactive or Storm. Wide FOV vs Narrow FOV comparison. This is the "showcase" preset.

---

e538-motion-sparks.asset

csharp
intensity = 2.5f;
density = 0.3f;
count = 80000;
gravity = 0.0f;
initialVelocity = 8.0f;
lifetime = 1.5f;  // short bright bursts
spawnRate = 4000f;
baseColor = new Color(1.0f, 0.85f, 0.30f, 1.0f);  // bright gold sparks

Source: E535-E538. Sparks spawn only on motion edges (where optical flow magnitude is high). High initial velocity, very short lifetime = brief streaks. Speed-to-brightness mapping makes them flash as they fire outward and dim as they slow. The "fireworks on movement" aesthetic.

---

e530-depth-spawn.asset

csharp
intensity = 1.0f;
density = 0.9f;
count = 92160;  // match depth frame pixel count
gravity = 0.0f;
initialVelocity = 0.0f;
lifetime = 2.0f;
spawnRate = 46000f;  // half population per second
baseColor = new Color(0.30f, 0.35f, 0.85f, 1.0f);  // default blue-purple

Source: E530+. The raw depth aesthetic. Particles spawn strictly at depth buffer positions with zero post-spawn motion. Minimal processing. This is the "honest" mode that just shows what the camera sees, re-rendered as particles.

---

5B: Fluid Sim Presets (LumeFluidSimPreset) -- 6 presets

Create at `Assets/Resources/LumePresets/FluidSim/`. Duncan names these verbatim in E534:

default.asset

csharp
presetName = "Default";
viscosity = 0.0001f;
diffusion = 0.00001f;
dt = 0.016f;
flowInjectionScale = 1.0f;
audioTurbulenceScale = 0.5f;

Duncan: "Nice swishy pressure wave, but does sometimes look too large a motion." General purpose starting point.

---

default-smooth.asset

csharp
presetName = "Default_Smooth";
viscosity = 0.0005f;
diffusion = 0.00005f;
dt = 0.016f;
flowInjectionScale = 0.8f;
audioTurbulenceScale = 0.3f;

Duncan: Smoothed Default for calmer scenes. Higher viscosity damps oscillations.

---

long-throw.asset

csharp
presetName = "LongThrow";
viscosity = 0.00002f;
diffusion = 0.000005f;
dt = 0.016f;
flowInjectionScale = 2.5f;
audioTurbulenceScale = 1.0f;

Duncan: "Lot of velocity propagation, tends to thin down over distance, lacks vorticity/swirls." Good for dramatic long-arc gestures.

---

mid-throw.asset

csharp
presetName = "MidThrow";
viscosity = 0.00005f;
diffusion = 0.00001f;
dt = 0.016f;
flowInjectionScale = 1.5f;
audioTurbulenceScale = 0.8f;

Duncan: "Puts more vorticity back, helps it not drop to ground level." Balanced between reach and visual interest.

---

short-throw.asset

csharp
presetName = "ShortThrow";
viscosity = 0.001f;
diffusion = 0.0001f;
dt = 0.016f;
flowInjectionScale = 0.5f;
audioTurbulenceScale = 0.3f;

Duncan: "Very local, helps leave trails/particle clones lingering in space." BEST for full-screen scale + multiple people (per E512). Good for cymatics-on-logo (particles bounce internally).

---

inverted-obstacle.asset

csharp
presetName = "InvertedObstacle";
viscosity = 0.0001f;
diffusion = 0.00001f;
dt = 0.016f;
flowInjectionScale = 1.0f;
audioTurbulenceScale = 0.5f;

Duncan: "Keep fluid flow inside the offscreen reprojected depth render." Body silhouette REPELS fluid instead of injecting velocity. Fluid flows only OUTSIDE the body. Requires modifying LumeFluidSim to invert the depth mask when this preset is active. Good for: logo volumetric reveals, negative-space aesthetics.

---

5C: Lighting Presets (LumeLightingPreset) -- 7 presets

Create at `Assets/Resources/LumePresets/Lighting/`. Read `LumeLightingPreset.cs` first to see exact field names.

Asset NameKey LightFill LightRim LightFogNotes
`fog-spot-left-right-1.asset`Left spot, warmRight spot, coolNoneVolumetric fog enabledTwo opposed spotlights with atmospheric haze
`fog-spot-left-right-2.asset`Left spot, warm, tighter coneRight spot, cool, tighter coneNoneVolumetric fogVariant: tighter cone angles for more dramatic falloff
`fog-spot-right.asset`NoneNoneRight spot, dramaticLight fogSingle-source Rembrandt lighting
`fog-spot-overhead.asset`Overhead spot, neutral whiteNoneNoneVolumetricTop-down, stage lighting feel
`fog-point-orbit-pulse.asset`Orbiting point lightNoneNoneLight fogPoint light orbits scene, intensity pulsed by `_AudioLevels.x` (RMS)
`bright-room.asset`Ambient, high intensityFill, high intensityNoneNo fogEven illumination, no drama. "Museum" mode
`emissive-only.asset`NoneNoneNoneNo fogNO scene lights. Particles self-illuminate only. 160-170fps (best perf). The "negative space" look -- confirmed by Duncan as a valid aesthetic, not a bug (E516)

---

5D: Environment Presets (LumeEnvironmentPreset) -- 4 presets

Create at `Assets/Resources/LumePresets/Environment/`. Read `LumeEnvironmentPreset.cs` first.

Asset NameSkyboxFog ColorFog DensityNotes
`studio-sky-sphere.asset`Neutral grey studio HDRIDark grey0.01Standard studio look
`sunset.asset`Warm sunset gradientWarm orange0.02Golden hour mood
`bolivian-salt-flats.asset`Mirror-ground infinite horizon HDRIWhite0.005Infinite reflective plane
`test-room-dark.asset`Solid blackBlack0.0Near-zero ambient. Particles + emissive only. Best for the bar display (dark surround = OLED-like contrast on IPS)

---

Part 6: Per-Particle Hue Stack (3 Simultaneous Sources)

This is HOW Duncan does color across ALL modes. All three layers stack additively:

### Layer 1: Global Slow Cycle
One full hue revolution per 30 seconds, applied uniformly to ALL particles.

hlsl
hue += _Time.y * 0.033; // 1/30 = 0.033 rev/sec

### Layer 2: Per-Particle Spawn Color
At spawn time, sample the depth camera's color texture at the spawn position. Convert RGB to luminance. Map luminance to hue offset.

hlsl
float spawnLuma = dot(spawnColor.rgb, float3(0.299, 0.587, 0.114));
hue += spawnLuma * 0.3;

### Layer 3: Per-Particle Speed-Driven Shift
Each frame, particle velocity magnitude shifts the hue. Fast particles diverge from base hue, slow particles stay near it.

hlsl
float speed = length(velocity);
hue += speed * shiftAmount; // shiftAmount ~0.1-0.3

The result: particles share a family palette but each drifts uniquely based on where it spawned and how fast it's currently moving. This creates the organic, living quality that distinguishes Duncan's work from static particle presets.

---

Part 7: Runtime Picker UI Specification

### Current State
`LumeVfxEditor.cs` has a procedural Canvas with 6 dropdowns. Works but ugly. Preset lists are empty. Toggle is backtick (conflicts with Unity console).

### What to Build
Replace with IMGUI panel mirroring `LumeCalibrationPanel.cs` pattern. That file demonstrates the exact approach:
- `GUI.Window(windowId, rect, DrawCallback, title)` with draggable title bar
- `GUILayout.BeginScrollView()` / `EndScrollView()` for scrollable content
- `GUILayout.HorizontalSlider()` for values, `GUILayout.Button()` for actions
- `GUILayout.Label()` with section headers
- JSON persistence via `JsonUtility.ToJson()` + `File.WriteAllText()` / `File.ReadAllText()`

Detailed Spec

PropertyValue
Toggle key`KeyCode.F1` (NOT backtick -- backtick opens Unity console)
VisibilityHidden by default. `visibleOnStart = false`
Window rect`new Rect(20, 20, 420, 600)`
Window ID`8121` (calibration panel uses 8120, must not collide)
DraggableYes, `GUI.DragWindow(new Rect(0, 0, width, 20))` on title bar
Tabs5 tabs: VFX, Fluid, Lighting, Environment, Avatar
Tab renderingHorizontal row of `GUILayout.Toggle()` styled as toolbar buttons
Per tab contentVertical scrollable list of preset names. Each row: color swatch (16x16 `GUI.DrawTexture` with `baseColor`) + preset name label + "Apply" button
Selected highlightCurrently active preset name shown in bold or different color
Auto-cycle sectionBelow the list: Toggle "Auto-cycle" + int field "BPM divisor" with buttons [4] [8] [16] [32]
Bottom buttons"Save" (persist), "Load" (reload), "Hide" (close panel)
Status line`GUILayout.Label("File: " + PersistPath)` showing the JSON save location

Preset Loading

csharp
void OnEnable()
{
    _vfxPresets = Resources.LoadAll<LumeVfxPreset>("LumePresets/Vfx");
    _fluidPresets = Resources.LoadAll<LumeFluidSimPreset>("LumePresets/FluidSim");
    _lightingPresets = Resources.LoadAll<LumeLightingPreset>("LumePresets/Lighting");
    _envPresets = Resources.LoadAll<LumeEnvironmentPreset>("LumePresets/Environment");
    _avatarPresets = Resources.LoadAll<LumeAvatarPreset>("LumePresets/Avatar");
    Load(); // restore last selection from JSON
}

### Apply Logic (reuse from LumeVfxEditor.cs)
- VFX: push `baseColor` via `MaterialPropertyBlock` to `LumePointRenderer`. Set count/spawnRate/lifetime on VFX Graph if VisualEffect present
- FluidSim: call `FindObjectOfType<LumeFluidSim>().ApplyPreset(preset)` (API already exists)
- Lighting: set Key/Fill/Rim light colors + intensities on scene lights found by tag or name
- Environment: set `RenderSettings.skybox`, `RenderSettings.fogColor`, `RenderSettings.fogDensity`
- Avatar: swap prefab on GameObject named "Avatar" if it exists

JSON Persistence

csharp
[Serializable]
class PickerState
{
    public string selectedVfx;
    public string selectedFluid;
    public string selectedLighting;
    public string selectedEnvironment;
    public string selectedAvatar;
    public bool autoCycle;
    public int bpmDivisor;
    public string savedAt;
}

string PersistPath => Path.Combine(Application.persistentDataPath, "lume-picker.json");

Save on: OnDisable, on selection change, on "Save" button click.
Load on: OnEnable.

### Integration with Auto-Wire
Add to `Assets/Editor/LumeWaveAutoWire.cs`:

csharp
// ---- Wave 8: Duncan preset picker ----------------------------
var picker = root.GetComponent<LumeDuncanPicker>();
if (picker == null) { picker = root.AddComponent<LumeDuncanPicker>(); added++; }

And add to the Verify menu output.

---

Part 8: VFX Editor Slot Enums (Duncan's Verbatim Names)

These are the exact enum values Duncan uses in his Settings panel. Our presets should map to these naming families:

Depth Modes:
  GhostChromatic2, GlassThin, GlassThick, GlassScan1, GlassScan2, GlassScan3,
  DepthCubes, HolePunch, None

VFX Slot 01:
  ClonesAudioDropBright, ParticleClone, ParticleClonesAudio, SwirlyParticles,
  KaleidoscopeRing16, Multiparticle_Spine_FX, None

VFX Slot 02:
  ParticleSystem_Spine_Trail, ClonesSnapshotRed, ClonesSnapshotRedPlexus, None

Painting Modes:
  SpotsLeftRightWithFill, Bounce_Frosted, Bounce_Plastic, None

Lighting:
  FogSpotLeftRight1, FogSpotLeftRight2, FogSpotRight, FogSpotOverhead,
  FogPointOrbitPulse, BrightRoom, SpotsLeftRightWithFill, EmissiveOnly, None

Lighting Profile:
  EmissiveOnly (160-170fps best perf), SpotShadows (70-110fps, 7x shadow cast cost)

Environment:
  Studio_Sky_Sphere, Sunset, BolivianSaltFlats, TestRoomDark, None

Background:
  StudioBG_Grey, Gallery2, TestRoomDark, None

Logo:
  HOLOVIS_Letters_AudioBlendshape, Off

Fluid Sim:
  Default, Default_Smooth, LongThrow, MidThrow, ShortThrow, InvertedObstacle

Body Sim:
  Same values as FluidSim (separate body-driven slot)

FX:
  ClonesSnapshotRed, ClonesSnapshotRedPlexus, None

Spawn Topology:
  FloorPlane, BodyVolume, CeilingPlane, WallPlanes, MidPlane

Noise Axis Mode:
  XZ_Twist_Contained, Y_Lift_With_LowFreqBulges

---

Part 9: Shader Reference Values (Inspector Dumps)

Duncan's Custom/Thickness_Apply shader (E462 Inspector capture)

IOR: 1.33 (water)
Translucency: 0.003
Fresnel Multiplier: 0.0 (OFF by default -- he rarely uses Fresnel on the blobby material)
Emissions Scale: 0.00-0.05 (THE ENTIRE useful range. NOT 0-1. Going above 0.05 = blown out white)
Surface Type: Transparent/Opaque dual-pass

Emission HDR Colors:
  Bright blue: RGB(109, 255, 255) = float3(0.427, 1.000, 1.000)
  Green:       RGB(133, 255, 0)   = float3(0.522, 1.000, 0.000)
  Magenta:     RGB(255, 0, 255)   = float3(1.000, 0.000, 1.000)

Compute thickness power curve: pow(thickness, k) where k = 6-10
  Result: bistable -- surfaces stay dark then suddenly glow when thickness crosses threshold

Our LumeCalibrationPanel -- 26 Tunables (current ranges)

Optical Flow (5 tunables):
  diffNormMm:      20-2000    (motion sensitivity in mm, 200 = shoulder-shift at arm's reach)
  responseGain:    0.1-10     (amplify motion before saturate)
  smoothing:       0.05-1.0   (EMA coefficient)
  decayPerSec:     0-8        (flow decay rate when no motion)
  lkFlowClampPx:   1-32       (max per-component flow in pixels/frame)

Depth Reproject (2 tunables):
  minDepthM:       0.01-1.0   (near clip in meters)
  maxDepthM:       0.5-16     (far clip in meters)

Audio FFT (6 tunables):
  rmsGain:         0.5-16     (RMS amplification)
  beatThreshold:   0-1        (beat detection sensitivity)
  beatSpike:       0-4        (outline brightness on beat)
  transientSpike:  0-4        (additional brightness on transient)
  smoothing:       0.05-1.0   (audio EMA)
  staleTimeout:    0.1-5      (seconds before declaring LUMF stale)

Impulse Force (3 tunables):
  forceMagnitude:  0-50       (kick strength, default 6)
  decayPerSec:     0.1-50     (force decay rate, default 8)
  outlineThreshold: 0.1-4    (fallback beat threshold when LUMF absent)

Fluid Sim (5 tunables):
  viscosity:           0.00001-0.01
  diffusion:           0.000001-0.001
  dt:                  0.001-0.05
  flowInjectionScale:  0-5
  audioTurbulenceScale: 0-3

Motion Gate (4 tunables):
  minScale:    0-0.5          (minimum time scale during stillness)
  maxScale:    0.5-1.0        (maximum time scale during motion)
  powerCurve:  0.5-4          (remapping curve exponent)
  smoothing:   0.01-0.5       (EMA coefficient for time-scale changes)

Bar Display (1 tunable):
  barVerticalFov:  10-60      (camera vertical FOV, default 25)

---

Part 10: Mac4 State and Sync Procedure

### Current Mac4 Reality (verified 2026-05-02 via SSH)
- Unity 6000.0.72f1 is RUNNING (PID 20180)
- But `Desktop/lume-commerce/` is EMPTY -- no scripts, no git, no assets
- `[home-path]` has a partial sync from Mac1 (never fully applied)
- Zero .cs files anywhere on Mac4's lume-commerce directory
- One tmux session running (`claude`)
- Mac4 reachable from Mac1 via Tailscale at ~7ms

Sync Procedure (run from Mac1)

bash
rsync -avz --progress \
  --exclude='.git' \
  --exclude='Library/' \
  --exclude='Temp/' \
  --exclude='Logs/' \
  --exclude='obj/' \
  --exclude='*.mp4' \
  Desktop/lume-commerce/ mac4:Desktop/lume-commerce/

After sync, on Mac4:
1. Unity will detect new files and reimport (~2-5 min)
2. Watch Console for compile errors
3. Expected: clean reload, 17 components available in AddComponent menu
4. Run: `Lume > Auto-Wire` to attach all components to LumeMain GameObject

### K11 State
K11 is currently UNREACHABLE (DNS resolution failure on Tailscale). K11 work is blocked until Tailscale connectivity is restored. K11 is second priority -- get Mac4 working first.

---

Part 11: Execution Order for Codex

### Step 1: Sync Mac1 to Mac4
Run the rsync command from Part 10 on Mac1. Wait for Unity reimport on Mac4.

### Step 2: Write Assets/Editor/LumePresetGenerator.cs
Menu item: `Lume > Generate Duncan Presets`
Programmatically creates ALL .asset files from Part 5 using `ScriptableObject.CreateInstance<T>()` + `AssetDatabase.CreateAsset()`.
Directory structure:

Assets/Resources/LumePresets/
  Vfx/           -- 13 VfxPreset assets
  FluidSim/      -- 6 FluidSimPreset assets
  Lighting/      -- 7 LightingPreset assets
  Environment/   -- 4 EnvironmentPreset assets
  Avatar/        -- (empty for now, avatar presets need humanoid model)
  MANIFEST.md    -- lists every asset with source reel + description

### Step 3: Run Generator in Unity
Menu: `Lume > Generate Duncan Presets`. Should produce 30+ assets.
Verify: `Resources.LoadAll<LumeVfxPreset>("LumePresets/Vfx").Length` should be >= 13.

### Step 4: Write LumeDuncanPicker.cs (IMGUI Picker)
New MonoBehaviour at `Assets/Scripts/LumeDuncanPicker.cs`.
F1 toggle, IMGUI panel, 5 tabs, preset list, auto-cycle, JSON persistence per Part 7 spec.

### Step 5: Wire into Auto-Wire
Add LumeDuncanPicker to `Assets/Editor/LumeWaveAutoWire.cs` (Wave 8 section).

### Step 6: Add FreezeVelocityField to LumeFluidSim
Add a `public bool freezeVelocityField` toggle. When true, skip compute shader dispatch but keep RTs alive. This enables the "Frozen Sim Buffers" mode from E560.

### Step 7: Verify on Mac4
- Start publishers: `pointcloud_pub.py --raw-depth --host [ip]` on :9700, `audio_pub.py --synthetic --host [ip]` on :9701
- Unity Play mode
- F12 shows calibration panel (26 tunables)
- F1 shows Duncan preset picker (5 tabs, 30+ presets)
- Switch VFX presets: point cloud color changes
- Switch Fluid presets: fluid behavior changes (verify via particle motion)
- Toggle freeze: fluid sim freezes but particles keep flowing
- Auto-cycle: presets rotate on BPM divisor

Step 8: Tests

bash
cd Desktop/lume-commerce/software/demo/
python3 -m pytest tests/ -q  # existing 33 fast tests still green

Unity edit-mode test at `Assets/Scripts/Editor/Tests/LumePresetTests.cs`:
- `Resources.LoadAll<LumeVfxPreset>("LumePresets/Vfx").Length >= 13`
- `Resources.LoadAll<LumeFluidSimPreset>("LumePresets/FluidSim").Length >= 6`
- Picker JSON round-trip: save state, reload, verify equality
- `LumeFluidSim.ApplyPreset()` accepts each generated FluidSim preset without exception

Step 9: Commit

feat(lume): duncan preset generator -- 13 VfxPreset + 6 FluidSim + 7 Lighting + 4 Environment assets
feat(lume): duncan runtime picker -- F1 IMGUI panel with 5 tabs, auto-cycle, JSON persistence
feat(lume): frozen velocity buffer toggle for FluidSim (E560 mode)

---

Part 12: Don't Touch List

File/SystemWhy
`software/demo/pointcloud_pub.py`synthpub LaunchAgent on Mac5 depends on its CLI. Adding flags is fine, renaming/removing is breaking
`Scripts/LumeUdpReceiver.cs`Wire format magic-byte dispatch is settled. 5 magic bytes pinned by golden tests
`Scripts/LumeAudioReactor.cs`Wave 1 fallback, parallel session contract. Use the public setters, do not modify internals
Wire format magic bytesLUME=0x4C554D45, LUMD=0x4C554D44, LUMF=0x4C554D46, LUMM=0x4C554D4D, LUMC=0x4C554D43. Pinned by `tests/test_wire_format_golden.py`
`Scripts/LumeFlowParticleOverlay.cs` + shaderTEMPORARY substitute for the unauthored .vfx graph. Must stay until real VFX Graph asset is authored and verified. Comment in file labels it temporary
Wave 1 cloud rendering pathMust stay bit-preserved as the demo rescue floor
`hardware/cad/`Different workstream (3D printing pipeline). Mac1 territory
Active tmux feeds on Mac4Don't kill `lume-real-d` or `lume-audio` while testing. Use Unity batchmode for compile checks

---

Part 13: Reference Material Locations

WhatPath
83-reel index table`Reference/Duncan/INDEX.md`
69 Gemini analyses`Reference/Duncan/analyses/E###-*.md`
Reel captions (text)`Reference/Duncan/reels/*.txt`
Reel metadata (JSON)`Reference/Duncan/reels/*.json`
Playbook v2 master (513 lines)`[home-path]`
Playbook chunk DV-DU (E485-E570)`[home-path]`
Playbook chunk DT-DS (E475-E521)`[home-path]`
Playbook chunk DR-DQ (E415-E474)`[home-path]`
Gap analysis (16-row table)`Desktop/lume-commerce/software/demo/DUNCAN-GAP-ANALYSIS.md`
Architecture doc`ARCHITECTURE.md` in Unity project root
VFX Graph node recipe`Assets/VFX/README-LumeFlowParticles.md`
Calibration panel (IMGUI pattern)`Assets/Scripts/LumeCalibrationPanel.cs`
Print validation guide`Desktop/lume-commerce/hardware/cad/PRINT-VALIDATION-GUIDE.md`

### Top 10 Analyses to Read First
1. `analyses/E598-*.md` -- Dissolving Clones (4,081 likes, highest engagement)
2. `analyses/E568-*.md` -- SuperHot Cubes / Frozen Sim Buffers 5
3. `analyses/E512-*.md` -- Audio coupling engineering rules (CRITICAL)
4. `analyses/E560-*.md` -- Frozen Sim Buffers 1 (accidental art)
5. `analyses/E549-*.md` -- Kelp / Seagrass Strips
6. `analyses/E535-*.md` -- Particle Motion Kick (impulse channel)
7. `analyses/E545-*.md` -- Pinscreen / Cube Grid
8. `analyses/E589-*.md` -- Dat Funk / Motion Sparks
9. `analyses/E593-*.md` -- Blobby Guys (metaball/marching cubes)
10. `analyses/E534-*.md` -- Fluid Presets Demo (verbatim preset names)

---

Part 14: Commit Convention

feat(lume): <what> -- <details>
fix(lume): <what>

Examples from git log:

feat(lume): fluid sim presets (Calm/Reactive/Storm) + ApplyPreset API
feat(lume): F12 panel gains FluidSim + MotionGate tunables + K11 CLAUDE.md
fix(lume): OSC packed vs per-bone address collision + 4 OSC decoder tests

---

Part 15: Performance Targets

PlatformTargetParticle Budget
Duncan's A6000 (reference ceiling)60fps2x150K = 300K mesh particles
Duncan's 3070 laptop (dev)30-60fps300K total
Mac4 M4 16GB (our dev)30-60fps100-200K particles
K11 Radeon 780M ~9 TFLOPS (product)60fps80-150K particles (estimate, untested)
EmissiveOnly lighting mode160-170fpsany count (no shadow compute)
SpotShadows lighting mode70-110fpsreduced (7x shadow cast cost)

---

This is the complete handoff. The file is also saved at `Desktop/lume-commerce/CODEX-DUNCAN-HANDOFF.md`.

Promotion Decision

Attach run IDs, datasets, metrics, and reproduction commands.

Source Anchor

lume-commerce/CODEX-DUNCAN-HANDOFF.md

Detected Structure

Method · Evaluation · Code Anchors · Architecture