@backbay/glia-three 0.2.0-alpha.2

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 (172) hide show
  1. package/package.json +43 -0
  2. package/src/environment/AuroraLayer/AuroraLayer.stories.tsx +43 -0
  3. package/src/environment/AuroraLayer/AuroraLayer.tsx +200 -0
  4. package/src/environment/AuroraLayer/index.ts +2 -0
  5. package/src/environment/AuroraLayer/types.ts +30 -0
  6. package/src/environment/EnvironmentLayer/EnvironmentLayer.stories.tsx +262 -0
  7. package/src/environment/EnvironmentLayer/EnvironmentLayer.tsx +105 -0
  8. package/src/environment/EnvironmentLayer/index.ts +4 -0
  9. package/src/environment/EnvironmentLayer/presets.ts +128 -0
  10. package/src/environment/FogLayer/FogLayer.stories.tsx +83 -0
  11. package/src/environment/FogLayer/FogLayer.tsx +113 -0
  12. package/src/environment/FogLayer/index.ts +2 -0
  13. package/src/environment/FogLayer/types.ts +45 -0
  14. package/src/environment/VolumetricLight/VolumetricLight.stories.tsx +55 -0
  15. package/src/environment/VolumetricLight/VolumetricLight.tsx +188 -0
  16. package/src/environment/VolumetricLight/index.ts +2 -0
  17. package/src/environment/VolumetricLight/types.ts +33 -0
  18. package/src/environment/WeatherLayer/WeatherLayer.stories.tsx +348 -0
  19. package/src/environment/WeatherLayer/WeatherLayer.tsx +266 -0
  20. package/src/environment/WeatherLayer/cinematicCanvas.tsx +809 -0
  21. package/src/environment/WeatherLayer/colors.ts +27 -0
  22. package/src/environment/WeatherLayer/index.ts +4 -0
  23. package/src/environment/WeatherLayer/leafPresets.ts +12 -0
  24. package/src/environment/WeatherLayer/particles.ts +227 -0
  25. package/src/environment/WeatherLayer/types.ts +140 -0
  26. package/src/environment/index.ts +17 -0
  27. package/src/environment/shared/index.ts +2 -0
  28. package/src/environment/shared/noise.ts +10 -0
  29. package/src/environment/shared/performance.ts +33 -0
  30. package/src/environment/shared/types.ts +26 -0
  31. package/src/index.ts +2 -0
  32. package/src/lib/utils.ts +6 -0
  33. package/src/lib/vision-types.ts +84 -0
  34. package/src/three/AgentConsole/AgentConsole.stories.tsx +397 -0
  35. package/src/three/AgentConsole/AgentConsole.tsx +195 -0
  36. package/src/three/AgentConsole/ConsoleChat.tsx +237 -0
  37. package/src/three/AgentConsole/FocusConstellation.tsx +297 -0
  38. package/src/three/AgentConsole/GlyphAvatar.stories.tsx +110 -0
  39. package/src/three/AgentConsole/GlyphAvatar.tsx +117 -0
  40. package/src/three/AgentConsole/QuickActions.tsx +111 -0
  41. package/src/three/AgentConsole/index.ts +41 -0
  42. package/src/three/AgentConsole/types.ts +241 -0
  43. package/src/three/AmbientField/AmbientField.stories.tsx +290 -0
  44. package/src/three/AmbientField/AmbientField.tsx +307 -0
  45. package/src/three/AmbientField/BackbayFieldBus.ts +326 -0
  46. package/src/three/AmbientField/FieldProvider.tsx +83 -0
  47. package/src/three/AmbientField/index.ts +37 -0
  48. package/src/three/AmbientField/shaders/constellation.ts +384 -0
  49. package/src/three/AmbientField/types.ts +174 -0
  50. package/src/three/AttackGraph/AttackGraph.stories.tsx +144 -0
  51. package/src/three/AttackGraph/AttackGraph.tsx +325 -0
  52. package/src/three/AttackGraph/index.ts +19 -0
  53. package/src/three/AttackGraph/types.ts +97 -0
  54. package/src/three/AuditTrail/AuditTrail.stories.tsx +567 -0
  55. package/src/three/AuditTrail/AuditTrail.tsx +644 -0
  56. package/src/three/AuditTrail/index.ts +33 -0
  57. package/src/three/AuditTrail/types.ts +192 -0
  58. package/src/three/CrystallineOrganism/Breadcrumb.tsx +61 -0
  59. package/src/three/CrystallineOrganism/CrystallineOrganism.stories.tsx +509 -0
  60. package/src/three/CrystallineOrganism/CrystallineOrganism.tsx +273 -0
  61. package/src/three/CrystallineOrganism/LatticeEdge.tsx +69 -0
  62. package/src/three/CrystallineOrganism/OrganismLattice.tsx +159 -0
  63. package/src/three/CrystallineOrganism/OrganismParticles.tsx +148 -0
  64. package/src/three/CrystallineOrganism/OrganismShell.tsx +277 -0
  65. package/src/three/CrystallineOrganism/constants.ts +161 -0
  66. package/src/three/CrystallineOrganism/index.ts +17 -0
  67. package/src/three/CrystallineOrganism/layouts/hexGrid.ts +85 -0
  68. package/src/three/CrystallineOrganism/layouts/index.ts +1 -0
  69. package/src/three/CrystallineOrganism/types.ts +167 -0
  70. package/src/three/CrystallineOrganism/useOrganismEmotion.ts +84 -0
  71. package/src/three/FirewallBarrier/FirewallBarrier.stories.tsx +167 -0
  72. package/src/three/FirewallBarrier/FirewallBarrier.tsx +259 -0
  73. package/src/three/FirewallBarrier/index.ts +14 -0
  74. package/src/three/FirewallBarrier/types.ts +52 -0
  75. package/src/three/Glyph/GlyphObject.stories.tsx +577 -0
  76. package/src/three/Glyph/GlyphObject.tsx +422 -0
  77. package/src/three/Glyph/index.ts +10 -0
  78. package/src/three/Glyph/types.ts +36 -0
  79. package/src/three/Glyph/useGlyphController.ts +231 -0
  80. package/src/three/Glyph/useGlyphEmotion.ts +70 -0
  81. package/src/three/Graph3D/Graph3D.stories.tsx +269 -0
  82. package/src/three/Graph3D/Graph3D.tsx +248 -0
  83. package/src/three/Graph3D/GraphEdge.tsx +79 -0
  84. package/src/three/Graph3D/GraphNode.tsx +239 -0
  85. package/src/three/Graph3D/types.ts +66 -0
  86. package/src/three/Graph3D/utils.ts +204 -0
  87. package/src/three/IntelFeed/IntelFeed.stories.tsx +168 -0
  88. package/src/three/IntelFeed/IntelFeed.tsx +284 -0
  89. package/src/three/IntelFeed/index.ts +14 -0
  90. package/src/three/IntelFeed/types.ts +56 -0
  91. package/src/three/MetricsGalaxy/MetricsGalaxy.tsx +484 -0
  92. package/src/three/MetricsGalaxy/index.ts +6 -0
  93. package/src/three/MetricsGalaxy/types.ts +26 -0
  94. package/src/three/NetworkTopology/NetworkTopology.stories.tsx +184 -0
  95. package/src/three/NetworkTopology/NetworkTopology.tsx +421 -0
  96. package/src/three/NetworkTopology/index.ts +34 -0
  97. package/src/three/NetworkTopology/types.ts +128 -0
  98. package/src/three/ParticleField/ParticleField.stories.tsx +162 -0
  99. package/src/three/ParticleField/ParticleField.tsx +81 -0
  100. package/src/three/ParticleField/index.ts +1 -0
  101. package/src/three/PermissionMatrix/PermissionMatrix.stories.tsx +475 -0
  102. package/src/three/PermissionMatrix/PermissionMatrix.tsx +380 -0
  103. package/src/three/PermissionMatrix/index.ts +15 -0
  104. package/src/three/PermissionMatrix/types.ts +54 -0
  105. package/src/three/QuantumField/ConstellationField.tsx +238 -0
  106. package/src/three/QuantumField/FieldBus.ts +349 -0
  107. package/src/three/QuantumField/FieldLayer.tsx +430 -0
  108. package/src/three/QuantumField/FieldProvider.tsx +460 -0
  109. package/src/three/QuantumField/PcbField.tsx +406 -0
  110. package/src/three/QuantumField/QuantumField.stories.tsx +1155 -0
  111. package/src/three/QuantumField/TrailRTT.ts +212 -0
  112. package/src/three/QuantumField/WaterField.tsx +226 -0
  113. package/src/three/QuantumField/WaterSimRTT.ts +283 -0
  114. package/src/three/QuantumField/domMapping.ts +185 -0
  115. package/src/three/QuantumField/index.ts +110 -0
  116. package/src/three/QuantumField/styles/index.ts +9 -0
  117. package/src/three/QuantumField/styles/styleA.ts +526 -0
  118. package/src/three/QuantumField/styles/styleB.ts +1210 -0
  119. package/src/three/QuantumField/styles/styleC.ts +266 -0
  120. package/src/three/QuantumField/themes.ts +211 -0
  121. package/src/three/QuantumField/types.ts +380 -0
  122. package/src/three/SOCCommandCenter/SOCCommandCenter.stories.tsx +591 -0
  123. package/src/three/SOCCommandCenter/SOCCommandCenter.tsx +248 -0
  124. package/src/three/SOCCommandCenter/index.ts +26 -0
  125. package/src/three/SOCCommandCenter/types.ts +201 -0
  126. package/src/three/SecurityDashboard/SecurityDashboard.stories.tsx +508 -0
  127. package/src/three/SecurityDashboard/SecurityDashboard.tsx +507 -0
  128. package/src/three/SecurityDashboard/index.ts +37 -0
  129. package/src/three/SecurityDashboard/types.ts +143 -0
  130. package/src/three/SecurityShield/SecurityShield.stories.tsx +257 -0
  131. package/src/three/SecurityShield/SecurityShield.tsx +502 -0
  132. package/src/three/SecurityShield/index.ts +25 -0
  133. package/src/three/SecurityShield/types.ts +64 -0
  134. package/src/three/Sentinel/AvatarMode.tsx +578 -0
  135. package/src/three/Sentinel/AvatarRenderer.tsx +199 -0
  136. package/src/three/Sentinel/CameraPip.tsx +127 -0
  137. package/src/three/Sentinel/CardinalItem.tsx +83 -0
  138. package/src/three/Sentinel/CardinalMenu.tsx +370 -0
  139. package/src/three/Sentinel/DockedMiniOrb.tsx +146 -0
  140. package/src/three/Sentinel/RadialSubmenu.tsx +273 -0
  141. package/src/three/Sentinel/SentinelConversation.tsx +802 -0
  142. package/src/three/Sentinel/SentinelOrb.tsx +316 -0
  143. package/src/three/Sentinel/SentinelOverlay.tsx +146 -0
  144. package/src/three/Sentinel/SentinelProvider.tsx +145 -0
  145. package/src/three/Sentinel/SentinelTether.tsx +182 -0
  146. package/src/three/Sentinel/SigilPlaceholder.tsx +176 -0
  147. package/src/three/Sentinel/VerticalSubmenu.tsx +150 -0
  148. package/src/three/Sentinel/index.ts +145 -0
  149. package/src/three/Sentinel/sentinelStore.ts +196 -0
  150. package/src/three/Sentinel/types.ts +403 -0
  151. package/src/three/Sentinel/useCameraPermission.ts +153 -0
  152. package/src/three/Sentinel/useThrowPhysics.ts +220 -0
  153. package/src/three/SpatialWorkspace/CyntraWorkspace.tsx +84 -0
  154. package/src/three/SpatialWorkspace/JobCluster.tsx +281 -0
  155. package/src/three/SpatialWorkspace/NodeGraph.tsx +236 -0
  156. package/src/three/SpatialWorkspace/ReceiptOrbit.tsx +368 -0
  157. package/src/three/SpatialWorkspace/SpatialWorkspace.stories.tsx +547 -0
  158. package/src/three/SpatialWorkspace/SpatialWorkspace.tsx +428 -0
  159. package/src/three/SpatialWorkspace/TrustRings.tsx +228 -0
  160. package/src/three/SpatialWorkspace/adapters.ts +353 -0
  161. package/src/three/SpatialWorkspace/index.ts +85 -0
  162. package/src/three/SpatialWorkspace/nexusAdapter.ts +182 -0
  163. package/src/three/SpatialWorkspace/types.ts +389 -0
  164. package/src/three/ThreatRadar/ThreatRadar.stories.tsx +451 -0
  165. package/src/three/ThreatRadar/ThreatRadar.tsx +542 -0
  166. package/src/three/ThreatRadar/index.ts +8 -0
  167. package/src/three/ThreatRadar/types.ts +90 -0
  168. package/src/three/ThreeErrorBoundary/ThreeErrorBoundary.tsx +235 -0
  169. package/src/three/ThreeErrorBoundary/index.ts +5 -0
  170. package/src/three/index.ts +56 -0
  171. package/tsconfig.json +20 -0
  172. package/tsup.config.ts +21 -0
