vireuikit 0.2.0 → 0.4.0

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.
@@ -1,4 +1,39 @@
1
1
  type VireUIKitMediumChannel = readonly [number, number, number];
2
+ /**
3
+ * A static light source, as data — the chamber's illumination is a separate entity from the gas it
4
+ * lights (see `MEDIUM_COMPOSITE_SHADER`): the medium itself is dark, and everything visible is
5
+ * light scattered by mist, condensate and tracks inside a light's reach.
6
+ */
7
+ type VireUIKitMediumLight = {
8
+ /** Fraction of the frame, 0…1 both axes. `y=0` is the chamber's visual BOTTOM edge, `y=1` the
9
+ * top — the same convention `gravityBottomBoost` already uses in `MEDIUM_COMPOSITE_SHADER`. */
10
+ position: readonly [number, number];
11
+ /** Unit direction (any nonzero vector is normalized before upload) the light points into the
12
+ * chamber, in the SAME up-is-positive-y convention as `position`. Kept off the exact
13
+ * vertical/horizontal axes by default: an on-axis beam edge is itself axis-aligned, which is
14
+ * exactly the straight-line artifact `check:medium`'s isotropy gate polices, and an angled shaft
15
+ * also reads more like a real illuminated volume than a spotlight aimed straight down a wall. */
16
+ direction: readonly [number, number];
17
+ /** Half-angle of the light's cone, radians. Wide (close to a right angle or beyond) for an edge
18
+ * strip that washes the whole chamber width — there the shaping comes from distance falloff, not
19
+ * a cone edge — narrow for a spotlight, where the cone edge IS the visible beam. */
20
+ coneAngle: number;
21
+ /** 1/(fraction of frame height) — how fast illumination drops off with distance from the light.
22
+ * Distance is normalized by frame HEIGHT (not a raw fraction of each axis) so a circular pool of
23
+ * light stays circular on screen regardless of the frame's aspect ratio. */
24
+ falloff: number;
25
+ /** sRGB 0…1. Defaults to a cool neutral; a caller deriving a light rig from cover art applies
26
+ * `speciesFamily`/`characterOf` (`species.ts`) to THESE colors now, not to `channelColors` —
27
+ * see the comment on `channelColors` below. */
28
+ color: VireUIKitMediumChannel;
29
+ /** ≥0 multiplier on this light's contribution. 0 turns it off without removing it from the array. */
30
+ intensity: number;
31
+ };
32
+ /** How many lights `MEDIUM_COMPOSITE_SHADER` reads — a fixed, small count of flat uniforms
33
+ * (`u_light0*`/`u_light1*`/`u_light2*`), the same pattern `u_lab0/1/2` already uses for species:
34
+ * `setUniform` (vireglass/web) has no array form, only scalars up to vec4. A `lights` array
35
+ * shorter than this is padded with intensity-0 entries; a longer one is truncated. */
36
+ declare const MEDIUM_MAX_LIGHTS = 3;
2
37
  type VireUIKitMediumParams = {
3
38
  /** 1/s — how fast the TRACK density decays per second. Water (vapor+condensate) does not decay:
4
39
  * it is a conserved volume, only moving between phases and being transported. */
@@ -33,6 +68,17 @@ type VireUIKitMediumParams = {
33
68
  /** grid-px/s — condensate drift downward, on top of the shared velocity field: gravity touches
34
69
  * droplets and leaves vapor alone. */
35
70
  condensateSettleSpeed: number;
71
+ /** Multiplier on the ambient curl field's OWN x-component, added to settling UNCONDITIONALLY
72
+ * (not gated by `turbulence` the way the shared velocity field is) — see
73
+ * MEDIUM_ADVECT_VELOCITY_SETTLE in advect-shader.ts. Without it, a droplet falling at a constant
74
+ * `condensateSettleSpeed` with only a turbulence-gated sideways push traces a near-straight
75
+ * vertical line whenever turbulence is low (rest/paused: 0.045, over 20x below the gated push
76
+ * needed to bend the fall) — many droplets in nearby columns then read as parallel vertical
77
+ * streaks, one of the axis-aligned artifacts `check-medium.mjs`'s isotropy gate measures. Reuses
78
+ * the SAME seamless curl field `vel` already reads rather than a second noise system, so it adds
79
+ * no new seam of its own. Calibrated against that gate at the product's default grid (1920x952,
80
+ * 74x37) — see the gate's own report for the measured ratio at this value. */
81
+ condensateSettleWiggle: number;
36
82
  /** 1/s — the rate at which a droplet blends with its grid neighbors. There is no separate age
37
83
  * counter: the blend repeats every frame, and whatever has survived more frames ends up more
38
84
  * blended with its neighbors on its own — that IS "growth". Keep it NOTICEABLY below
@@ -52,9 +98,16 @@ type VireUIKitMediumParams = {
52
98
  /** Near-white sRGB tone of droplets with no hue seeding (`condensateTint=0`) — droplets scatter
53
99
  * light rather than color it. */
54
100
  condensateColor: VireUIKitMediumChannel;
55
- /** Each vapor species' hue, sRGB 0…1. Derived as a family around one tone (`speciesFamily`),
56
- * not picked as three separate colors — see `species.ts`. */
101
+ /** Each vapor species' hue, sRGB 0…1, folded into the dark ambient mix underneath the lit layer
102
+ * (see `MEDIUM_COMPOSITE_SHADER`) — a minor, near-neutral hint by default. Colour now lives on
103
+ * `lights`, not here: a real chamber's gas is invisible, only what scatters light reads at all
104
+ * (see `channelScatter`). Kept for callers that still want a tinted ambient base, and because the
105
+ * isotropy/contrast/depth gates in `check-medium.mjs` measure structure through it directly. */
57
106
  channelColors: readonly [VireUIKitMediumChannel, VireUIKitMediumChannel, VireUIKitMediumChannel];
107
+ /** How strongly each vapor species scatters light, relative to the others — what a species IS,
108
+ * now that colour lives on `lights` (see `channelColors`). `[1, 1, 1]` (equal) by default; a
109
+ * caller can make one species read as denser mist than another without touching its hue. */
110
+ channelScatter: VireUIKitMediumChannel;
58
111
  /** The platform's neutral tone — what the medium is, alone, with no source playing. */
59
112
  baseColor: VireUIKitMediumChannel;
60
113
  /** Background weight in the Oklab mix: keeps the platform tone visible even where the smoke is dense. */
@@ -81,7 +134,8 @@ type VireUIKitMediumParams = {
81
134
  * `depthFarOffsetPx` below and `check-medium.mjs`'s decorrelation gate, which measures this
82
135
  * directly at the README's own 128x72. */
83
136
  depthFarOffsetFrac: readonly [number, number];
84
- /** Grid-px tap radius for the far plane's 4-tap box blur. A spatial average cannot raise local
137
+ /** Grid-px radius of the far plane's 8-tap ring blur, rotated so no tap pair lies on a grid
138
+ * axis. A spatial average cannot raise local
85
139
  * variance, so the same blur that softens the far plane's edges also, by construction, lowers
86
140
  * its measured contrast — one mechanism for both aerial-perspective cues (no separate contrast
87
141
  * knob exists). 0 disables it (the near plane stays sharp). Measured: reading the far plane's
@@ -119,7 +173,30 @@ type VireUIKitMediumParams = {
119
173
  * `smoothstep`. The brief asks for a denser LAYER at the floor of the chamber, not a boost that
120
174
  * reaches halfway up the frame — kept in the bottom quarter. */
121
175
  gravityBottomBoostStart: number;
176
+ /** Static lights — the chamber's illumination, entirely separate from the gas (see
177
+ * `VireUIKitMediumLight`). Padded/truncated to `MEDIUM_MAX_LIGHTS` by the web runtime. */
178
+ lights: readonly VireUIKitMediumLight[];
179
+ /** How strongly the gas between a spotlight and a point dims it, per frame height of dense gas. */
180
+ shadowExtinction: number;
181
+ /** 0…1 — a track's minimum visibility regardless of local illumination, added to the light
182
+ * reaching it. Tracks are lit the same way as mist, but a fresh ionization trail is bright enough
183
+ * in a real chamber to read even where the ambient light is weak — this is that floor, not a
184
+ * second light source. */
185
+ trackLightFloor: number;
186
+ /** ≥0 — master strength of the crisp, screen-space track layer (`web/track-layer.ts`): a grid
187
+ * cell cannot carry a thin sharp line (see that module's own header), so the grid stamp this
188
+ * file's `decay`/`trackLightFloor` govern stays the soft, gas-carried residue, and THIS is the
189
+ * bright, sharp geometry drawn over it in content pixels. 0 disables it outright — the canary
190
+ * `check:medium` uses to prove its "sharp at birth" gate is actually sensitive to the layer,
191
+ * not to the residue underneath it. */
192
+ trackLayerAmount: number;
122
193
  };
194
+ /**
195
+ * An edge strip along the chamber floor plus two spotlights above the frame, aimed down across it
196
+ * toward the source point. The lamps stay off screen, so a beam never shows its apex, and the two
197
+ * differ slightly in position, angle and strength: an exact mirror reads as staged.
198
+ */
199
+ declare const MEDIUM_DEFAULT_LIGHTS: readonly VireUIKitMediumLight[];
123
200
  declare const MEDIUM_DEFAULTS: VireUIKitMediumParams;
124
201
  /**
125
202
  * Resolves the far plane's fractional offset (`depthFarOffsetFrac`) into grid-px for a GIVEN grid
@@ -145,9 +222,11 @@ declare const MEDIUM_PROBE_FLOOR_PX = 22;
145
222
  declare const MEDIUM_DEFAULT_CELL_PX = 26;
146
223
  /**
147
224
  * The geometry of one track stamp (`emit-shader.ts`), in FRACTIONS of `min(gridW, gridH)` rather
148
- * than pixels — so a preset doesn't depend on the simulation grid's resolution. Two presets:
149
- * alpha is a thick, short, straight track; beta is a thin, long, ragged one. The third, natural,
150
- * is dim and short, for the occasional background track at rest (not tied to cover art).
225
+ * than pixels — so a preset doesn't depend on the simulation grid's resolution. Three presets, the
226
+ * reference's three visible kinds: `alpha` is a thick, short, straight ray — `createMediumDynamics`
227
+ * stamps a dozen or so of these at once per event, radiating from one point, so a single ray is
228
+ * short on purpose. `electron` is thin, long-ish and ragged (wiggly, granular). `muon` is long,
229
+ * straight and thin — a cosmic ray crossing the whole chamber, unrelated to any source.
151
230
  */
152
231
  type VireUIKitMediumTrackPreset = {
153
232
  lengthFrac: number;
@@ -162,7 +241,7 @@ type VireUIKitMediumTrackPreset = {
162
241
  tailDim: number;
163
242
  intensity: number;
164
243
  };
165
- declare const MEDIUM_TRACK_PRESETS: Readonly<Record<'alpha' | 'beta' | 'natural', VireUIKitMediumTrackPreset>>;
244
+ declare const MEDIUM_TRACK_PRESETS: Readonly<Record<'alpha' | 'electron' | 'muon', VireUIKitMediumTrackPreset>>;
166
245
  /** Turbulence at rest/paused/with no active track — the "slow drift" baseline shared by all three
167
246
  * neutral states (they differ from each other in other ways, not in this number). */
168
247
  declare const MEDIUM_DRIFT_TURBULENCE = 0.045;
@@ -184,30 +263,100 @@ declare const MEDIUM_EMISSION_PROBABILITY = 0.55;
184
263
  declare const MEDIUM_MIN_EMISSION_INTERVAL = 1.2;
185
264
  /** Average interval between solitary "background emission" tracks at rest, seconds, plus a random
186
265
  * `MEDIUM_NATURAL_JITTER` on top — rare, with no discernible rhythm. */
187
- declare const MEDIUM_NATURAL_INTERVAL = 25;
188
- declare const MEDIUM_NATURAL_JITTER = 35;
266
+ declare const MEDIUM_NATURAL_INTERVAL = 8;
267
+ declare const MEDIUM_NATURAL_JITTER = 12;
189
268
  /** The sensitive layer — fractions of height, top to bottom. Above it there isn't enough
190
269
  * supersaturation for a track to appear at all, so background emission is only ever visible here. */
191
270
  declare const MEDIUM_SENSITIVE_TOP = 0.55;
192
271
  declare const MEDIUM_SENSITIVE_BOTTOM = 0.95;
272
+ /** How many alpha rays a single decay event stamps at once, radiating from the same source point —
273
+ * the reference's starburst, not a lone track (`MEDIUM_TRACK_PRESETS.alpha` is the shape of ONE
274
+ * ray). Randomized per event within this range. */
275
+ declare const MEDIUM_ALPHA_BURST_RAYS: readonly [number, number];
276
+ /** Per-ray length multiplier within a burst, so the rays read as an organic spray rather than a
277
+ * wheel of identical spokes — the reference's rays visibly differ in reach. */
278
+ declare const MEDIUM_ALPHA_RAY_LENGTH_SCALE: readonly [number, number];
193
279
  /** Per-emission depth range applied to a track's width (`headWidthFrac`/`tailWidthFrac`), far…near.
194
280
  * The same knob reads as both "thin" and "soft": a Gaussian stamp's edge steepness scales with its
195
281
  * own width (`emit-shader.ts`'s only extent parameter), so shrinking it for a far track thins and
196
282
  * softens it in the same stroke — there is no second knob to hang an independent softness number
197
283
  * on. Floored at 0.55: below that a far `alpha` track's head narrows past what MacCormack's own
198
- * numerical smoothing already blurs it to, and the depth cue disappears into that noise floor. */
284
+ * numerical smoothing already blurs it to, and the depth cue disappears into that noise floor.
285
+ * The near end now runs past 1 (1.2, not 1): depth of field needs the NEAREST tracks a little
286
+ * LARGER than the base preset too, not just full-size — the reference shows near tracks blurring
287
+ * into big soft shapes rather than merely reading "as sharp as it gets". The same knob does both
288
+ * jobs again: a wider-than-base stamp is also a softer-edged one. */
199
289
  declare const MEDIUM_TRACK_DEPTH_WIDTH_RANGE: readonly [number, number];
200
290
  /** Per-emission depth range applied to a track's intensity, far…near — "dim". Floored at 0.4, not
201
291
  * lower: a far track still has to clear `MEDIUM_DEFAULTS.condensationFloor`'s excess threshold
202
292
  * often enough to read as a track, or depth would look like tracks randomly failing to spawn
203
293
  * rather than fading into the distance. */
204
294
  declare const MEDIUM_TRACK_DEPTH_INTENSITY_RANGE: readonly [number, number];
295
+ /**
296
+ * How long a live track stays in the crisp screen-space layer (`web/track-layer.ts`), seconds, by
297
+ * preset — tuned by eye against the reference's own "roughly one to two seconds"
298
+ * (`docs`/the brief's own reference frames): `alpha` is the shortest, thickest burst ray; `muon`,
299
+ * a cosmic ray crossing the whole chamber, lingers longest. Independent of `MEDIUM_DEFAULTS.decay`,
300
+ * which times the SOFT gas-carried residue left behind in the grid, not this layer.
301
+ */
302
+ declare const MEDIUM_TRACK_LAYER_LIFE_SECONDS: Readonly<Record<keyof typeof MEDIUM_TRACK_PRESETS, number>>;
303
+ /** Oldest-out cap on the live-track list (`web/track-layer.ts`) — a burst of a dozen-plus alpha
304
+ * rays every few seconds would otherwise grow the instanced draw without bound over a long
305
+ * session; 64 is comfortably above what a few overlapping bursts plus background radiation ever
306
+ * need alive at once. */
307
+ declare const MEDIUM_TRACK_LAYER_MAX_TRACKS = 64;
308
+ /**
309
+ * How a track looks in the crisp layer, per kind. Lengths are fractions of
310
+ * `min(contentWidth, contentHeight)`: the grid presets' widths are sized for a 26 px cell and
311
+ * would be tens of pixels wide here.
312
+ */
313
+ type VireUIKitTrackLayerLook = {
314
+ /** Core Gaussian sigma at birth, at the source end. */
315
+ sigmaFrac: number;
316
+ /** Sigma multiplier at the far end: an alpha thickens toward the end of its range. */
317
+ endSigmaMul: number;
318
+ /** Diffusion: sigma(age) = sigma * sqrt(1 + broaden * age). */
319
+ broaden: number;
320
+ /** Sideways wiggle amplitude, for kinds whose preset has a ragged path. */
321
+ wiggleFrac: number;
322
+ /** Period of the brightness beads along the track. */
323
+ beadFrac: number;
324
+ /** Mean spacing of stray droplets along the track, and the share of slots that hold one. */
325
+ dropletFrac: number;
326
+ dropletChance: number;
327
+ /** Gap between the source point and where the track starts. */
328
+ startFrac: number;
329
+ /** Brightness at birth, before depth and fade. */
330
+ gain: number;
331
+ };
332
+ declare const MEDIUM_TRACK_LAYER_LOOK: Readonly<Record<keyof typeof MEDIUM_TRACK_PRESETS, VireUIKitTrackLayerLook>>;
333
+ /** Seconds over which one alpha burst's rays are born: a strong source sprays rays one after
334
+ * another, not all at the same instant. The first `MEDIUM_TRACK_LAYER_BURST_ACCENT` rays land on
335
+ * the beat itself. */
336
+ declare const MEDIUM_TRACK_LAYER_BURST_SPREAD = 1.6;
337
+ declare const MEDIUM_TRACK_LAYER_BURST_ACCENT = 3;
338
+ /** Shapes the brightness-vs-age curve as `(1 - age/life) ** gamma`: reaches exactly 0 at removal
339
+ * (no pop when a track leaves the list) while staying close to full brightness for the first part
340
+ * of its life (>1 skews the fade toward the end rather than a flat linear ramp down). */
341
+ declare const MEDIUM_TRACK_LAYER_FADE_GAMMA = 1.6;
342
+ /** Downward drift, as a fraction of `min(contentWidth, contentHeight)` per second — droplets that
343
+ * formed along a track keep settling after the stamp itself, the same physical idea as
344
+ * `MEDIUM_DEFAULTS.condensateSettleSpeed` but for this layer's own screen-space geometry rather
345
+ * than the simulation grid. Small: over a track's longest life (muon, 2.2s) this alone moves it
346
+ * well under a tenth of the frame. */
347
+ declare const MEDIUM_TRACK_LAYER_GRAVITY_SAG = 0.018;
348
+ /** Scales the curl field's push on a live track (see `web/track-layer.ts`'s `curlVelocityJS`),
349
+ * units of `min(contentWidth, contentHeight)` per second per unit of curl velocity — small enough
350
+ * to read as "nudged", not carried the way the gas itself is by `MEDIUM_DEFAULTS.advectSpeed`. */
351
+ declare const MEDIUM_TRACK_LAYER_CURL_PUSH = 0.05;
205
352
 
206
353
  type VireUIKitMediumPlaybackState = 'idle' | 'playing' | 'paused' | 'stopped';
207
354
  type VireUIKitMediumTrackPresetName = keyof typeof MEDIUM_TRACK_PRESETS;
208
355
  /** The fully resolved geometry of one track stamp — the platform layer (`web/medium.ts`)
209
356
  * translates fractions into simulation-grid pixels and binds the uniforms, deciding nothing else:
210
- * which preset, where, and how bright is entirely this model's call. */
357
+ * which preset, where, and how bright is entirely this model's call. Carries no color of its own
358
+ * (there is no `channel` field): a track is lit the same way as the mist around it
359
+ * (`MEDIUM_COMPOSITE_SHADER`), not tinted by which track it happens to be. */
211
360
  type VireUIKitMediumEmission = VireUIKitMediumTrackPreset & {
212
361
  /** 0…1, a fraction of the grid on both axes — the source point (cover art while playing;
213
362
  * wherever it happens to land at rest). */
@@ -215,12 +364,16 @@ type VireUIKitMediumEmission = VireUIKitMediumTrackPreset & {
215
364
  /** Radians, direction from the source to the head. */
216
365
  angle: number;
217
366
  seed: number;
218
- channel: readonly [number, number, number];
219
367
  /** 0 (farthest) … 1 (nearest), random within the full range and deterministic from `seed` — which
220
368
  * plane the track reads as living on. Already folded into this emission's own `headWidthFrac`/
221
369
  * `tailWidthFrac`/`intensity` below (see `buildEmission`); carried here too so a caller inspecting
222
370
  * an emission can see why. */
223
371
  depth: number;
372
+ /** Which preset this emission was built from — `applyTrackDepth` already folds depth into the
373
+ * numeric fields above, but the crisp screen-space track layer (`web/track-layer.ts`) still needs
374
+ * the NAME: droplet density/size and per-kind lifetime are keyed off it, not off any numeric
375
+ * field here (a depth-scaled `headWidthFrac` alone can't tell an alpha from an electron). */
376
+ kind: VireUIKitMediumTrackPresetName;
224
377
  };
225
378
  type VireUIKitMediumDynamicsInput = {
226
379
  state: VireUIKitMediumPlaybackState;
@@ -246,7 +399,10 @@ type VireUIKitMediumDynamics = {
246
399
  * Scales a track preset's width and intensity by depth (0 far … 1 near) — separated from
247
400
  * `buildEmission` below and exported so depth's effect on a preset is directly testable with two
248
401
  * plain numbers, independent of how a specific emission's depth got hashed from its seed. At
249
- * depth=1 this returns the preset's own numbers unchanged (both ranges' upper bound is 1).
402
+ * depth=1 this returns the preset's OWN intensity unchanged but its width scaled by the width
403
+ * range's own near end (`MEDIUM_TRACK_DEPTH_WIDTH_RANGE[1]`, now slightly above 1 — the nearest
404
+ * tracks read a little larger and softer than the base preset, not merely "as sharp as it gets";
405
+ * see that constant's own comment).
250
406
  */
251
407
  declare function applyTrackDepth(preset: VireUIKitMediumTrackPreset, depth: number): VireUIKitMediumTrackPreset;
252
408
  /**
@@ -256,4 +412,4 @@ declare function applyTrackDepth(preset: VireUIKitMediumTrackPreset, depth: numb
256
412
  */
257
413
  declare function createMediumDynamics(): VireUIKitMediumDynamics;
258
414
 
259
- export { wrappedDistanceFromZero as A, MEDIUM_DEFAULTS as M, type VireUIKitMediumChannel as V, MEDIUM_DEFAULT_CELL_PX as a, MEDIUM_DRIFT_TURBULENCE as b, MEDIUM_EMISSION_BEATS as c, MEDIUM_EMISSION_PROBABILITY as d, MEDIUM_MIN_EMISSION_INTERVAL as e, MEDIUM_NATURAL_INTERVAL as f, MEDIUM_NATURAL_JITTER as g, MEDIUM_PLAYING_TURBULENCE_RANGE as h, MEDIUM_PROBE_FLOOR_PX as i, MEDIUM_SENSITIVE_BOTTOM as j, MEDIUM_SENSITIVE_TOP as k, MEDIUM_TRACK_DEPTH_INTENSITY_RANGE as l, MEDIUM_TRACK_DEPTH_WIDTH_RANGE as m, MEDIUM_TRACK_PRESETS as n, MEDIUM_TURBULENCE_TAU as o, type VireUIKitMediumDynamics as p, type VireUIKitMediumDynamicsFrame as q, type VireUIKitMediumDynamicsInput as r, type VireUIKitMediumEmission as s, type VireUIKitMediumParams as t, type VireUIKitMediumPlaybackState as u, type VireUIKitMediumTrackPreset as v, type VireUIKitMediumTrackPresetName as w, applyTrackDepth as x, createMediumDynamics as y, depthFarOffsetPx as z };
415
+ export { MEDIUM_TRACK_PRESETS as A, MEDIUM_TURBULENCE_TAU as B, type VireUIKitMediumDynamics as C, type VireUIKitMediumDynamicsFrame as D, type VireUIKitMediumDynamicsInput as E, type VireUIKitMediumEmission as F, type VireUIKitMediumParams as G, type VireUIKitMediumPlaybackState as H, type VireUIKitMediumTrackPreset as I, type VireUIKitMediumTrackPresetName as J, type VireUIKitTrackLayerLook as K, applyTrackDepth as L, MEDIUM_ALPHA_BURST_RAYS as M, createMediumDynamics as N, depthFarOffsetPx as O, wrappedDistanceFromZero as P, type VireUIKitMediumChannel as V, type VireUIKitMediumLight as a, MEDIUM_ALPHA_RAY_LENGTH_SCALE as b, MEDIUM_DEFAULTS as c, MEDIUM_DEFAULT_CELL_PX as d, MEDIUM_DEFAULT_LIGHTS as e, MEDIUM_DRIFT_TURBULENCE as f, MEDIUM_EMISSION_BEATS as g, MEDIUM_EMISSION_PROBABILITY as h, MEDIUM_MAX_LIGHTS as i, MEDIUM_MIN_EMISSION_INTERVAL as j, MEDIUM_NATURAL_INTERVAL as k, MEDIUM_NATURAL_JITTER as l, MEDIUM_PLAYING_TURBULENCE_RANGE as m, MEDIUM_PROBE_FLOOR_PX as n, MEDIUM_SENSITIVE_BOTTOM as o, MEDIUM_SENSITIVE_TOP as p, MEDIUM_TRACK_DEPTH_INTENSITY_RANGE as q, MEDIUM_TRACK_DEPTH_WIDTH_RANGE as r, MEDIUM_TRACK_LAYER_BURST_ACCENT as s, MEDIUM_TRACK_LAYER_BURST_SPREAD as t, MEDIUM_TRACK_LAYER_CURL_PUSH as u, MEDIUM_TRACK_LAYER_FADE_GAMMA as v, MEDIUM_TRACK_LAYER_GRAVITY_SAG as w, MEDIUM_TRACK_LAYER_LIFE_SECONDS as x, MEDIUM_TRACK_LAYER_LOOK as y, MEDIUM_TRACK_LAYER_MAX_TRACKS as z };
@@ -1,4 +1,39 @@
1
1
  type VireUIKitMediumChannel = readonly [number, number, number];
2
+ /**
3
+ * A static light source, as data — the chamber's illumination is a separate entity from the gas it
4
+ * lights (see `MEDIUM_COMPOSITE_SHADER`): the medium itself is dark, and everything visible is
5
+ * light scattered by mist, condensate and tracks inside a light's reach.
6
+ */
7
+ type VireUIKitMediumLight = {
8
+ /** Fraction of the frame, 0…1 both axes. `y=0` is the chamber's visual BOTTOM edge, `y=1` the
9
+ * top — the same convention `gravityBottomBoost` already uses in `MEDIUM_COMPOSITE_SHADER`. */
10
+ position: readonly [number, number];
11
+ /** Unit direction (any nonzero vector is normalized before upload) the light points into the
12
+ * chamber, in the SAME up-is-positive-y convention as `position`. Kept off the exact
13
+ * vertical/horizontal axes by default: an on-axis beam edge is itself axis-aligned, which is
14
+ * exactly the straight-line artifact `check:medium`'s isotropy gate polices, and an angled shaft
15
+ * also reads more like a real illuminated volume than a spotlight aimed straight down a wall. */
16
+ direction: readonly [number, number];
17
+ /** Half-angle of the light's cone, radians. Wide (close to a right angle or beyond) for an edge
18
+ * strip that washes the whole chamber width — there the shaping comes from distance falloff, not
19
+ * a cone edge — narrow for a spotlight, where the cone edge IS the visible beam. */
20
+ coneAngle: number;
21
+ /** 1/(fraction of frame height) — how fast illumination drops off with distance from the light.
22
+ * Distance is normalized by frame HEIGHT (not a raw fraction of each axis) so a circular pool of
23
+ * light stays circular on screen regardless of the frame's aspect ratio. */
24
+ falloff: number;
25
+ /** sRGB 0…1. Defaults to a cool neutral; a caller deriving a light rig from cover art applies
26
+ * `speciesFamily`/`characterOf` (`species.ts`) to THESE colors now, not to `channelColors` —
27
+ * see the comment on `channelColors` below. */
28
+ color: VireUIKitMediumChannel;
29
+ /** ≥0 multiplier on this light's contribution. 0 turns it off without removing it from the array. */
30
+ intensity: number;
31
+ };
32
+ /** How many lights `MEDIUM_COMPOSITE_SHADER` reads — a fixed, small count of flat uniforms
33
+ * (`u_light0*`/`u_light1*`/`u_light2*`), the same pattern `u_lab0/1/2` already uses for species:
34
+ * `setUniform` (vireglass/web) has no array form, only scalars up to vec4. A `lights` array
35
+ * shorter than this is padded with intensity-0 entries; a longer one is truncated. */
36
+ declare const MEDIUM_MAX_LIGHTS = 3;
2
37
  type VireUIKitMediumParams = {
3
38
  /** 1/s — how fast the TRACK density decays per second. Water (vapor+condensate) does not decay:
4
39
  * it is a conserved volume, only moving between phases and being transported. */
@@ -33,6 +68,17 @@ type VireUIKitMediumParams = {
33
68
  /** grid-px/s — condensate drift downward, on top of the shared velocity field: gravity touches
34
69
  * droplets and leaves vapor alone. */
35
70
  condensateSettleSpeed: number;
71
+ /** Multiplier on the ambient curl field's OWN x-component, added to settling UNCONDITIONALLY
72
+ * (not gated by `turbulence` the way the shared velocity field is) — see
73
+ * MEDIUM_ADVECT_VELOCITY_SETTLE in advect-shader.ts. Without it, a droplet falling at a constant
74
+ * `condensateSettleSpeed` with only a turbulence-gated sideways push traces a near-straight
75
+ * vertical line whenever turbulence is low (rest/paused: 0.045, over 20x below the gated push
76
+ * needed to bend the fall) — many droplets in nearby columns then read as parallel vertical
77
+ * streaks, one of the axis-aligned artifacts `check-medium.mjs`'s isotropy gate measures. Reuses
78
+ * the SAME seamless curl field `vel` already reads rather than a second noise system, so it adds
79
+ * no new seam of its own. Calibrated against that gate at the product's default grid (1920x952,
80
+ * 74x37) — see the gate's own report for the measured ratio at this value. */
81
+ condensateSettleWiggle: number;
36
82
  /** 1/s — the rate at which a droplet blends with its grid neighbors. There is no separate age
37
83
  * counter: the blend repeats every frame, and whatever has survived more frames ends up more
38
84
  * blended with its neighbors on its own — that IS "growth". Keep it NOTICEABLY below
@@ -52,9 +98,16 @@ type VireUIKitMediumParams = {
52
98
  /** Near-white sRGB tone of droplets with no hue seeding (`condensateTint=0`) — droplets scatter
53
99
  * light rather than color it. */
54
100
  condensateColor: VireUIKitMediumChannel;
55
- /** Each vapor species' hue, sRGB 0…1. Derived as a family around one tone (`speciesFamily`),
56
- * not picked as three separate colors — see `species.ts`. */
101
+ /** Each vapor species' hue, sRGB 0…1, folded into the dark ambient mix underneath the lit layer
102
+ * (see `MEDIUM_COMPOSITE_SHADER`) — a minor, near-neutral hint by default. Colour now lives on
103
+ * `lights`, not here: a real chamber's gas is invisible, only what scatters light reads at all
104
+ * (see `channelScatter`). Kept for callers that still want a tinted ambient base, and because the
105
+ * isotropy/contrast/depth gates in `check-medium.mjs` measure structure through it directly. */
57
106
  channelColors: readonly [VireUIKitMediumChannel, VireUIKitMediumChannel, VireUIKitMediumChannel];
107
+ /** How strongly each vapor species scatters light, relative to the others — what a species IS,
108
+ * now that colour lives on `lights` (see `channelColors`). `[1, 1, 1]` (equal) by default; a
109
+ * caller can make one species read as denser mist than another without touching its hue. */
110
+ channelScatter: VireUIKitMediumChannel;
58
111
  /** The platform's neutral tone — what the medium is, alone, with no source playing. */
59
112
  baseColor: VireUIKitMediumChannel;
60
113
  /** Background weight in the Oklab mix: keeps the platform tone visible even where the smoke is dense. */
@@ -81,7 +134,8 @@ type VireUIKitMediumParams = {
81
134
  * `depthFarOffsetPx` below and `check-medium.mjs`'s decorrelation gate, which measures this
82
135
  * directly at the README's own 128x72. */
83
136
  depthFarOffsetFrac: readonly [number, number];
84
- /** Grid-px tap radius for the far plane's 4-tap box blur. A spatial average cannot raise local
137
+ /** Grid-px radius of the far plane's 8-tap ring blur, rotated so no tap pair lies on a grid
138
+ * axis. A spatial average cannot raise local
85
139
  * variance, so the same blur that softens the far plane's edges also, by construction, lowers
86
140
  * its measured contrast — one mechanism for both aerial-perspective cues (no separate contrast
87
141
  * knob exists). 0 disables it (the near plane stays sharp). Measured: reading the far plane's
@@ -119,7 +173,30 @@ type VireUIKitMediumParams = {
119
173
  * `smoothstep`. The brief asks for a denser LAYER at the floor of the chamber, not a boost that
120
174
  * reaches halfway up the frame — kept in the bottom quarter. */
121
175
  gravityBottomBoostStart: number;
176
+ /** Static lights — the chamber's illumination, entirely separate from the gas (see
177
+ * `VireUIKitMediumLight`). Padded/truncated to `MEDIUM_MAX_LIGHTS` by the web runtime. */
178
+ lights: readonly VireUIKitMediumLight[];
179
+ /** How strongly the gas between a spotlight and a point dims it, per frame height of dense gas. */
180
+ shadowExtinction: number;
181
+ /** 0…1 — a track's minimum visibility regardless of local illumination, added to the light
182
+ * reaching it. Tracks are lit the same way as mist, but a fresh ionization trail is bright enough
183
+ * in a real chamber to read even where the ambient light is weak — this is that floor, not a
184
+ * second light source. */
185
+ trackLightFloor: number;
186
+ /** ≥0 — master strength of the crisp, screen-space track layer (`web/track-layer.ts`): a grid
187
+ * cell cannot carry a thin sharp line (see that module's own header), so the grid stamp this
188
+ * file's `decay`/`trackLightFloor` govern stays the soft, gas-carried residue, and THIS is the
189
+ * bright, sharp geometry drawn over it in content pixels. 0 disables it outright — the canary
190
+ * `check:medium` uses to prove its "sharp at birth" gate is actually sensitive to the layer,
191
+ * not to the residue underneath it. */
192
+ trackLayerAmount: number;
122
193
  };
194
+ /**
195
+ * An edge strip along the chamber floor plus two spotlights above the frame, aimed down across it
196
+ * toward the source point. The lamps stay off screen, so a beam never shows its apex, and the two
197
+ * differ slightly in position, angle and strength: an exact mirror reads as staged.
198
+ */
199
+ declare const MEDIUM_DEFAULT_LIGHTS: readonly VireUIKitMediumLight[];
123
200
  declare const MEDIUM_DEFAULTS: VireUIKitMediumParams;
124
201
  /**
125
202
  * Resolves the far plane's fractional offset (`depthFarOffsetFrac`) into grid-px for a GIVEN grid
@@ -145,9 +222,11 @@ declare const MEDIUM_PROBE_FLOOR_PX = 22;
145
222
  declare const MEDIUM_DEFAULT_CELL_PX = 26;
146
223
  /**
147
224
  * The geometry of one track stamp (`emit-shader.ts`), in FRACTIONS of `min(gridW, gridH)` rather
148
- * than pixels — so a preset doesn't depend on the simulation grid's resolution. Two presets:
149
- * alpha is a thick, short, straight track; beta is a thin, long, ragged one. The third, natural,
150
- * is dim and short, for the occasional background track at rest (not tied to cover art).
225
+ * than pixels — so a preset doesn't depend on the simulation grid's resolution. Three presets, the
226
+ * reference's three visible kinds: `alpha` is a thick, short, straight ray — `createMediumDynamics`
227
+ * stamps a dozen or so of these at once per event, radiating from one point, so a single ray is
228
+ * short on purpose. `electron` is thin, long-ish and ragged (wiggly, granular). `muon` is long,
229
+ * straight and thin — a cosmic ray crossing the whole chamber, unrelated to any source.
151
230
  */
152
231
  type VireUIKitMediumTrackPreset = {
153
232
  lengthFrac: number;
@@ -162,7 +241,7 @@ type VireUIKitMediumTrackPreset = {
162
241
  tailDim: number;
163
242
  intensity: number;
164
243
  };
165
- declare const MEDIUM_TRACK_PRESETS: Readonly<Record<'alpha' | 'beta' | 'natural', VireUIKitMediumTrackPreset>>;
244
+ declare const MEDIUM_TRACK_PRESETS: Readonly<Record<'alpha' | 'electron' | 'muon', VireUIKitMediumTrackPreset>>;
166
245
  /** Turbulence at rest/paused/with no active track — the "slow drift" baseline shared by all three
167
246
  * neutral states (they differ from each other in other ways, not in this number). */
168
247
  declare const MEDIUM_DRIFT_TURBULENCE = 0.045;
@@ -184,30 +263,100 @@ declare const MEDIUM_EMISSION_PROBABILITY = 0.55;
184
263
  declare const MEDIUM_MIN_EMISSION_INTERVAL = 1.2;
185
264
  /** Average interval between solitary "background emission" tracks at rest, seconds, plus a random
186
265
  * `MEDIUM_NATURAL_JITTER` on top — rare, with no discernible rhythm. */
187
- declare const MEDIUM_NATURAL_INTERVAL = 25;
188
- declare const MEDIUM_NATURAL_JITTER = 35;
266
+ declare const MEDIUM_NATURAL_INTERVAL = 8;
267
+ declare const MEDIUM_NATURAL_JITTER = 12;
189
268
  /** The sensitive layer — fractions of height, top to bottom. Above it there isn't enough
190
269
  * supersaturation for a track to appear at all, so background emission is only ever visible here. */
191
270
  declare const MEDIUM_SENSITIVE_TOP = 0.55;
192
271
  declare const MEDIUM_SENSITIVE_BOTTOM = 0.95;
272
+ /** How many alpha rays a single decay event stamps at once, radiating from the same source point —
273
+ * the reference's starburst, not a lone track (`MEDIUM_TRACK_PRESETS.alpha` is the shape of ONE
274
+ * ray). Randomized per event within this range. */
275
+ declare const MEDIUM_ALPHA_BURST_RAYS: readonly [number, number];
276
+ /** Per-ray length multiplier within a burst, so the rays read as an organic spray rather than a
277
+ * wheel of identical spokes — the reference's rays visibly differ in reach. */
278
+ declare const MEDIUM_ALPHA_RAY_LENGTH_SCALE: readonly [number, number];
193
279
  /** Per-emission depth range applied to a track's width (`headWidthFrac`/`tailWidthFrac`), far…near.
194
280
  * The same knob reads as both "thin" and "soft": a Gaussian stamp's edge steepness scales with its
195
281
  * own width (`emit-shader.ts`'s only extent parameter), so shrinking it for a far track thins and
196
282
  * softens it in the same stroke — there is no second knob to hang an independent softness number
197
283
  * on. Floored at 0.55: below that a far `alpha` track's head narrows past what MacCormack's own
198
- * numerical smoothing already blurs it to, and the depth cue disappears into that noise floor. */
284
+ * numerical smoothing already blurs it to, and the depth cue disappears into that noise floor.
285
+ * The near end now runs past 1 (1.2, not 1): depth of field needs the NEAREST tracks a little
286
+ * LARGER than the base preset too, not just full-size — the reference shows near tracks blurring
287
+ * into big soft shapes rather than merely reading "as sharp as it gets". The same knob does both
288
+ * jobs again: a wider-than-base stamp is also a softer-edged one. */
199
289
  declare const MEDIUM_TRACK_DEPTH_WIDTH_RANGE: readonly [number, number];
200
290
  /** Per-emission depth range applied to a track's intensity, far…near — "dim". Floored at 0.4, not
201
291
  * lower: a far track still has to clear `MEDIUM_DEFAULTS.condensationFloor`'s excess threshold
202
292
  * often enough to read as a track, or depth would look like tracks randomly failing to spawn
203
293
  * rather than fading into the distance. */
204
294
  declare const MEDIUM_TRACK_DEPTH_INTENSITY_RANGE: readonly [number, number];
295
+ /**
296
+ * How long a live track stays in the crisp screen-space layer (`web/track-layer.ts`), seconds, by
297
+ * preset — tuned by eye against the reference's own "roughly one to two seconds"
298
+ * (`docs`/the brief's own reference frames): `alpha` is the shortest, thickest burst ray; `muon`,
299
+ * a cosmic ray crossing the whole chamber, lingers longest. Independent of `MEDIUM_DEFAULTS.decay`,
300
+ * which times the SOFT gas-carried residue left behind in the grid, not this layer.
301
+ */
302
+ declare const MEDIUM_TRACK_LAYER_LIFE_SECONDS: Readonly<Record<keyof typeof MEDIUM_TRACK_PRESETS, number>>;
303
+ /** Oldest-out cap on the live-track list (`web/track-layer.ts`) — a burst of a dozen-plus alpha
304
+ * rays every few seconds would otherwise grow the instanced draw without bound over a long
305
+ * session; 64 is comfortably above what a few overlapping bursts plus background radiation ever
306
+ * need alive at once. */
307
+ declare const MEDIUM_TRACK_LAYER_MAX_TRACKS = 64;
308
+ /**
309
+ * How a track looks in the crisp layer, per kind. Lengths are fractions of
310
+ * `min(contentWidth, contentHeight)`: the grid presets' widths are sized for a 26 px cell and
311
+ * would be tens of pixels wide here.
312
+ */
313
+ type VireUIKitTrackLayerLook = {
314
+ /** Core Gaussian sigma at birth, at the source end. */
315
+ sigmaFrac: number;
316
+ /** Sigma multiplier at the far end: an alpha thickens toward the end of its range. */
317
+ endSigmaMul: number;
318
+ /** Diffusion: sigma(age) = sigma * sqrt(1 + broaden * age). */
319
+ broaden: number;
320
+ /** Sideways wiggle amplitude, for kinds whose preset has a ragged path. */
321
+ wiggleFrac: number;
322
+ /** Period of the brightness beads along the track. */
323
+ beadFrac: number;
324
+ /** Mean spacing of stray droplets along the track, and the share of slots that hold one. */
325
+ dropletFrac: number;
326
+ dropletChance: number;
327
+ /** Gap between the source point and where the track starts. */
328
+ startFrac: number;
329
+ /** Brightness at birth, before depth and fade. */
330
+ gain: number;
331
+ };
332
+ declare const MEDIUM_TRACK_LAYER_LOOK: Readonly<Record<keyof typeof MEDIUM_TRACK_PRESETS, VireUIKitTrackLayerLook>>;
333
+ /** Seconds over which one alpha burst's rays are born: a strong source sprays rays one after
334
+ * another, not all at the same instant. The first `MEDIUM_TRACK_LAYER_BURST_ACCENT` rays land on
335
+ * the beat itself. */
336
+ declare const MEDIUM_TRACK_LAYER_BURST_SPREAD = 1.6;
337
+ declare const MEDIUM_TRACK_LAYER_BURST_ACCENT = 3;
338
+ /** Shapes the brightness-vs-age curve as `(1 - age/life) ** gamma`: reaches exactly 0 at removal
339
+ * (no pop when a track leaves the list) while staying close to full brightness for the first part
340
+ * of its life (>1 skews the fade toward the end rather than a flat linear ramp down). */
341
+ declare const MEDIUM_TRACK_LAYER_FADE_GAMMA = 1.6;
342
+ /** Downward drift, as a fraction of `min(contentWidth, contentHeight)` per second — droplets that
343
+ * formed along a track keep settling after the stamp itself, the same physical idea as
344
+ * `MEDIUM_DEFAULTS.condensateSettleSpeed` but for this layer's own screen-space geometry rather
345
+ * than the simulation grid. Small: over a track's longest life (muon, 2.2s) this alone moves it
346
+ * well under a tenth of the frame. */
347
+ declare const MEDIUM_TRACK_LAYER_GRAVITY_SAG = 0.018;
348
+ /** Scales the curl field's push on a live track (see `web/track-layer.ts`'s `curlVelocityJS`),
349
+ * units of `min(contentWidth, contentHeight)` per second per unit of curl velocity — small enough
350
+ * to read as "nudged", not carried the way the gas itself is by `MEDIUM_DEFAULTS.advectSpeed`. */
351
+ declare const MEDIUM_TRACK_LAYER_CURL_PUSH = 0.05;
205
352
 
206
353
  type VireUIKitMediumPlaybackState = 'idle' | 'playing' | 'paused' | 'stopped';
207
354
  type VireUIKitMediumTrackPresetName = keyof typeof MEDIUM_TRACK_PRESETS;
208
355
  /** The fully resolved geometry of one track stamp — the platform layer (`web/medium.ts`)
209
356
  * translates fractions into simulation-grid pixels and binds the uniforms, deciding nothing else:
210
- * which preset, where, and how bright is entirely this model's call. */
357
+ * which preset, where, and how bright is entirely this model's call. Carries no color of its own
358
+ * (there is no `channel` field): a track is lit the same way as the mist around it
359
+ * (`MEDIUM_COMPOSITE_SHADER`), not tinted by which track it happens to be. */
211
360
  type VireUIKitMediumEmission = VireUIKitMediumTrackPreset & {
212
361
  /** 0…1, a fraction of the grid on both axes — the source point (cover art while playing;
213
362
  * wherever it happens to land at rest). */
@@ -215,12 +364,16 @@ type VireUIKitMediumEmission = VireUIKitMediumTrackPreset & {
215
364
  /** Radians, direction from the source to the head. */
216
365
  angle: number;
217
366
  seed: number;
218
- channel: readonly [number, number, number];
219
367
  /** 0 (farthest) … 1 (nearest), random within the full range and deterministic from `seed` — which
220
368
  * plane the track reads as living on. Already folded into this emission's own `headWidthFrac`/
221
369
  * `tailWidthFrac`/`intensity` below (see `buildEmission`); carried here too so a caller inspecting
222
370
  * an emission can see why. */
223
371
  depth: number;
372
+ /** Which preset this emission was built from — `applyTrackDepth` already folds depth into the
373
+ * numeric fields above, but the crisp screen-space track layer (`web/track-layer.ts`) still needs
374
+ * the NAME: droplet density/size and per-kind lifetime are keyed off it, not off any numeric
375
+ * field here (a depth-scaled `headWidthFrac` alone can't tell an alpha from an electron). */
376
+ kind: VireUIKitMediumTrackPresetName;
224
377
  };
225
378
  type VireUIKitMediumDynamicsInput = {
226
379
  state: VireUIKitMediumPlaybackState;
@@ -246,7 +399,10 @@ type VireUIKitMediumDynamics = {
246
399
  * Scales a track preset's width and intensity by depth (0 far … 1 near) — separated from
247
400
  * `buildEmission` below and exported so depth's effect on a preset is directly testable with two
248
401
  * plain numbers, independent of how a specific emission's depth got hashed from its seed. At
249
- * depth=1 this returns the preset's own numbers unchanged (both ranges' upper bound is 1).
402
+ * depth=1 this returns the preset's OWN intensity unchanged but its width scaled by the width
403
+ * range's own near end (`MEDIUM_TRACK_DEPTH_WIDTH_RANGE[1]`, now slightly above 1 — the nearest
404
+ * tracks read a little larger and softer than the base preset, not merely "as sharp as it gets";
405
+ * see that constant's own comment).
250
406
  */
251
407
  declare function applyTrackDepth(preset: VireUIKitMediumTrackPreset, depth: number): VireUIKitMediumTrackPreset;
252
408
  /**
@@ -256,4 +412,4 @@ declare function applyTrackDepth(preset: VireUIKitMediumTrackPreset, depth: numb
256
412
  */
257
413
  declare function createMediumDynamics(): VireUIKitMediumDynamics;
258
414
 
259
- export { wrappedDistanceFromZero as A, MEDIUM_DEFAULTS as M, type VireUIKitMediumChannel as V, MEDIUM_DEFAULT_CELL_PX as a, MEDIUM_DRIFT_TURBULENCE as b, MEDIUM_EMISSION_BEATS as c, MEDIUM_EMISSION_PROBABILITY as d, MEDIUM_MIN_EMISSION_INTERVAL as e, MEDIUM_NATURAL_INTERVAL as f, MEDIUM_NATURAL_JITTER as g, MEDIUM_PLAYING_TURBULENCE_RANGE as h, MEDIUM_PROBE_FLOOR_PX as i, MEDIUM_SENSITIVE_BOTTOM as j, MEDIUM_SENSITIVE_TOP as k, MEDIUM_TRACK_DEPTH_INTENSITY_RANGE as l, MEDIUM_TRACK_DEPTH_WIDTH_RANGE as m, MEDIUM_TRACK_PRESETS as n, MEDIUM_TURBULENCE_TAU as o, type VireUIKitMediumDynamics as p, type VireUIKitMediumDynamicsFrame as q, type VireUIKitMediumDynamicsInput as r, type VireUIKitMediumEmission as s, type VireUIKitMediumParams as t, type VireUIKitMediumPlaybackState as u, type VireUIKitMediumTrackPreset as v, type VireUIKitMediumTrackPresetName as w, applyTrackDepth as x, createMediumDynamics as y, depthFarOffsetPx as z };
415
+ export { MEDIUM_TRACK_PRESETS as A, MEDIUM_TURBULENCE_TAU as B, type VireUIKitMediumDynamics as C, type VireUIKitMediumDynamicsFrame as D, type VireUIKitMediumDynamicsInput as E, type VireUIKitMediumEmission as F, type VireUIKitMediumParams as G, type VireUIKitMediumPlaybackState as H, type VireUIKitMediumTrackPreset as I, type VireUIKitMediumTrackPresetName as J, type VireUIKitTrackLayerLook as K, applyTrackDepth as L, MEDIUM_ALPHA_BURST_RAYS as M, createMediumDynamics as N, depthFarOffsetPx as O, wrappedDistanceFromZero as P, type VireUIKitMediumChannel as V, type VireUIKitMediumLight as a, MEDIUM_ALPHA_RAY_LENGTH_SCALE as b, MEDIUM_DEFAULTS as c, MEDIUM_DEFAULT_CELL_PX as d, MEDIUM_DEFAULT_LIGHTS as e, MEDIUM_DRIFT_TURBULENCE as f, MEDIUM_EMISSION_BEATS as g, MEDIUM_EMISSION_PROBABILITY as h, MEDIUM_MAX_LIGHTS as i, MEDIUM_MIN_EMISSION_INTERVAL as j, MEDIUM_NATURAL_INTERVAL as k, MEDIUM_NATURAL_JITTER as l, MEDIUM_PLAYING_TURBULENCE_RANGE as m, MEDIUM_PROBE_FLOOR_PX as n, MEDIUM_SENSITIVE_BOTTOM as o, MEDIUM_SENSITIVE_TOP as p, MEDIUM_TRACK_DEPTH_INTENSITY_RANGE as q, MEDIUM_TRACK_DEPTH_WIDTH_RANGE as r, MEDIUM_TRACK_LAYER_BURST_ACCENT as s, MEDIUM_TRACK_LAYER_BURST_SPREAD as t, MEDIUM_TRACK_LAYER_CURL_PUSH as u, MEDIUM_TRACK_LAYER_FADE_GAMMA as v, MEDIUM_TRACK_LAYER_GRAVITY_SAG as w, MEDIUM_TRACK_LAYER_LIFE_SECONDS as x, MEDIUM_TRACK_LAYER_LOOK as y, MEDIUM_TRACK_LAYER_MAX_TRACKS as z };