@@ -0,0 +1,1210 @@
1
+ /**
2
+ * Style B - "PCB Vector Field (Sci-Fi/Engineering)"
3
+ *
4
+ * A technical substrate: circuit-board routing traces and vector-field arrows that "route"
5
+ * energy around UI surfaces. Hover creates a "routing negotiation" (signals bend into nearest tracks);
6
+ * etch draws crisp conductor paths; click bursts like an oscilloscope impulse;
7
+ * latch becomes a pin/via node with persistent trace to origin.
8
+ */
9
+
10
+ import * as THREE from "three";
11
+
12
+ // -----------------------------------------------------------------------------
13
+ // Shader Code
14
+ // -----------------------------------------------------------------------------
15
+
16
+ export const PCB_VERTEX_SHADER = /* glsl */ `
17
+ varying vec2 vUv;
18
+ varying vec3 vPosition;
19
+
20
+ void main() {
21
+ vUv = uv;
22
+ vPosition = position;
23
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
24
+ }
25
+ `;
26
+
27
+ export const PCB_FRAGMENT_SHADER = /* glsl */ `
28
+ uniform float uTime;
29
+ uniform vec2 uResolution;
30
+ uniform vec4 uHover; // xy = uv, z = intent (0=probe, 1=etch), w = active
31
+ uniform vec4 uImpulses[24]; // xy = uv, z = radius, w = amp
32
+ uniform float uImpulsesAge[24];
33
+ uniform int uImpulseCount;
34
+ uniform vec4 uAnchors[8]; // xy = uv, z = strength, w = phase
35
+ uniform int uAnchorCount;
36
+ uniform sampler2D uTrailTex;
37
+ uniform float uTrailEnabled;
38
+ uniform float uProbeRadius;
39
+
40
+ // === Ultra-Fine Lattice Uniforms ===
41
+ uniform float uMicroGrid1; // Fine grid frequency (default ~60)
42
+ uniform float uMicroGrid2; // Ultra-fine grid frequency (default ~200)
43
+ uniform float uMicroGridStrength; // Overall micro-lattice visibility (0-1)
44
+ uniform float uRevealStrength; // How much hover reveals the lattice (0-1)
45
+ uniform float uBaseVisibility; // Base visibility without hover (0-1, default 0.05)
46
+ uniform float uMicroWarp; // Domain warp amount (subtle, ~0.01-0.05)
47
+ uniform float uEtchDistortion; // UV distortion in etched areas (0-0.02)
48
+
49
+ // === Phase Lens Uniforms ===
50
+ uniform vec4 uLens; // xy = center uv, z = radius, w = magnification
51
+ uniform float uLensVelocity; // 0..1 normalized cursor velocity
52
+ uniform float uLensChromatic; // chromatic aberration intensity (0-1)
53
+ uniform float uLensEnabled; // 0 or 1
54
+
55
+ // === Lattice Mode Uniform ===
56
+ uniform int uLatticeMode; // 0=rect, 1=hex, 2=tri
57
+
58
+ // === Etch Velocity Uniform ===
59
+ uniform float uEtchVelocity; // 0..1 for visual feedback in etch
60
+
61
+ // === Palette / Atmosphere Uniforms ===
62
+ uniform int uPaletteMode; // 0=glia-cyan, 1=orchid, 2=amber, 3=mono, 4=ice
63
+ uniform float uAccentIntensity; // 0-2, default 1.0
64
+ uniform float uIridescenceStrength; // 0-1
65
+ uniform float uIridescenceScale; // 1-40
66
+ uniform float uExposure; // 0.6-2.0
67
+ uniform float uFilmic; // 0-1
68
+ uniform float uGrainStrength; // 0-0.08
69
+ uniform float uCrtStrength; // 0-1
70
+ uniform float uCopperStrength; // 0-1
71
+
72
+ varying vec2 vUv;
73
+ varying vec3 vPosition;
74
+
75
+ const float PI = 3.14159265359;
76
+
77
+ // ============================================================================
78
+ // Noise Functions
79
+ // ============================================================================
80
+
81
+ float hash(vec2 p) {
82
+ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
83
+ }
84
+
85
+ vec2 hash2(vec2 p) {
86
+ p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
87
+ return fract(sin(p) * 43758.5453);
88
+ }
89
+
90
+ float noise(vec2 p) {
91
+ vec2 i = floor(p);
92
+ vec2 f = fract(p);
93
+ f = f * f * (3.0 - 2.0 * f);
94
+
95
+ float a = hash(i);
96
+ float b = hash(i + vec2(1.0, 0.0));
97
+ float c = hash(i + vec2(0.0, 1.0));
98
+ float d = hash(i + vec2(1.0, 1.0));
99
+
100
+ return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
101
+ }
102
+
103
+ // Curl noise for vector field
104
+ vec2 curlNoise(vec2 p) {
105
+ float eps = 0.01;
106
+ float n1 = noise(p + vec2(eps, 0.0));
107
+ float n2 = noise(p - vec2(eps, 0.0));
108
+ float n3 = noise(p + vec2(0.0, eps));
109
+ float n4 = noise(p - vec2(0.0, eps));
110
+
111
+ return vec2(n3 - n4, n1 - n2) / (2.0 * eps);
112
+ }
113
+
114
+ // ============================================================================
115
+ // Ultra-Fine Micro-Lattice Functions
116
+ // ============================================================================
117
+
118
+ // Anti-aliased grid line using fwidth for stable rendering at any zoom
119
+ // Returns 0-1 where 1 = on the grid line
120
+ float gridAA(vec2 uv, float freq, float thickness) {
121
+ vec2 gridUv = fract(uv * freq);
122
+ vec2 gridDeriv = fwidth(uv * freq);
123
+
124
+ // Distance to nearest grid line (horizontal + vertical)
125
+ vec2 lineDistH = abs(gridUv - 0.5);
126
+ vec2 lineDistV = abs(gridUv);
127
+
128
+ // Use smoothstep with fwidth for anti-aliasing
129
+ float lineH = 1.0 - smoothstep(thickness - gridDeriv.y, thickness + gridDeriv.y, lineDistH.y);
130
+ float lineV = 1.0 - smoothstep(thickness - gridDeriv.x, thickness + gridDeriv.x, lineDistH.x);
131
+
132
+ // Also check lines at cell edges (0/1)
133
+ float edgeH = 1.0 - smoothstep(thickness - gridDeriv.y, thickness + gridDeriv.y, min(gridUv.y, 1.0 - gridUv.y));
134
+ float edgeV = 1.0 - smoothstep(thickness - gridDeriv.x, thickness + gridDeriv.x, min(gridUv.x, 1.0 - gridUv.x));
135
+
136
+ return max(max(lineH, lineV), max(edgeH, edgeV));
137
+ }
138
+
139
+ // Rotated grid lines for hex/tri lattice - returns AA line intensity
140
+ float gridRotatedAA(vec2 uv, float freq, float thickness, float angleDeg) {
141
+ float rad = angleDeg * PI / 180.0;
142
+ float c = cos(rad);
143
+ float s = sin(rad);
144
+ mat2 rot = mat2(c, -s, s, c);
145
+ vec2 rotUv = rot * uv;
146
+
147
+ float lineCoord = rotUv.x * freq;
148
+ float lineDeriv = fwidth(lineCoord);
149
+ float lineDist = abs(fract(lineCoord) - 0.5);
150
+
151
+ return 1.0 - smoothstep(thickness - lineDeriv, thickness + lineDeriv, lineDist);
152
+ }
153
+
154
+ // Triangular lattice: 3 sets of parallel lines at 0°, 60°, 120°
155
+ // Creates a hexagonal cell pattern with triangular subdivisions
156
+ float triLatticeAA(vec2 uv, float freq, float thickness) {
157
+ float line0 = gridRotatedAA(uv, freq, thickness, 0.0);
158
+ float line60 = gridRotatedAA(uv, freq, thickness, 60.0);
159
+ float line120 = gridRotatedAA(uv, freq, thickness, 120.0);
160
+
161
+ return max(line0, max(line60, line120));
162
+ }
163
+
164
+ // Hexagonal lattice with node emphasis at hex centers
165
+ float hexLatticeAA(vec2 uv, float freq, float thickness) {
166
+ // Base tri lattice already gives you the hex look.
167
+ float tri = triLatticeAA(uv, freq, thickness);
168
+ return tri; // <-- removes center “bulbs”
169
+ }
170
+
171
+
172
+ // Unified lattice function that selects mode
173
+ // mode: 0=rect, 1=hex, 2=tri
174
+ float latticeLines(vec2 uv, float freq, float thickness, int mode) {
175
+ if (mode == 1) {
176
+ return hexLatticeAA(uv, freq, thickness);
177
+ } else if (mode == 2) {
178
+ return triLatticeAA(uv, freq, thickness);
179
+ }
180
+ // Default: rect
181
+ return gridAA(uv, freq, thickness);
182
+ }
183
+
184
+ // ============================================================================
185
+ // Phase Lens Functions (WebGL1 compatible - no struct returns)
186
+ // ============================================================================
187
+
188
+ // Compute lens mask (0=outside, 1=center) for given UV
189
+ float computeLensMask(vec2 uv, vec2 lensCenter, float lensRadius) {
190
+ float lensDist = length(uv - lensCenter);
191
+ return smoothstep(lensRadius, lensRadius * 0.4, lensDist);
192
+ }
193
+
194
+ // Compute lens rim highlight (chromatic fringe zone)
195
+ float computeLensRim(vec2 uv, vec2 lensCenter, float lensRadius, float velocity) {
196
+ float lensDist = length(uv - lensCenter);
197
+ float rimInner = smoothstep(lensRadius * 0.75, lensRadius * 0.88, lensDist);
198
+ float rimOuter = smoothstep(lensRadius * 1.05, lensRadius * 0.92, lensDist);
199
+ float rim = rimInner * rimOuter;
200
+ return rim * (1.0 + velocity * 0.5);
201
+ }
202
+
203
+ // Compute lens-distorted UV for magnification effect
204
+ vec2 computeLensDistortedUv(vec2 uv, vec2 lensCenter, float lensRadius, float magnification) {
205
+ vec2 toLens = uv - lensCenter;
206
+ float lensDist = length(toLens);
207
+ float mask = smoothstep(lensRadius, lensRadius * 0.4, lensDist);
208
+
209
+ // Magnification distortion: pull UVs toward center inside lens
210
+ float distortionStrength = mask * (magnification - 1.0) * 0.25;
211
+ vec2 distortDir = normalize(toLens + 0.0001);
212
+ return uv - distortDir * distortionStrength * lensDist;
213
+ }
214
+
215
+ // Domain warping for organic feel - returns warped UV
216
+ vec2 warpUV(vec2 uv, float amount, float time) {
217
+ // Use curl noise for divergence-free warping (feels more natural)
218
+ vec2 warp = curlNoise(uv * 5.0 + time * 0.05);
219
+ return uv + warp * amount;
220
+ }
221
+
222
+ // Compute reveal mask - how much the lattice should be visible
223
+ // Based on distance to hover point, anchors, and etch trails
224
+ float computeReveal(vec2 uv, vec2 hoverUv, float hoverActive, float probeRadius, float revealStr) {
225
+ float reveal = 0.0;
226
+
227
+ // Hover probe reveals the lattice
228
+ if (hoverActive > 0.5) {
229
+ float hoverDist = length(uv - hoverUv);
230
+ // Wider, softer reveal zone
231
+ reveal = smoothstep(probeRadius * 3.0, probeRadius * 0.3, hoverDist) * revealStr;
232
+ }
233
+
234
+ return reveal;
235
+ }
236
+
237
+ // ============================================================================
238
+ // Palette System (Cinematic Color Scarcity)
239
+ // ============================================================================
240
+
241
+ // Get base/background color for palette mode
242
+ vec3 paletteBase(int mode) {
243
+ // All modes start near-black for scarcity
244
+ if (mode == 0) return vec3(0.003, 0.012, 0.010); // glia-cyan: deep teal-black
245
+ if (mode == 1) return vec3(0.008, 0.005, 0.015); // orchid: deep purple-black
246
+ if (mode == 2) return vec3(0.012, 0.008, 0.003); // amber: warm black
247
+ if (mode == 3) return vec3(0.008, 0.012, 0.008); // mono: green-black
248
+ if (mode == 4) return vec3(0.005, 0.010, 0.018); // ice: cold blue-black
249
+ // Techno-Gothic themes
250
+ if (mode == 5) return vec3(0.012, 0.005, 0.020); // gothic-cathedral: deep ecclesiastical purple
251
+ if (mode == 6) return vec3(0.008, 0.002, 0.002); // gothic-void: abyssal black with blood tint
252
+ if (mode == 7) return vec3(0.008, 0.010, 0.008); // gothic-sanctum: oxidized bronze black
253
+ if (mode == 8) return vec3(0.015, 0.003, 0.008); // gothic-rose: deep crimson black
254
+ return vec3(0.005, 0.010, 0.008);
255
+ }
256
+
257
+ // Get lattice color for palette mode (revealed state)
258
+ vec3 paletteLattice(int mode, float reveal) {
259
+ vec3 dim, bright;
260
+
261
+ if (mode == 0) { // glia-cyan - boosted for visibility
262
+ dim = vec3(0.03, 0.10, 0.08);
263
+ bright = vec3(0.08, 0.50, 0.42);
264
+ } else if (mode == 1) { // orchid
265
+ dim = vec3(0.04, 0.02, 0.06);
266
+ bright = vec3(0.25, 0.12, 0.45);
267
+ } else if (mode == 2) { // amber
268
+ dim = vec3(0.06, 0.04, 0.02);
269
+ bright = vec3(0.45, 0.30, 0.08);
270
+ } else if (mode == 3) { // mono
271
+ dim = vec3(0.02, 0.05, 0.02);
272
+ bright = vec3(0.08, 0.35, 0.12);
273
+ } else if (mode == 4) { // ice
274
+ dim = vec3(0.03, 0.05, 0.08);
275
+ bright = vec3(0.15, 0.35, 0.55);
276
+ } else if (mode == 5) { // gothic-cathedral: stained glass purple/gold
277
+ dim = vec3(0.06, 0.02, 0.10);
278
+ bright = vec3(0.35, 0.15, 0.55);
279
+ } else if (mode == 6) { // gothic-void: blood red tracery
280
+ dim = vec3(0.08, 0.01, 0.01);
281
+ bright = vec3(0.45, 0.05, 0.08);
282
+ } else if (mode == 7) { // gothic-sanctum: verdigris bronze
283
+ dim = vec3(0.04, 0.06, 0.04);
284
+ bright = vec3(0.15, 0.35, 0.20);
285
+ } else if (mode == 8) { // gothic-rose: crimson with gold
286
+ dim = vec3(0.10, 0.02, 0.04);
287
+ bright = vec3(0.50, 0.10, 0.15);
288
+ } else {
289
+ dim = vec3(0.03, 0.05, 0.08);
290
+ bright = vec3(0.15, 0.35, 0.55);
291
+ }
292
+
293
+ return mix(dim, bright, reveal);
294
+ }
295
+
296
+ // Get accent color for palette mode (etch, impulses, rim)
297
+ vec3 paletteAccent(int mode) {
298
+ if (mode == 0) return vec3(0.15, 1.0, 0.7); // glia-cyan: neon mint
299
+ if (mode == 1) return vec3(0.4, 0.9, 1.0); // orchid: cyan fringe
300
+ if (mode == 2) return vec3(0.2, 0.8, 0.7); // amber: teal edge
301
+ if (mode == 3) return vec3(0.3, 1.0, 0.4); // mono: bright green
302
+ if (mode == 4) return vec3(0.6, 0.85, 1.0); // ice: pale blue-white
303
+ // Techno-Gothic accents
304
+ if (mode == 5) return vec3(1.0, 0.85, 0.3); // gothic-cathedral: sacred gold
305
+ if (mode == 6) return vec3(1.0, 0.15, 0.2); // gothic-void: arterial red
306
+ if (mode == 7) return vec3(0.4, 0.9, 0.6); // gothic-sanctum: patina green
307
+ if (mode == 8) return vec3(1.0, 0.7, 0.2); // gothic-rose: gilded gold
308
+ return vec3(0.2, 1.0, 0.6);
309
+ }
310
+
311
+ // Get copper/trace color for palette mode (subdued)
312
+ vec3 paletteCopper(int mode) {
313
+ if (mode == 0) return vec3(0.06, 0.15, 0.12); // glia-cyan: dark teal
314
+ if (mode == 1) return vec3(0.12, 0.06, 0.18); // orchid: dark purple
315
+ if (mode == 2) return vec3(0.18, 0.12, 0.04); // amber: dark gold
316
+ if (mode == 3) return vec3(0.06, 0.12, 0.06); // mono: dark green
317
+ if (mode == 4) return vec3(0.08, 0.12, 0.18); // ice: dark blue
318
+ // Techno-Gothic copper/trace colors
319
+ if (mode == 5) return vec3(0.15, 0.08, 0.22); // gothic-cathedral: ecclesiastical purple
320
+ if (mode == 6) return vec3(0.18, 0.04, 0.06); // gothic-void: dried blood
321
+ if (mode == 7) return vec3(0.10, 0.14, 0.08); // gothic-sanctum: oxidized bronze
322
+ if (mode == 8) return vec3(0.20, 0.08, 0.10); // gothic-rose: aged crimson
323
+ return vec3(0.08, 0.15, 0.10);
324
+ }
325
+
326
+ // ============================================================================
327
+ // Iridescent Interference (Thin-Film Spectral Shimmer)
328
+ // ============================================================================
329
+
330
+ // Spectral color from phase (cheap cosine-based rainbow)
331
+ vec3 spectral(float t) {
332
+ // Phase-offset cosines for RGB
333
+ vec3 a = vec3(0.5, 0.5, 0.5);
334
+ vec3 b = vec3(0.5, 0.5, 0.5);
335
+ vec3 c = vec3(1.0, 1.0, 1.0);
336
+ vec3 d = vec3(0.0, 0.33, 0.67); // Phase offsets for rainbow
337
+ return a + b * cos(6.28318 * (c * t + d));
338
+ }
339
+
340
+ // Compute iridescence for a surface point
341
+ vec3 iridescence(vec2 uv, float time, float scale, float strength) {
342
+ // Phase based on UV position + time + noise for organic feel
343
+ float phase = dot(uv, vec2(0.7, 0.3)) * scale + time * 0.06;
344
+ phase += noise(uv * 12.0) * 0.3;
345
+
346
+ vec3 spec = spectral(phase);
347
+ // Return as deviation from neutral (centered around 0.5)
348
+ return (spec - 0.5) * strength;
349
+ }
350
+
351
+ // ============================================================================
352
+ // Atmosphere (Filmic Tonemap + Grain + CRT)
353
+ // ============================================================================
354
+
355
+ // ACES-ish filmic tonemap approximation
356
+ vec3 filmicTonemap(vec3 x) {
357
+ float a = 2.51;
358
+ float b = 0.03;
359
+ float c = 2.43;
360
+ float d = 0.59;
361
+ float e = 0.14;
362
+ return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);
363
+ }
364
+
365
+ // Film grain (stable, not flickery)
366
+ float filmGrain(vec2 uv, vec2 resolution, float time) {
367
+ vec2 px = uv * resolution;
368
+ // Use floor to get per-pixel noise, add time for subtle animation
369
+ float grain = hash(floor(px * 0.5) + floor(time * 30.0));
370
+ return grain - 0.5; // Center around 0
371
+ }
372
+
373
+ // CRT scanline + aperture grille effect
374
+ float crtEffect(vec2 uv, float time, float strength) {
375
+ // Scanlines (horizontal)
376
+ float scanline = sin(uv.y * 900.0 + time * 1.5) * 0.5 + 0.5;
377
+ scanline = pow(scanline, 1.5) * 0.15;
378
+
379
+ // Aperture grille (vertical RGB stripes, very subtle)
380
+ float grille = sin(uv.x * 1400.0) * 0.5 + 0.5;
381
+ grille = pow(grille, 2.0) * 0.08;
382
+
383
+ // Combine with vignette falloff (stronger at edges)
384
+ vec2 vigUv = uv * 2.0 - 1.0;
385
+ float vigDist = length(vigUv);
386
+ float edgeFalloff = smoothstep(0.3, 1.2, vigDist);
387
+
388
+ return (scanline + grille) * strength * (0.3 + edgeFalloff * 0.7);
389
+ }
390
+
391
+ // ============================================================================
392
+ // PCB Trace SDFs
393
+ // ============================================================================
394
+
395
+ // Manhattan distance grid
396
+ float manhattanGrid(vec2 uv, float spacing) {
397
+ vec2 grid = abs(fract(uv * spacing) - 0.5);
398
+ float manhattan = min(grid.x, grid.y);
399
+ return manhattan;
400
+ }
401
+
402
+ // Horizontal trace
403
+ float hTrace(vec2 uv, float y, float width) {
404
+ return abs(uv.y - y) - width * 0.5;
405
+ }
406
+
407
+ // Vertical trace
408
+ float vTrace(vec2 uv, float x, float width) {
409
+ return abs(uv.x - x) - width * 0.5;
410
+ }
411
+
412
+ // Via (circular pad)
413
+ float via(vec2 uv, vec2 center, float radius) {
414
+ return length(uv - center) - radius;
415
+ }
416
+
417
+ // Combined trace network (procedural)
418
+ float traceNetwork(vec2 uv, float scale) {
419
+ uv *= scale;
420
+
421
+ // Main grid lines
422
+ float gridSpacing = 0.2;
423
+ float traceWidth = 0.01;
424
+
425
+ vec2 gridUv = mod(uv, gridSpacing) - gridSpacing * 0.5;
426
+ float hLine = abs(gridUv.y) - traceWidth;
427
+ float vLine = abs(gridUv.x) - traceWidth;
428
+
429
+ // Create Manhattan-style traces with some randomization
430
+ float h1 = hTrace(uv, 0.1, traceWidth);
431
+ float h2 = hTrace(uv, 0.3, traceWidth * 0.8);
432
+ float h3 = hTrace(uv, 0.5, traceWidth);
433
+ float h4 = hTrace(uv, 0.7, traceWidth * 0.8);
434
+ float h5 = hTrace(uv, 0.9, traceWidth);
435
+
436
+ float v1 = vTrace(uv, 0.15, traceWidth);
437
+ float v2 = vTrace(uv, 0.35, traceWidth * 0.8);
438
+ float v3 = vTrace(uv, 0.55, traceWidth);
439
+ float v4 = vTrace(uv, 0.75, traceWidth * 0.8);
440
+ float v5 = vTrace(uv, 0.95, traceWidth);
441
+
442
+ float traces = min(min(min(min(h1, h2), min(h3, h4)), h5),
443
+ min(min(min(v1, v2), min(v3, v4)), v5));
444
+
445
+ return min(traces, min(hLine, vLine));
446
+ }
447
+
448
+ // ============================================================================
449
+ // Main Shader
450
+ // ============================================================================
451
+
452
+ void main() {
453
+ vec2 uv = vUv;
454
+
455
+ // Base: use palette-defined substrate color (near-black, cinematic)
456
+ vec3 baseColor = paletteBase(uPaletteMode);
457
+ vec3 color = baseColor;
458
+
459
+ // ========================================================================
460
+ // Sample Etch Trail Early (for UV distortion + reveal boost)
461
+ // ========================================================================
462
+
463
+ float trailIntensity = 0.0;
464
+ if (uTrailEnabled > 0.5) {
465
+ vec4 trailSample = texture2D(uTrailTex, uv);
466
+ trailIntensity = trailSample.r;
467
+ }
468
+
469
+ // ========================================================================
470
+ // UV Distortion in Etched Areas (makes it feel engraved)
471
+ // ========================================================================
472
+
473
+ vec2 distortedUv = uv;
474
+ if (trailIntensity > 0.01) {
475
+ // Add subtle UV distortion based on trail + noise
476
+ vec2 distortNoise = curlNoise(uv * 30.0 + uTime * 0.2);
477
+ distortedUv += distortNoise * trailIntensity * uEtchDistortion;
478
+ }
479
+
480
+ // ========================================================================
481
+ // Compute Reveal Mask (etch/anchor proximity increases lattice visibility)
482
+ // Only respond to ETCH (click+drag), not probe hover. uHover.z = 1 for etch.
483
+ // ========================================================================
484
+
485
+ // Only compute reveal from cursor if in etch mode (uHover.z > 0.5)
486
+ float isEtchMode = step(0.5, uHover.z);
487
+ float reveal = computeReveal(uv, uHover.xy, uHover.w * isEtchMode, uProbeRadius, uRevealStrength);
488
+
489
+ // Anchors also reveal the lattice
490
+ for (int i = 0; i < 8; i++) {
491
+ if (i >= uAnchorCount) break;
492
+ float anchorDist = length(uv - uAnchors[i].xy);
493
+ reveal = max(reveal, smoothstep(0.15, 0.02, anchorDist) * uAnchors[i].z * uRevealStrength);
494
+ }
495
+
496
+ // Etched areas (trails) are always revealed
497
+ reveal = max(reveal, trailIntensity * 1.5);
498
+ reveal = clamp(reveal, 0.0, 1.0);
499
+
500
+ // ========================================================================
501
+ // Phase Lens Setup (if enabled) - only active during ETCH, not hover
502
+ // ========================================================================
503
+
504
+ float lensMask = 0.0;
505
+ float lensRim = 0.0;
506
+ vec2 lensDistortedUv = distortedUv;
507
+
508
+ // Lens only activates in etch mode (click+drag), not probe hover
509
+ if (uLensEnabled > 0.5 && uHover.w > 0.5 && uHover.z > 0.5) {
510
+ vec2 lensCenter = uLens.xy;
511
+ float lensRadius = uLens.z;
512
+ float lensMag = uLens.w;
513
+
514
+ lensMask = computeLensMask(distortedUv, lensCenter, lensRadius);
515
+ lensRim = computeLensRim(distortedUv, lensCenter, lensRadius, uLensVelocity);
516
+ lensDistortedUv = computeLensDistortedUv(distortedUv, lensCenter, lensRadius, lensMag);
517
+ }
518
+
519
+ // ========================================================================
520
+ // Ultra-Fine Micro-Lattice Layer (THE STAR)
521
+ // ========================================================================
522
+
523
+ // Apply domain warping for organic, alive feel
524
+ vec2 warpedUv = warpUV(distortedUv, uMicroWarp, uTime);
525
+ vec2 warpedLensUv = warpUV(lensDistortedUv, uMicroWarp, uTime);
526
+
527
+ // Two-frequency micro-grid using selected lattice mode
528
+ float microGrid1 = latticeLines(warpedUv, uMicroGrid1, 0.02, uLatticeMode);
529
+ float microGrid2 = latticeLines(warpedUv, uMicroGrid2, 0.015, uLatticeMode);
530
+
531
+ // Lens-magnified versions (higher detail inside lens)
532
+ float microGrid1Lens = latticeLines(warpedLensUv, uMicroGrid1 * 1.3, 0.018, uLatticeMode);
533
+ float microGrid2Lens = latticeLines(warpedLensUv, uMicroGrid2 * 1.2, 0.012, uLatticeMode);
534
+
535
+ // Blend between normal and lens-magnified based on lens mask
536
+ float effectiveMicroGrid1 = mix(microGrid1, microGrid1Lens, lensMask);
537
+ float effectiveMicroGrid2 = mix(microGrid2, microGrid2Lens, lensMask);
538
+
539
+ // Combine grids with different weights
540
+ float microLattice = max(effectiveMicroGrid1 * 0.7, effectiveMicroGrid2 * 0.4);
541
+
542
+ // Lens boosts reveal effect (magnification = amplified visibility)
543
+ float lensRevealBoost = lensMask * 0.6;
544
+ float effectiveReveal = min(reveal + lensRevealBoost, 1.0);
545
+
546
+ // Base visibility can be set via uBaseVisibility; reveal increases it to full
547
+ // COLOR SCARCITY: Use baseVisibility for always-on backgrounds, low for reveal-based UX
548
+ float latticeVis = uMicroGridStrength * (uBaseVisibility + effectiveReveal * (1.0 - uBaseVisibility));
549
+
550
+ // Lattice color: use palette system
551
+ // IMPORTANT: Use max of baseVisibility and reveal for color lookup so lattice is visible
552
+ // without hover when baseVisibility is high (for always-on backgrounds)
553
+ float latticeColorMix = max(uBaseVisibility, effectiveReveal);
554
+ vec3 baseLatticeColor = paletteLattice(uPaletteMode, latticeColorMix);
555
+
556
+ // Inside lens: boost saturation + add iridescence
557
+ vec3 lensLatticeColor = baseLatticeColor * 1.3;
558
+ vec3 iridescentShift = iridescence(warpedLensUv, uTime, uIridescenceScale, uIridescenceStrength);
559
+ lensLatticeColor += iridescentShift * lensMask * 0.4;
560
+
561
+ vec3 latticeColor = mix(baseLatticeColor, lensLatticeColor, lensMask);
562
+
563
+ // Base energy from baseVisibility + boost from reveal/lens
564
+ float latticeEnergy = max(uBaseVisibility * 0.5, max(effectiveReveal, lensMask * 0.6));
565
+ // When baseVisibility is high, don't penalize - scale from 0.7 to 1.0
566
+ float baseMult = 0.7 + uBaseVisibility * 0.3;
567
+ color += latticeColor * microLattice * latticeVis * (baseMult + latticeEnergy * 0.3);
568
+
569
+ // Energy nodes at grid intersections - DISABLED for cleaner look
570
+ // (uncomment to re-enable pulsing intersection dots)
571
+
572
+ // ========================================================================
573
+ // Phase Lens Rim Effect (iridescent + chromatic fringe)
574
+ // ========================================================================
575
+
576
+ if (lensRim > 0.01) {
577
+ // Get accent color from palette
578
+ vec3 accentColor = paletteAccent(uPaletteMode);
579
+
580
+ // Chromatic aberration on rim
581
+ float rimOffset = lensRim * uLensChromatic;
582
+
583
+ // Iridescent shimmer on rim (the wow factor)
584
+ vec3 rimIridescence = iridescence(distortedUv, uTime * 1.5, uIridescenceScale * 2.0, 1.0);
585
+
586
+ // Base rim color from accent
587
+ vec3 rimColor = accentColor * 0.6;
588
+
589
+ // Add chromatic split
590
+ rimColor.r += rimOffset * 0.2;
591
+ rimColor.b -= rimOffset * 0.15;
592
+
593
+ // Add iridescence to rim
594
+ rimColor += rimIridescence * uIridescenceStrength * 0.5;
595
+
596
+ // Velocity adds warmth to rim
597
+ rimColor = mix(rimColor, accentColor * vec3(1.2, 0.8, 0.6), uLensVelocity * 0.3);
598
+
599
+ color += rimColor * lensRim * 0.3 * uAccentIntensity;
600
+ }
601
+
602
+ // ========================================================================
603
+ // PCB Traces Layer (SUBDUED - rails under the lattice, not the main show)
604
+ // ========================================================================
605
+
606
+ float traceScale = 5.0;
607
+ float traceSdf = traceNetwork(distortedUv, traceScale);
608
+ float traceEdge = smoothstep(0.002, -0.002, traceSdf);
609
+
610
+ // Copper color from palette (subdued to match theme)
611
+ vec3 copperColor = paletteCopper(uPaletteMode) * (0.8 + 0.2 * noise(uv * 20.0));
612
+
613
+ // Traces are very faint - controlled by copperStrength
614
+ // COLOR SCARCITY: Traces are just "rails", not the star
615
+ float traceVis = (0.05 + reveal * 0.3) * uCopperStrength;
616
+ color += copperColor * traceEdge * traceVis;
617
+
618
+ // ========================================================================
619
+ // Trail Layer (Etched Inscription - enhanced with char edge + velocity)
620
+ // ========================================================================
621
+
622
+ // Track etch mask for color scarcity
623
+ float etchMask = 0.0;
624
+
625
+ if (uTrailEnabled > 0.5 && trailIntensity > 0.01) {
626
+ // Sharpened edge for crisp inscription look
627
+ float sharpTrail = smoothstep(0.05, 0.3, trailIntensity);
628
+ etchMask = sharpTrail; // Store for accent calculation
629
+
630
+ // === Char Edge Detection (burned/charred rim around inscription) ===
631
+ vec2 texelSize = vec2(1.0 / 512.0);
632
+ float trailN = texture2D(uTrailTex, uv + vec2(0.0, texelSize.y)).r;
633
+ float trailS = texture2D(uTrailTex, uv - vec2(0.0, texelSize.y)).r;
634
+ float trailE = texture2D(uTrailTex, uv + vec2(texelSize.x, 0.0)).r;
635
+ float trailW = texture2D(uTrailTex, uv - vec2(texelSize.x, 0.0)).r;
636
+
637
+ vec2 trailGrad = vec2(trailE - trailW, trailN - trailS);
638
+ float edgeStrength = clamp(length(trailGrad) * 8.0, 0.0, 1.0);
639
+
640
+ // Char color: palette-aware burned edge
641
+ vec3 charColor = paletteCopper(uPaletteMode) * 0.5;
642
+
643
+ // Core glow: use palette accent for inscription
644
+ float velocityDim = 1.0 - uEtchVelocity * 0.2;
645
+ vec3 accentColor = paletteAccent(uPaletteMode);
646
+ vec3 coreColor = accentColor * velocityDim;
647
+
648
+ // Add subtle iridescence to inscription core
649
+ vec3 etchIridescence = iridescence(uv, uTime, uIridescenceScale * 0.5, uIridescenceStrength);
650
+ coreColor += etchIridescence * 0.2;
651
+
652
+ color += coreColor * sharpTrail * 0.5 * uAccentIntensity;
653
+
654
+ // Char edge: subtract warm dark at edges
655
+ color = mix(color, charColor, edgeStrength * sharpTrail * 0.35);
656
+
657
+ // Soft bloom edge using palette lattice color
658
+ float softTrail = smoothstep(0.0, 0.15, trailIntensity);
659
+ vec3 bloomColor = paletteLattice(uPaletteMode, 0.5);
660
+ float bloomFactor = 1.0 - uEtchVelocity * 0.4;
661
+ color += bloomColor * softTrail * 0.25 * bloomFactor;
662
+
663
+ // Increase micro-lattice visibility in etched areas
664
+ float latticeAcceptance = sharpTrail * (1.0 + edgeStrength * 0.3);
665
+ color += latticeColor * microLattice * latticeAcceptance * 0.35;
666
+
667
+ // Subtle sparkle using palette accent
668
+ float sparkleGate = sharpTrail * (1.0 - edgeStrength * 0.5);
669
+ float sparkle = noise(uv * 500.0 + uTime * 5.0);
670
+ color += accentColor * 0.3 * sparkle * sparkleGate * 0.12;
671
+
672
+ // Velocity streaking
673
+ if (uEtchVelocity > 0.3) {
674
+ float streak = noise(uv * 200.0 + uTime * 8.0) * uEtchVelocity;
675
+ color += accentColor * 0.4 * streak * sharpTrail * 0.08;
676
+ }
677
+ }
678
+
679
+ // ========================================================================
680
+ // Hover Probe Effect (enhanced with Phase Lens integration)
681
+ // ========================================================================
682
+
683
+ if (uHover.w > 0.5) {
684
+ vec2 hoverUv = uHover.xy;
685
+ float isEtch = uHover.z;
686
+ float hoverDist = length(uv - hoverUv);
687
+
688
+ // Routing highlight zone
689
+ float highlightZone = smoothstep(uProbeRadius * 2.0, uProbeRadius * 0.5, hoverDist);
690
+
691
+ // Highlight nearby traces using palette
692
+ float nearbyTraces = traceEdge * highlightZone;
693
+ vec3 accentColor = paletteAccent(uPaletteMode);
694
+ vec3 highlightColor = isEtch > 0.5
695
+ ? accentColor * vec3(1.2, 0.6, 0.3) // Warm accent for etch
696
+ : accentColor; // Cool accent for probe
697
+
698
+ color += highlightColor * nearbyTraces * 0.5 * uAccentIntensity;
699
+
700
+ // If lens is enabled, the lens rim acts as indicator (no square needed)
701
+ if (uLensEnabled < 0.5) {
702
+ float indicator = smoothstep(uProbeRadius * 0.4, uProbeRadius * 0.3, hoverDist);
703
+ indicator *= smoothstep(uProbeRadius * 0.2, uProbeRadius * 0.3, hoverDist);
704
+ color += highlightColor * indicator * 0.35;
705
+ }
706
+
707
+ // Data packet flow animation using lattice color
708
+ float flow = sin((hoverDist - uTime * 2.0) * 50.0) * 0.5 + 0.5;
709
+ float flowZone = smoothstep(uProbeRadius * 1.5, uProbeRadius * 0.3, hoverDist);
710
+ color += paletteLattice(uPaletteMode, 0.8) * flow * flowZone * nearbyTraces * 0.25;
711
+ }
712
+
713
+ // ========================================================================
714
+ // Impulse Rings (Square Engineering Bursts)
715
+ // ========================================================================
716
+
717
+ for (int i = 0; i < 24; i++) {
718
+ if (i >= uImpulseCount) break;
719
+
720
+ vec4 imp = uImpulses[i];
721
+ float age = uImpulsesAge[i];
722
+
723
+ if (age >= 1.0) continue;
724
+
725
+ vec2 impUv = imp.xy;
726
+ float baseRadius = imp.z;
727
+ float amp = imp.w;
728
+
729
+ // Square expanding ring (Manhattan distance)
730
+ vec2 toImp = abs(uv - impUv);
731
+ float sqDist = max(toImp.x, toImp.y);
732
+ float expandedRadius = baseRadius + age * 0.3;
733
+
734
+ // Ring with thickness
735
+ float ringThickness = 0.01 + (1.0 - age) * 0.015;
736
+ float ringOuter = smoothstep(expandedRadius + ringThickness, expandedRadius, sqDist);
737
+ float ringInner = smoothstep(expandedRadius - ringThickness, expandedRadius, sqDist);
738
+ float squareRing = ringOuter * ringInner;
739
+
740
+ // Fade with age
741
+ float fade = (1.0 - age * age) * amp;
742
+
743
+ // Burst color from palette accent with age transition
744
+ vec3 accentColor = paletteAccent(uPaletteMode);
745
+ vec3 burstColor = mix(
746
+ accentColor,
747
+ accentColor * vec3(1.3, 1.0, 0.6), // Warmer as it fades
748
+ age
749
+ );
750
+
751
+ color += burstColor * squareRing * fade * 0.7 * uAccentIntensity;
752
+
753
+ // Data traveling along traces from burst
754
+ float impDist = length(uv - impUv);
755
+ float traceActivation = smoothstep(0.3, 0.0, abs(traceSdf)) *
756
+ smoothstep(expandedRadius * 1.5, expandedRadius * 0.5, impDist) *
757
+ (1.0 - age);
758
+ color += paletteLattice(uPaletteMode, 0.7) * traceActivation * fade * 0.25;
759
+ }
760
+
761
+ // ========================================================================
762
+ // Anchors (Vias/Pins)
763
+ // ========================================================================
764
+
765
+ for (int i = 0; i < 8; i++) {
766
+ if (i >= uAnchorCount) break;
767
+
768
+ vec4 anchor = uAnchors[i];
769
+ vec2 anchorUv = anchor.xy;
770
+ float strength = anchor.z;
771
+ float phase = anchor.w;
772
+
773
+ float anchorDist = length(uv - anchorUv);
774
+
775
+ // Via pad (circular)
776
+ float viaPad = smoothstep(0.025, 0.02, anchorDist);
777
+
778
+ // Via hole (inner dark circle)
779
+ float viaHole = smoothstep(0.008, 0.012, anchorDist);
780
+
781
+ // Ring around via
782
+ float viaRing = smoothstep(0.03, 0.028, anchorDist) * smoothstep(0.025, 0.028, anchorDist);
783
+
784
+ // Pulsing glow
785
+ float pulse = 0.6 + 0.4 * sin(uTime * 2.0 + phase);
786
+
787
+ // Via color using palette
788
+ vec3 padColor = paletteCopper(uPaletteMode) * 2.0;
789
+ vec3 ringColor = paletteAccent(uPaletteMode);
790
+
791
+ color += padColor * viaPad * viaHole * strength * 0.7 * uCopperStrength;
792
+ color += ringColor * viaRing * strength * pulse * uAccentIntensity;
793
+
794
+ // Emanating trace activation
795
+ float emanate = smoothstep(0.15, 0.03, anchorDist) * traceEdge * strength;
796
+ float emanatePulse = sin(anchorDist * 50.0 - uTime * 3.0 + phase) * 0.5 + 0.5;
797
+ color += ringColor * emanate * emanatePulse * 0.25;
798
+ }
799
+
800
+ // ========================================================================
801
+ // Vector Field Visualization (Subtle, palette-aware)
802
+ // ========================================================================
803
+
804
+ vec2 flowDir = curlNoise(uv * 3.0 + uTime * 0.1);
805
+ float flowMag = length(flowDir);
806
+
807
+ // Subtle directional lines using palette lattice color
808
+ vec2 lineUv = uv + flowDir * 0.01;
809
+ float flowLine = abs(fract(dot(lineUv, normalize(flowDir + 0.001)) * 20.0) - 0.5);
810
+ float flowVis = smoothstep(0.1, 0.05, flowLine) * flowMag * 0.06;
811
+ color += paletteLattice(uPaletteMode, 0.2) * flowVis;
812
+
813
+ // ========================================================================
814
+ // Atmosphere Pass (Exposure + Filmic + Grain + CRT + Vignette)
815
+ // ========================================================================
816
+
817
+ // Apply exposure
818
+ color *= uExposure;
819
+
820
+ // Filmic tonemapping (blend between linear and filmic)
821
+ vec3 filmicColor = filmicTonemap(color);
822
+ color = mix(color, filmicColor, uFilmic);
823
+
824
+ // Film grain (subtle, stable)
825
+ if (uGrainStrength > 0.001) {
826
+ float grain = filmGrain(uv, uResolution, uTime);
827
+ color += grain * uGrainStrength;
828
+ }
829
+
830
+ // CRT effect (scanlines + aperture grille, gated by vignette)
831
+ if (uCrtStrength > 0.01) {
832
+ float crt = crtEffect(uv, uTime, uCrtStrength);
833
+ color -= crt * 0.1;
834
+ }
835
+
836
+ // Vignette (slightly stronger for cinematic focus)
837
+ vec2 vignetteUv = uv * 2.0 - 1.0;
838
+ float vignetteDist = length(vignetteUv);
839
+ float vignette = 1.0 - vignetteDist * vignetteDist * 0.2;
840
+ color *= vignette;
841
+
842
+ // Clamp to prevent negative values from grain/CRT
843
+ color = max(color, vec3(0.0));
844
+
845
+ gl_FragColor = vec4(color, 1.0);
846
+ }
847
+ `;
848
+
849
+ // -----------------------------------------------------------------------------
850
+ // Arrow Instance Shader
851
+ // -----------------------------------------------------------------------------
852
+
853
+ export const PCB_ARROWS_VERTEX_SHADER = /* glsl */ `
854
+ uniform float uTime;
855
+ uniform vec4 uHover;
856
+ uniform vec4 uAnchors[8];
857
+ uniform int uAnchorCount;
858
+ uniform float uProbeRadius;
859
+ // FIX: Add plane size uniform for correct UV normalization
860
+ uniform vec2 uPlaneSize;
861
+ // FIX: Add max point size uniform to prevent blowout
862
+ uniform float uMaxPointSize;
863
+
864
+ attribute float aPhase;
865
+ attribute float aSize;
866
+
867
+ varying float vBrightness;
868
+ varying float vPhase;
869
+ varying vec2 vArrowDir;
870
+
871
+ // Curl noise
872
+ float hash(vec2 p) {
873
+ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
874
+ }
875
+
876
+ float noise(vec2 p) {
877
+ vec2 i = floor(p);
878
+ vec2 f = fract(p);
879
+ f = f * f * (3.0 - 2.0 * f);
880
+
881
+ float a = hash(i);
882
+ float b = hash(i + vec2(1.0, 0.0));
883
+ float c = hash(i + vec2(0.0, 1.0));
884
+ float d = hash(i + vec2(1.0, 1.0));
885
+
886
+ return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
887
+ }
888
+
889
+ vec2 curlNoise(vec2 p) {
890
+ float eps = 0.01;
891
+ float n1 = noise(p + vec2(eps, 0.0));
892
+ float n2 = noise(p - vec2(eps, 0.0));
893
+ float n3 = noise(p + vec2(0.0, eps));
894
+ float n4 = noise(p - vec2(0.0, eps));
895
+
896
+ return vec2(n3 - n4, n1 - n2) / (2.0 * eps);
897
+ }
898
+
899
+ void main() {
900
+ vec3 pos = position;
901
+
902
+ // FIX: Correct UV normalization - plane positions are in world coords (e.g. -10 to +10)
903
+ // Convert to 0-1 UV space by dividing by plane size
904
+ vec2 posUv = (pos.xy / uPlaneSize) + 0.5;
905
+
906
+ // Get flow direction from curl noise
907
+ vec2 flowDir = curlNoise(posUv * 3.0 + uTime * 0.1);
908
+
909
+ // Modify flow toward hover point (subtle)
910
+ if (uHover.w > 0.5) {
911
+ vec2 toHover = uHover.xy - posUv;
912
+ float hoverDist = length(toHover);
913
+ // FIX: Reduce attraction strength to prevent warping entire field
914
+ float attraction = smoothstep(uProbeRadius * 3.0, 0.0, hoverDist);
915
+ flowDir = mix(flowDir, normalize(toHover + 0.001), attraction * 0.3);
916
+ }
917
+
918
+ // Modify flow toward anchors (subtle)
919
+ for (int i = 0; i < 8; i++) {
920
+ if (i >= uAnchorCount) break;
921
+ vec2 anchorUv = uAnchors[i].xy;
922
+ float strength = uAnchors[i].z;
923
+ vec2 toAnchor = anchorUv - posUv;
924
+ float anchorDist = length(toAnchor);
925
+ float pull = smoothstep(0.2, 0.0, anchorDist) * strength;
926
+ flowDir = mix(flowDir, normalize(toAnchor + 0.001), pull * 0.3);
927
+ }
928
+
929
+ vArrowDir = normalize(flowDir + 0.001);
930
+
931
+ // Base brightness (reduced to prevent blowout)
932
+ float brightness = 0.15 + length(flowDir) * 1.0;
933
+
934
+ // Hover boost (reduced)
935
+ if (uHover.w > 0.5) {
936
+ float hoverDist = length(posUv - uHover.xy);
937
+ float hoverBoost = smoothstep(uProbeRadius * 2.0, uProbeRadius * 0.5, hoverDist);
938
+ brightness += hoverBoost * 0.3;
939
+ }
940
+
941
+ vBrightness = clamp(brightness, 0.0, 1.0);
942
+ vPhase = aPhase;
943
+
944
+ // FIX: REMOVED the rotation that was clustering everything toward origin
945
+ // The old code: pos.xy = rotation * pos.xy * 0.5; was scaling positions down by 0.5
946
+ // This caused all points to cluster in a small area
947
+ // Instead, just apply a tiny jitter for animation
948
+ pos.xy += flowDir * sin(uTime * 2.0 + aPhase * 6.28) * 0.02;
949
+
950
+ vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
951
+ gl_Position = projectionMatrix * mvPosition;
952
+
953
+ // FIX: Safer point size calculation with clamp to prevent giant points
954
+ float rawSize = aSize * (0.5 + brightness * 0.3) * (60.0 / -mvPosition.z);
955
+ gl_PointSize = clamp(rawSize, 1.0, uMaxPointSize);
956
+ }
957
+ `;
958
+
959
+ export const PCB_ARROWS_FRAGMENT_SHADER = /* glsl */ `
960
+ uniform float uTime;
961
+ uniform int uPaletteMode;
962
+ uniform float uAccentIntensity;
963
+
964
+ varying float vBrightness;
965
+ varying float vPhase;
966
+ varying vec2 vArrowDir;
967
+
968
+ // Palette-aware arrow colors (simplified versions of main shader palettes)
969
+ vec3 arrowColorDim(int mode) {
970
+ if (mode == 0) return vec3(0.03, 0.12, 0.10); // glia-cyan
971
+ if (mode == 1) return vec3(0.08, 0.04, 0.12); // orchid
972
+ if (mode == 2) return vec3(0.12, 0.08, 0.03); // amber
973
+ if (mode == 3) return vec3(0.04, 0.10, 0.04); // mono
974
+ if (mode == 4) return vec3(0.05, 0.10, 0.15); // ice
975
+ // Techno-Gothic arrow dims
976
+ if (mode == 5) return vec3(0.08, 0.03, 0.12); // gothic-cathedral
977
+ if (mode == 6) return vec3(0.10, 0.02, 0.03); // gothic-void
978
+ if (mode == 7) return vec3(0.06, 0.08, 0.05); // gothic-sanctum
979
+ if (mode == 8) return vec3(0.12, 0.03, 0.05); // gothic-rose
980
+ return vec3(0.05, 0.12, 0.10);
981
+ }
982
+
983
+ vec3 arrowColorBright(int mode) {
984
+ if (mode == 0) return vec3(0.08, 0.35, 0.30); // glia-cyan
985
+ if (mode == 1) return vec3(0.20, 0.15, 0.35); // orchid
986
+ if (mode == 2) return vec3(0.30, 0.22, 0.08); // amber
987
+ if (mode == 3) return vec3(0.10, 0.30, 0.12); // mono
988
+ if (mode == 4) return vec3(0.12, 0.28, 0.40); // ice
989
+ // Techno-Gothic arrow brights
990
+ if (mode == 5) return vec3(0.30, 0.15, 0.45); // gothic-cathedral: purple glow
991
+ if (mode == 6) return vec3(0.40, 0.08, 0.10); // gothic-void: blood glow
992
+ if (mode == 7) return vec3(0.18, 0.32, 0.22); // gothic-sanctum: verdigris
993
+ if (mode == 8) return vec3(0.45, 0.12, 0.18); // gothic-rose: rose glow
994
+ return vec3(0.10, 0.30, 0.25);
995
+ }
996
+
997
+ void main() {
998
+ vec2 center = gl_PointCoord - 0.5;
999
+
1000
+ // Arrow shape (triangle pointing in flow direction)
1001
+ float angle = atan(vArrowDir.y, vArrowDir.x);
1002
+ mat2 rot = mat2(cos(-angle), -sin(-angle), sin(-angle), cos(-angle));
1003
+ vec2 p = rot * center;
1004
+
1005
+ // Triangle SDF approximation
1006
+ float arrow = max(abs(p.y) - (0.3 - p.x * 0.8), p.x - 0.25);
1007
+
1008
+ if (arrow > 0.0) discard;
1009
+
1010
+ float alpha = smoothstep(0.0, -0.05, arrow);
1011
+
1012
+ // Flow animation (subtle)
1013
+ float flow = sin(uTime * 4.0 + vPhase * 6.28) * 0.2 + 0.8;
1014
+
1015
+ // Color from palette (darker base, brighter tips)
1016
+ vec3 color = mix(
1017
+ arrowColorDim(uPaletteMode),
1018
+ arrowColorBright(uPaletteMode),
1019
+ vBrightness
1020
+ );
1021
+
1022
+ // Apply accent intensity
1023
+ color *= (0.8 + uAccentIntensity * 0.2);
1024
+
1025
+ // Reduced alpha to prevent additive blowout
1026
+ gl_FragColor = vec4(color, alpha * vBrightness * flow * 0.22);
1027
+ }
1028
+ `;
1029
+
1030
+ // -----------------------------------------------------------------------------
1031
+ // Material Factory
1032
+ // -----------------------------------------------------------------------------
1033
+
1034
+ export interface PcbUniforms {
1035
+ [key: string]: { value: unknown };
1036
+ uTime: { value: number };
1037
+ uResolution: { value: THREE.Vector2 };
1038
+ uHover: { value: THREE.Vector4 };
1039
+ uImpulses: { value: THREE.Vector4[] };
1040
+ uImpulsesAge: { value: Float32Array };
1041
+ uImpulseCount: { value: number };
1042
+ uAnchors: { value: THREE.Vector4[] };
1043
+ uAnchorCount: { value: number };
1044
+ uTrailTex: { value: THREE.Texture | null };
1045
+ uTrailEnabled: { value: number };
1046
+ uProbeRadius: { value: number };
1047
+ // FIX: New uniforms for correct UV normalization and point size control
1048
+ uPlaneSize: { value: THREE.Vector2 };
1049
+ uMaxPointSize: { value: number };
1050
+ // === Ultra-Fine Lattice Uniforms ===
1051
+ uMicroGrid1: { value: number };
1052
+ uMicroGrid2: { value: number };
1053
+ uMicroGridStrength: { value: number };
1054
+ uRevealStrength: { value: number };
1055
+ uBaseVisibility: { value: number };
1056
+ uMicroWarp: { value: number };
1057
+ uEtchDistortion: { value: number };
1058
+ // === Phase Lens Uniforms ===
1059
+ uLens: { value: THREE.Vector4 }; // xy = center uv, z = radius, w = magnification
1060
+ uLensVelocity: { value: number }; // 0..1 normalized velocity
1061
+ uLensChromatic: { value: number }; // chromatic aberration intensity
1062
+ uLensEnabled: { value: number }; // 0 or 1
1063
+ // === Lattice Mode Uniform ===
1064
+ uLatticeMode: { value: number }; // 0=rect, 1=hex, 2=tri
1065
+ // === Etch Velocity Uniform ===
1066
+ uEtchVelocity: { value: number }; // 0..1 for visual feedback
1067
+ // === Palette / Atmosphere Uniforms ===
1068
+ uPaletteMode: { value: number }; // 0=glia-cyan, 1=orchid, 2=amber, 3=mono, 4=ice
1069
+ uAccentIntensity: { value: number }; // 0-2
1070
+ uIridescenceStrength: { value: number }; // 0-1
1071
+ uIridescenceScale: { value: number }; // 1-40
1072
+ uExposure: { value: number }; // 0.6-2.0
1073
+ uFilmic: { value: number }; // 0-1
1074
+ uGrainStrength: { value: number }; // 0-0.08
1075
+ uCrtStrength: { value: number }; // 0-1
1076
+ uCopperStrength: { value: number }; // 0-1
1077
+ }
1078
+
1079
+ export function createPcbUniforms(): PcbUniforms {
1080
+ return {
1081
+ uTime: { value: 0 },
1082
+ uResolution: { value: new THREE.Vector2(1920, 1080) },
1083
+ uHover: { value: new THREE.Vector4(0.5, 0.5, 0, 0) },
1084
+ uImpulses: {
1085
+ value: Array(24)
1086
+ .fill(null)
1087
+ .map(() => new THREE.Vector4(0, 0, 0, 0)),
1088
+ },
1089
+ uImpulsesAge: { value: new Float32Array(24) },
1090
+ uImpulseCount: { value: 0 },
1091
+ uAnchors: {
1092
+ value: Array(8)
1093
+ .fill(null)
1094
+ .map(() => new THREE.Vector4(0, 0, 0, 0)),
1095
+ },
1096
+ uAnchorCount: { value: 0 },
1097
+ uTrailTex: { value: null },
1098
+ uTrailEnabled: { value: 0 },
1099
+ uProbeRadius: { value: 0.08 },
1100
+ // FIX: New uniforms - will be set from PcbField.tsx
1101
+ uPlaneSize: { value: new THREE.Vector2(20, 15) },
1102
+ uMaxPointSize: { value: 8 },
1103
+ // === Ultra-Fine Lattice Uniforms ===
1104
+ uMicroGrid1: { value: 60 }, // Fine grid frequency
1105
+ uMicroGrid2: { value: 200 }, // Ultra-fine grid frequency
1106
+ uMicroGridStrength: { value: 0.8 }, // Overall lattice visibility
1107
+ uRevealStrength: { value: 1.0 }, // How much hover reveals lattice
1108
+ uBaseVisibility: { value: 0.05 }, // Base visibility without hover (0-1)
1109
+ uMicroWarp: { value: 0.015 }, // Subtle domain warping
1110
+ uEtchDistortion: { value: 0.008 }, // UV distortion in etched areas
1111
+ // === Phase Lens Uniforms ===
1112
+ uLens: { value: new THREE.Vector4(0.5, 0.5, 0.12, 1.0) }, // center, radius, mag
1113
+ uLensVelocity: { value: 0 },
1114
+ uLensChromatic: { value: 0.35 },
1115
+ uLensEnabled: { value: 1 },
1116
+ // === Lattice Mode Uniform ===
1117
+ uLatticeMode: { value: 0 }, // 0=rect, 1=hex, 2=tri
1118
+ // === Etch Velocity Uniform ===
1119
+ uEtchVelocity: { value: 0 },
1120
+ // === Palette / Atmosphere Uniforms ===
1121
+ uPaletteMode: { value: 1 }, // Default: orchid (museum-grade)
1122
+ uAccentIntensity: { value: 1.1 },
1123
+ uIridescenceStrength: { value: 0.35 },
1124
+ uIridescenceScale: { value: 14 },
1125
+ uExposure: { value: 1.0 },
1126
+ uFilmic: { value: 0.85 },
1127
+ uGrainStrength: { value: 0.015 },
1128
+ uCrtStrength: { value: 0.25 },
1129
+ uCopperStrength: { value: 0.15 },
1130
+ };
1131
+ }
1132
+
1133
+ export function createPcbMaterial(uniforms: PcbUniforms): THREE.ShaderMaterial {
1134
+ return new THREE.ShaderMaterial({
1135
+ vertexShader: PCB_VERTEX_SHADER,
1136
+ fragmentShader: PCB_FRAGMENT_SHADER,
1137
+ uniforms,
1138
+ transparent: true,
1139
+ depthWrite: false,
1140
+ });
1141
+ }
1142
+
1143
+ export function createPcbArrowsMaterial(
1144
+ uniforms: PcbUniforms,
1145
+ blending: "normal" | "additive" = "normal"
1146
+ ): THREE.ShaderMaterial {
1147
+ return new THREE.ShaderMaterial({
1148
+ vertexShader: PCB_ARROWS_VERTEX_SHADER,
1149
+ fragmentShader: PCB_ARROWS_FRAGMENT_SHADER,
1150
+ uniforms: {
1151
+ uTime: uniforms.uTime,
1152
+ uHover: uniforms.uHover,
1153
+ uAnchors: uniforms.uAnchors,
1154
+ uAnchorCount: uniforms.uAnchorCount,
1155
+ uProbeRadius: uniforms.uProbeRadius,
1156
+ // UV normalization and point size control
1157
+ uPlaneSize: uniforms.uPlaneSize,
1158
+ uMaxPointSize: uniforms.uMaxPointSize,
1159
+ // Palette uniforms for arrows
1160
+ uPaletteMode: uniforms.uPaletteMode,
1161
+ uAccentIntensity: uniforms.uAccentIntensity,
1162
+ },
1163
+ transparent: true,
1164
+ depthWrite: false,
1165
+ // FIX: Default to normal blending for debugging; additive can be enabled via config
1166
+ blending: blending === "additive" ? THREE.AdditiveBlending : THREE.NormalBlending,
1167
+ });
1168
+ }
1169
+
1170
+ // -----------------------------------------------------------------------------
1171
+ // Arrows Geometry Factory
1172
+ // -----------------------------------------------------------------------------
1173
+
1174
+ export function createPcbArrowsGeometry(
1175
+ count: number,
1176
+ planeWidth: number,
1177
+ planeHeight: number
1178
+ ): THREE.BufferGeometry {
1179
+ const geometry = new THREE.BufferGeometry();
1180
+
1181
+ const positions = new Float32Array(count * 3);
1182
+ const phases = new Float32Array(count);
1183
+ const sizes = new Float32Array(count);
1184
+
1185
+ // Grid distribution for arrows
1186
+ const gridSizeX = Math.ceil(Math.sqrt(count * (planeWidth / planeHeight)));
1187
+ const gridSizeY = Math.ceil(count / gridSizeX);
1188
+
1189
+ for (let i = 0; i < count; i++) {
1190
+ const gx = i % gridSizeX;
1191
+ const gy = Math.floor(i / gridSizeX);
1192
+
1193
+ // Position with slight jitter
1194
+ const jitterX = (Math.random() - 0.5) * 0.3;
1195
+ const jitterY = (Math.random() - 0.5) * 0.3;
1196
+
1197
+ positions[i * 3] = ((gx + 0.5 + jitterX) / gridSizeX - 0.5) * planeWidth;
1198
+ positions[i * 3 + 1] = ((gy + 0.5 + jitterY) / gridSizeY - 0.5) * planeHeight;
1199
+ positions[i * 3 + 2] = 0.02;
1200
+
1201
+ phases[i] = Math.random();
1202
+ sizes[i] = 2.0 + Math.random() * 1.5;
1203
+ }
1204
+
1205
+ geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
1206
+ geometry.setAttribute("aPhase", new THREE.BufferAttribute(phases, 1));
1207
+ geometry.setAttribute("aSize", new THREE.BufferAttribute(sizes, 1));
1208
+
1209
+ return geometry;
1210
+ }