@idle-screens/schema 2.2.0 → 2.3.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.
package/dist/index.d.ts CHANGED
@@ -22,8 +22,22 @@ interface SaverSpec {
22
22
  background?: BackgroundSpec;
23
23
  layers: LayerSpec[];
24
24
  motionIntensity?: 'calm' | 'moderate' | 'energetic';
25
- /** Dimensional unit system. 'viewport' = all sizes/speeds/distances are fractions of min(w,h). */
26
- units?: 'px' | 'viewport';
25
+ /**
26
+ * Frame persistence (0..1, capped at LIMITS.maxGhosting). Instead of fully clearing,
27
+ * each frame the background is painted at reduced alpha so moving entities leave
28
+ * decaying after-images (Mystify smears, long-exposure light). Inherently
29
+ * flash-safe: ghosting can only smooth luminance changes, never sharpen them.
30
+ * `renderFrame(t)` stays deterministic for seeks via a fixed-step warm-up replay.
31
+ */
32
+ ghosting?: number;
33
+ /** Dimensional unit system. 'viewport' (default) = all sizes/speeds/distances are fractions of min(w,h). */
34
+ units?: 'viewport' | 'px';
35
+ /**
36
+ * Resolution at which this spec was designed. Density scaling kicks in above this
37
+ * threshold: entity counts scale by min(w,h) / referenceViewport. Default: 1080.
38
+ * Set to your design resolution so a spec authored at 4K doesn't over-densify.
39
+ */
40
+ referenceViewport?: number;
27
41
  }
28
42
  type BackgroundSpec = {
29
43
  type: 'solid';
@@ -42,7 +56,7 @@ interface GradientStop {
42
56
  /** An optional solid band at the bottom (e.g. an aquarium seafloor). */
43
57
  interface BandSpec {
44
58
  color: string;
45
- /** Band height in px. */
59
+ /** Band height — px or a viewport fraction, per the spec's `units`. */
46
60
  height: number;
47
61
  }
48
62
  /** Slow oscillation of gradient stop positions — makes the background feel alive. */
@@ -64,8 +78,12 @@ interface LayerSpec {
64
78
  flip?: boolean;
65
79
  /** Per-entity opacity range, both 0..1. Default [1,1]. */
66
80
  alpha?: [number, number];
67
- /** Compositing for this layer. 'lighter' = additive (glow stacking). Default source-over. */
68
- blend?: 'lighter';
81
+ /**
82
+ * Compositing for this layer. 'lighter' = additive (glow stacking); 'screen' =
83
+ * gentler additive for pale backgrounds; 'multiply' = darkening (shadows,
84
+ * silhouettes). Default source-over. None of these can strobe.
85
+ */
86
+ blend?: 'lighter' | 'screen' | 'multiply';
69
87
  /**
70
88
  * Fractional spawn window (0 = left/top, 1 = right/bottom). Constrains where
71
89
  * entities are PLACED, not where they may travel. Default full viewport.
@@ -79,10 +97,20 @@ interface LayerSpec {
79
97
  * period has a floor (LIMITS.minPulsePeriod — max 2 Hz), and every entity gets
80
98
  * its own seeded phase, so a layer can never strobe in unison. Effective alpha
81
99
  * is clamped to 0..1.
100
+ *
101
+ * `wave` derives each entity's phase from its spawn position instead of a seeded
102
+ * draw, turning independent breathing into a traveling wave across the field
103
+ * (ripples, wind through grass). `wavelength` is dimensional (px or viewport
104
+ * units); `angle` is the propagation direction in degrees (0 = rightward,
105
+ * default 0). Still flash-safe: neighbors are out of phase by construction.
82
106
  */
83
107
  pulse?: {
84
108
  amp: number;
85
109
  period: number;
110
+ wave?: {
111
+ wavelength: number;
112
+ angle?: number;
113
+ };
86
114
  };
87
115
  /**
88
116
  * Per-entity rotation speed in degrees/sec (positive = clockwise).
@@ -112,7 +140,13 @@ interface LayerSpec {
112
140
  y: number;
113
141
  };
114
142
  /**
115
- * Inter-entity links: draw lines to each entity's k nearest neighbors within maxDist.
143
+ * Inter-entity links. `mode` picks the wiring:
144
+ * - 'nearest' (default): each entity's k nearest neighbors within maxDist.
145
+ * - 'chain': entities connected in order (0-1-2-…) — Mystify polygons, string
146
+ * art. Ignores k/maxDist; `closed` joins the last entity back to the first.
147
+ * - 'random': a fixed golden-ratio-stride wiring (k partners per entity,
148
+ * deterministic, no RNG draws) — breaks the crystalline k-nearest look.
149
+ * `falloff` fades link alpha with distance/maxDist, killing pop-in at the cutoff.
116
150
  * Capped at LIMITS.maxLinksK. Layer count must be <= LIMITS.maxLinkLayerCount when set.
117
151
  */
118
152
  links?: {
@@ -121,6 +155,9 @@ interface LayerSpec {
121
155
  color?: string;
122
156
  alpha?: number;
123
157
  width?: number;
158
+ mode?: 'nearest' | 'chain' | 'random';
159
+ falloff?: boolean;
160
+ closed?: boolean;
124
161
  };
125
162
  /**
126
163
  * Afterglow trail behind moving entities. Samples past positions analytically
@@ -130,6 +167,31 @@ interface LayerSpec {
130
167
  length: number;
131
168
  fade?: number;
132
169
  };
170
+ /**
171
+ * Grid placement instead of random scatter: entities fill cells row-major within
172
+ * `region`. `columns` defaults to an aspect-fit square-ish grid; `jitter` (0..1,
173
+ * scalar or per-axis) offsets each entity within its cell by a seeded fraction of
174
+ * the cell size — `{ y: 1 }` keeps columns crisp while scattering vertically
175
+ * (Matrix rain). Unlocks column effects, LED walls, mosaics, uniform dot fields.
176
+ */
177
+ layout?: {
178
+ type: 'grid';
179
+ columns?: number;
180
+ jitter?: number | {
181
+ x?: number;
182
+ y?: number;
183
+ };
184
+ };
185
+ /**
186
+ * Layer lifecycle for act structure — a pure function of t, no state. Alpha is 0
187
+ * before `enter` (ms), ramps up over `fade` ms (default 1000), holds at 1, then
188
+ * ramps down starting at `exit`. Entities are skipped entirely while at alpha 0.
189
+ */
190
+ life?: {
191
+ enter?: number;
192
+ exit?: number;
193
+ fade?: number;
194
+ };
133
195
  }
134
196
  type SpriteSpec = {
135
197
  kind: 'emoji';
@@ -145,13 +207,52 @@ type SpriteSpec = {
145
207
  maxWidth?: number;
146
208
  cycle?: CycleSpec;
147
209
  }
148
- /** `soft` renders a radial falloff (glow orb) instead of a hard disc. */
210
+ /** `soft` renders a radial falloff (glow orb) instead of a hard disc.
211
+ * `colorWeights` (same length as `colors`) biases the seeded per-entity pick —
212
+ * "mostly cool tones, occasional ember" without duplicating entries. */
149
213
  | {
150
214
  kind: 'circle';
151
215
  radius: [number, number];
152
216
  color: string;
153
217
  soft?: boolean;
154
218
  colors?: string[];
219
+ colorWeights?: number[];
220
+ }
221
+ /** Unfilled circle (bubbles, portals, sonar pings). `width` = stroke width. */
222
+ | {
223
+ kind: 'ring';
224
+ radius: [number, number];
225
+ color: string;
226
+ width?: number;
227
+ colors?: string[];
228
+ colorWeights?: number[];
229
+ }
230
+ /**
231
+ * A line segment oriented along the entity's instantaneous heading (derived
232
+ * analytically from its motion) with a faded tail — rain that reads as rain,
233
+ * shooting stars, warp stars. `length` is the segment length range; `width` the
234
+ * stroke width.
235
+ */
236
+ | {
237
+ kind: 'streak';
238
+ length: [number, number];
239
+ color: string;
240
+ width?: number;
241
+ colors?: string[];
242
+ colorWeights?: number[];
243
+ }
244
+ /**
245
+ * Axis-aligned rectangle (rotates with `spin`). `width` is the horizontal size
246
+ * range; `aspect` the height/width ratio range (default [1,1] = squares).
247
+ * Mondrian blocks, confetti, city lights.
248
+ */
249
+ | {
250
+ kind: 'rect';
251
+ width: [number, number];
252
+ aspect?: [number, number];
253
+ color: string;
254
+ colors?: string[];
255
+ colorWeights?: number[];
155
256
  };
156
257
  /** Rotate through sprite variants over time. Each entity offsets by its seeded phase. */
157
258
  interface CycleSpec {
@@ -190,7 +291,10 @@ type MotionSpec =
190
291
  /**
191
292
  * Orbit around a center point. Each entity gets a seeded radius from `radius`
192
293
  * and a seeded phase. `speed` is angular velocity in degrees/sec. `center` is
193
- * fractional {x, y} (default {0.5, 0.5} = viewport center).
294
+ * fractional {x, y} (default {0.5, 0.5} = viewport center) — or
295
+ * `{ layer: key }` to orbit a single-entity parent layer (moons around a
296
+ * wandering planet). Strictly one level deep: the parent may not itself
297
+ * orbit a layer, and must have `count: 1`.
194
298
  */
195
299
  | {
196
300
  type: 'orbit';
@@ -199,7 +303,59 @@ type MotionSpec =
199
303
  center?: {
200
304
  x: number;
201
305
  y: number;
306
+ } | {
307
+ layer: string;
202
308
  };
309
+ }
310
+ /**
311
+ * Organic harmonic drift — the analytic flow field. Each entity moves at a base
312
+ * velocity from `speed` plus 3 seeded sine octaves per axis, producing flowing
313
+ * curved paths (Flurry streams, aurora, jellyfish) with zero simulation state.
314
+ * `angle` fixes the base heading in degrees (omit for a seeded heading per
315
+ * entity); `meander` scales the harmonic amplitude (dimensional, default 0.05
316
+ * viewport units); `coherence` (0..1) blends every entity's harmonics toward a
317
+ * shared layer-level set — at 1 the field undulates in unison (fake flocking).
318
+ */
319
+ | {
320
+ type: 'wander';
321
+ speed: [number, number];
322
+ angle?: number;
323
+ meander?: number;
324
+ coherence?: number;
325
+ }
326
+ /**
327
+ * Perspective starfield: entities live on a depth axis and stream toward the
328
+ * viewer, projected as `screen = center + offset / z`. Size and alpha scale
329
+ * with 1/z (small and faint at the far plane, large and fast up close), and z
330
+ * wraps — the honest warp tunnel. `speed` is in depth-units/sec
331
+ * (1 = full near-to-far span per second, capped at LIMITS.maxWarpSpeed).
332
+ */
333
+ | {
334
+ type: 'warp';
335
+ speed: [number, number];
336
+ center?: {
337
+ x: number;
338
+ y: number;
339
+ };
340
+ }
341
+ /**
342
+ * Choreographed spline motion: entities traverse `points` (fractional {x,y})
343
+ * over `duration` ms. `curve: 'smooth'` (default) uses Catmull-Rom through the
344
+ * points; 'linear' uses straight segments. `closed` (default true) loops the
345
+ * path; open paths ping-pong. Each entity gets a seeded phase offset along the
346
+ * path, and `scatter` (dimensional) adds a seeded per-entity offset so shared
347
+ * paths don't stack. Figure-eights, sweeping arcs, patrol routes.
348
+ */
349
+ | {
350
+ type: 'path';
351
+ points: Array<{
352
+ x: number;
353
+ y: number;
354
+ }>;
355
+ duration: number;
356
+ curve?: 'linear' | 'smooth';
357
+ closed?: boolean;
358
+ scatter?: number;
203
359
  };
204
360
  /** A validation problem, pointing at a JSON path within the spec. */
205
361
  interface SpecError {
@@ -237,6 +393,14 @@ declare const LIMITS: {
237
393
  readonly maxTrailSamples: 24;
238
394
  readonly minDriftPeriod: 10000;
239
395
  readonly maxDriftAmount: 0.3;
396
+ readonly maxGhosting: 0.95;
397
+ readonly maxGhostReplayFrames: 120;
398
+ readonly maxMeander: 500;
399
+ readonly maxWarpSpeed: 1.5;
400
+ readonly minPathPoints: 2;
401
+ readonly maxPathPoints: 24;
402
+ readonly minPathDuration: 2000;
403
+ readonly maxGridColumns: 100;
240
404
  };
241
405
 
242
406
  /**
@@ -250,8 +414,21 @@ declare function validateSpec(spec: unknown): ValidationResult;
250
414
  /** Narrowing helper: validate + cast. Throws with a joined message when invalid. */
251
415
  declare function assertValidSpec(spec: unknown): SaverSpec;
252
416
 
417
+ /** Near plane for warp motion. z lives in [WARP_NEAR, 1]; screen scale is 1/z. */
418
+ declare const WARP_NEAR = 0.08;
419
+ /** One axis-pair of seeded harmonic octaves for wander motion (amps px, freqs rad/ms). */
420
+ interface WanderOsc {
421
+ ax: number[];
422
+ fx: number[];
423
+ phx: number[];
424
+ ay: number[];
425
+ fy: number[];
426
+ phy: number[];
427
+ }
253
428
  /** A seeded entity: initial state + resolved velocity. `positionAt` is a pure function
254
- * of this + time, so the rAF loop and renderFrame(t) produce identical results. */
429
+ * of this + time, so the rAF loop and renderFrame(t) produce identical results.
430
+ * IMPORTANT (determinism/compat): fields for newer features are OPTIONAL and only
431
+ * set when the feature is declared, so older specs snapshot byte-identically. */
255
432
  interface Entity {
256
433
  x0: number;
257
434
  y0: number;
@@ -261,7 +438,7 @@ interface Entity {
261
438
  vx: number;
262
439
  vy: number;
263
440
  bob: number;
264
- motion: 'drift' | 'rise' | 'bounce' | 'static' | 'orbit';
441
+ motion: 'drift' | 'rise' | 'bounce' | 'static' | 'orbit' | 'wander' | 'warp' | 'path';
265
442
  headingLeft: boolean;
266
443
  alpha: number;
267
444
  pulseAmp: number;
@@ -277,6 +454,39 @@ interface Entity {
277
454
  orbitCy: number;
278
455
  colorIndex: number;
279
456
  cyclePeriod: number;
457
+ /** Rect sprite second dimension (height px). Only set for rect sprites. */
458
+ size2?: number;
459
+ /** Orbit parent layer key (motion.center = { layer }). Center resolved at render time. */
460
+ orbitParent?: string;
461
+ /** Harmonic drift params. Only set for wander motion. */
462
+ wander?: {
463
+ own: WanderOsc;
464
+ shared: WanderOsc;
465
+ coherence: number;
466
+ margin: number;
467
+ };
468
+ /** Depth-axis params. Only set for warp motion. */
469
+ warp?: {
470
+ ux: number;
471
+ uy: number;
472
+ z0: number;
473
+ vz: number;
474
+ cx: number;
475
+ cy: number;
476
+ };
477
+ /** Spline params (pts in px, shared per layer). Only set for path motion. */
478
+ path?: {
479
+ pts: Array<{
480
+ x: number;
481
+ y: number;
482
+ }>;
483
+ duration: number;
484
+ closed: boolean;
485
+ smooth: boolean;
486
+ phase: number;
487
+ offX: number;
488
+ offY: number;
489
+ };
280
490
  }
281
491
  interface Placed {
282
492
  x: number;
@@ -294,10 +504,43 @@ declare function alphaAt(e: Entity, t: number): number;
294
504
  declare function sizeAt(e: Entity, t: number): number;
295
505
  /** Analytic rotation angle at time `t` (ms) in radians. Pure. */
296
506
  declare function rotationAt(e: Entity, t: number): number;
507
+ /** Layer lifecycle alpha multiplier at time `t` (ms). Pure. 1 when `life` is unset. */
508
+ declare function lifeAlphaAt(life: {
509
+ enter?: number;
510
+ exit?: number;
511
+ fade?: number;
512
+ } | undefined, t: number): number;
297
513
  /** Analytic position of an entity at logical time `t` (ms). Pure & deterministic. */
298
514
  declare function positionAt(e: Entity, t: number, w: number, h: number): Placed;
515
+ /**
516
+ * Analytic heading (radians) at time `t`, from a finite difference of positionAt.
517
+ * Used to orient streak sprites. Returns null across a wrap seam (position jump
518
+ * larger than half the viewport) — callers should skip orientation that frame.
519
+ */
520
+ declare function headingAt(e: Entity, t: number, w: number, h: number): number | null;
299
521
  /** Time-varying sprite index for text/emoji cycling. Returns static index when cyclePeriod is 0. */
300
522
  declare function spriteIndexAt(e: Entity, t: number, variants: number): number;
523
+ /** An inter-entity link edge with its (wrap-aware) distance, for falloff alpha. */
524
+ interface LinkEdge {
525
+ i: number;
526
+ j: number;
527
+ dist: number;
528
+ }
529
+ /**
530
+ * Edge list for a layer's `links`, dispatching on mode:
531
+ * 'nearest' (default) k-nearest within maxDist; 'chain' sequential order
532
+ * (Mystify — ignores k/maxDist, `closed` joins last to first); 'random' a fixed
533
+ * golden-ratio-stride wiring filtered by maxDist (deterministic, no RNG draws).
534
+ */
535
+ declare function linkEdges(links: {
536
+ k: number;
537
+ maxDist: number;
538
+ mode?: 'nearest' | 'chain' | 'random';
539
+ closed?: boolean;
540
+ }, positions: Array<{
541
+ x: number;
542
+ y: number;
543
+ }>, maxDistPx: number, doWrap: boolean, w: number, h: number): LinkEdge[];
301
544
  /** K-nearest-neighbor edge list. Deterministic tie-break: (dist, index).
302
545
  * When `doWrap` is true, uses toroidal distance so links don't streak across wrap seams. */
303
546
  declare function linkPairs(positions: Array<{
@@ -416,6 +659,157 @@ declare function describeScene(spec: SaverSpec, options?: {
416
659
  times?: number[];
417
660
  }): SceneDescription;
418
661
 
662
+ interface PerceiveOptions {
663
+ viewport?: {
664
+ width: number;
665
+ height: number;
666
+ };
667
+ seed?: number;
668
+ /** Sample time in ms. Default 5000 (past all typical life.enter staging). */
669
+ t?: number;
670
+ }
671
+ interface LuminanceGrid {
672
+ cols: number;
673
+ rows: number;
674
+ /** Row-major luminance 0..1 including the background. */
675
+ cells: number[];
676
+ /** Per-row background luminance (what an empty scene would be). */
677
+ background: number[];
678
+ meanLuminance: number;
679
+ /** Fraction of cells deviating perceptibly (> 0.03) from the background. */
680
+ coverage: number;
681
+ /** Deviation-weighted centre of visual mass as viewport fractions, or null for a flat scene. */
682
+ centroid: {
683
+ x: number;
684
+ y: number;
685
+ } | null;
686
+ /** Mean deviation-from-background per row/column — 1D transects of the composition. */
687
+ rowProfile: number[];
688
+ colProfile: number[];
689
+ }
690
+ interface LuminanceGridOptions extends PerceiveOptions {
691
+ /** Grid resolution. Defaults 80×48 (→ a 40×12 braille map). */
692
+ cols?: number;
693
+ rows?: number;
694
+ }
695
+ /**
696
+ * Sample the spec into a coarse luminance image — analytically, no renderer.
697
+ * Approximations (documented, deliberate): trails and ghosting are ignored,
698
+ * soft circles use a linear falloff, background drift is sampled at its rest
699
+ * position. Good enough to perceive composition, focus, and balance.
700
+ */
701
+ declare function luminanceGrid(spec: SaverSpec, opts?: LuminanceGridOptions): LuminanceGrid;
702
+ /**
703
+ * Encode a luminance grid as a braille "image": each output character is a 2×4
704
+ * pixel cell, ordered-dithered so mid-tones render as dot density. A grid of
705
+ * 80×48 becomes 12 lines of 40 chars — a picture a text model can read whole.
706
+ *
707
+ * Auto-exposure: most savers live in the bottom of the luminance range, so the
708
+ * cells are normalized (min → 98th percentile) before dithering — like a
709
+ * camera exposing for the scene. Stats on the grid itself stay raw.
710
+ */
711
+ declare function renderBrailleMap(grid: LuminanceGrid): string;
712
+ interface DominanceEntry {
713
+ rank: number;
714
+ layerIndex: number;
715
+ key: string | undefined;
716
+ /** Normalized share of total visual weight, 0..1 (all entries sum to 1). */
717
+ share: number;
718
+ entityCount: number;
719
+ meanLuma: number;
720
+ factors: {
721
+ area: number;
722
+ contrast: number;
723
+ blendBoost: number;
724
+ motionBoost: number;
725
+ };
726
+ }
727
+ /**
728
+ * Rank layers by estimated visual weight — where the eye goes. Weight is
729
+ * on-screen area × alpha, scaled by contrast against the background, an
730
+ * additive-glow boost, and a motion boost. Link lines count as area.
731
+ */
732
+ declare function dominanceRanking(spec: SaverSpec, opts?: PerceiveOptions): DominanceEntry[];
733
+ interface LayerMotionStats {
734
+ layerIndex: number;
735
+ key: string | undefined;
736
+ /** Mean / max on-screen speed in px per second (wrap jumps excluded). */
737
+ meanSpeed: number;
738
+ maxSpeed: number;
739
+ moving: boolean;
740
+ }
741
+ /** Per-layer displacement between t and t+dt — choreography as numbers. */
742
+ declare function motionStats(spec: SaverSpec, opts?: PerceiveOptions & {
743
+ dt?: number;
744
+ }): LayerMotionStats[];
745
+ declare const REGION_NAMES: readonly ["top-left", "top-center", "top-right", "mid-left", "center", "mid-right", "bottom-left", "bottom-center", "bottom-right"];
746
+ interface SceneDiff {
747
+ t: number;
748
+ coverage: {
749
+ a: number;
750
+ b: number;
751
+ delta: number;
752
+ };
753
+ meanLuminance: {
754
+ a: number;
755
+ b: number;
756
+ delta: number;
757
+ };
758
+ /** Shift of the visual-mass centroid from A to B, in viewport fractions. */
759
+ balanceShift: {
760
+ dx: number;
761
+ dy: number;
762
+ } | null;
763
+ /** Mean deviation-from-background per third-by-third region, with deltas. */
764
+ regions: Array<{
765
+ region: (typeof REGION_NAMES)[number];
766
+ a: number;
767
+ b: number;
768
+ delta: number;
769
+ }>;
770
+ /** Dominance changes, matched by layer key (falls back to index). Sorted by |share delta|. */
771
+ dominance: Array<{
772
+ key: string;
773
+ rankA: number | null;
774
+ rankB: number | null;
775
+ shareA: number;
776
+ shareB: number;
777
+ }>;
778
+ advisories: {
779
+ added: string[];
780
+ removed: string[];
781
+ };
782
+ }
783
+ /**
784
+ * Compare two specs at the same time/viewport — relative perception. A text
785
+ * model is far better at "is B better than A" than "is A good"; this returns
786
+ * the deltas that question needs: coverage, balance, per-region visual mass,
787
+ * dominance-rank movement, and advisory changes.
788
+ */
789
+ declare function diffScenes(a: SaverSpec, b: SaverSpec, opts?: LuminanceGridOptions): SceneDiff;
790
+ interface ScenePerception {
791
+ t: number;
792
+ /** The picture: a braille luminance map (each char = 2×4 pixels). */
793
+ braille: string;
794
+ coverage: number;
795
+ meanLuminance: number;
796
+ centroid: {
797
+ x: number;
798
+ y: number;
799
+ } | null;
800
+ rowProfile: number[];
801
+ colProfile: number[];
802
+ dominance: DominanceEntry[];
803
+ motion: LayerMotionStats[];
804
+ advisories: ReturnType<typeof adviseSpec>;
805
+ }
806
+ /**
807
+ * Everything a non-vision agent needs to "see" a spec in one deterministic
808
+ * call: the braille picture, composition transects, dominance ranking, motion
809
+ * stats, and advisories. Intended as the payload behind an MCP previewScene.
810
+ */
811
+ declare function perceiveScene(spec: SaverSpec, opts?: LuminanceGridOptions): ScenePerception;
812
+
419
813
  /**
420
814
  * Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test
421
815
  * that the schema is actually expressive: two layers (fish drifting both ways at varied
@@ -433,7 +827,7 @@ declare const COMETS_SPEC: SaverSpec;
433
827
  /**
434
828
  * Constellation: drifting stars connected by faint k-nearest links. Exercises
435
829
  * colors[] (multi-hued stars), links (k-nearest neighbor lines), and cycle
436
- * (emoji asterisms rotate glyphs). Stars are soft circles in pixel units.
830
+ * (emoji asterisms rotate glyphs). Stars are soft circles in viewport units.
437
831
  */
438
832
  declare const CONSTELLATION_SPEC: SaverSpec;
439
833
 
@@ -455,23 +849,24 @@ declare const SNOWFALL_SPEC: SaverSpec;
455
849
  declare const LANTERNS_SPEC: SaverSpec;
456
850
 
457
851
  /**
458
- * Sakura Drift: cherry-blossom petals carried on a spring twilight breeze. Five
459
- * correlated-parallax layers build the depth — the far backdrop is a barely-moving
460
- * field of soft blossom-glow orbs (additive, breathing out of phase), then three
461
- * petal layers grow LARGER, FASTER, BRIGHTER and FLUTTER WIDER as they approach
462
- * (the near gust), over a blue-hour gradient with a warm rose horizon and a dark
463
- * grass band. A final low layer of dim pink glow settles at the grass line — fallen
464
- * petals catching the last light. Wind direction is consistent (down-left) across
465
- * every moving layer; petals differ from snowfall by glyph, colour, palette and the
466
- * diagonal breeze, and from lanterns by falling rather than rising.
852
+ * Sakura Drift: cherry-blossom petals carried on a spring twilight breeze.
853
+ * Glow-up pass (v1-ceiling features): petals ride `wander` harmonics with shared
854
+ * `coherence` so gusts move the whole fall together, `spin` tumbles the glyphs,
855
+ * faint `streak` wind-lines cross the frame, the blossom-glow backdrop shimmers
856
+ * as a traveling `pulse.wave`, and dusk fireflies wander the lower field. The
857
+ * ground is a warm plum earth blended out of the sunset (no more abrupt dark
858
+ * band), with slow background `drift` keeping the sky alive.
467
859
  */
468
860
  declare const SAKURA_SPEC: SaverSpec;
469
861
 
470
862
  /**
471
- * Dev Dashboard: a dense ops-style HUD exercising static positioning, keyed layers,
472
- * and text alignment at scale. Two ambient particle layers provide depth behind 28
473
- * pinned text elements arranged in a three-column layout with header, metrics,
474
- * file list, and scrolling event log.
863
+ * Control Center: a general-purpose ambient status HUD (home / ops / anything —
864
+ * not dev-specific). Exercises the static/HUD subset at scale plus the
865
+ * v1-ceiling features: `life` staggers the panels in like a boot sequence, a
866
+ * grid-`layout` LED wall breathes behind everything via a traveling
867
+ * `pulse.wave`, `ring` sprites form the central gauge, and a rect "equalizer"
868
+ * animates with a wave-phased pulse. Every panel is keyed so an agent can
869
+ * steer any reading live (`power-value`, `gauge-value`, `feed-1`, ...).
475
870
  */
476
871
  declare const DASHBOARD_SPEC: SaverSpec;
477
872
 
@@ -483,6 +878,44 @@ declare const DASHBOARD_SPEC: SaverSpec;
483
878
  */
484
879
  declare const ORRERY_SPEC: SaverSpec;
485
880
 
881
+ /**
882
+ * Aurora Borealis: the "fake fluid" recipe — showcases `wander` motion (harmonic
883
+ * drift) with high `coherence` so the curtain undulates as one body, `pulse.wave`
884
+ * (a traveling shimmer across the star field), weighted palettes, additive blend,
885
+ * and `ghosting` smearing the soft glows into continuous curtains of light.
886
+ */
887
+ declare const AURORA_SPEC: SaverSpec;
888
+
889
+ /**
890
+ * Matrix Rain: showcases `layout: grid` with per-axis jitter ({ y: 1 } keeps the
891
+ * columns crisp while de-syncing them vertically), glyph `cycle`, and `ghosting`
892
+ * doing the classic fading-trail work. Two depths of rain plus rare bright heads.
893
+ */
894
+ declare const MATRIX_RAIN_SPEC: SaverSpec;
895
+
896
+ /**
897
+ * Polygons: the classic Mystify-style bouncing-polygon saver, expressed as chain links + heavy
898
+ * ghosting. Showcases `links.mode: 'chain'` with `closed` (vertices wired in order
899
+ * into a polygon), `bounce` motion, and high `ghosting` (the decaying after-images
900
+ * ARE the effect — no per-entity trail sampling involved).
901
+ */
902
+ declare const POLYGONS_SPEC: SaverSpec;
903
+
904
+ /**
905
+ * Night Procession: a choreographed scene. Showcases `path` motion (a smooth
906
+ * figure-eight lantern route with scatter), layer-parented `orbit` (moths circling
907
+ * a wandering lantern-bearer), `life` staging (stars first, the procession enters
908
+ * at 4 s, moths at 7 s), plus `ring` and `rect` sprites.
909
+ */
910
+ declare const PROCESSION_SPEC: SaverSpec;
911
+
912
+ /**
913
+ * Warp Tunnel: showcases `warp` motion (perspective depth — size/alpha/speed scale
914
+ * with 1/z), `streak` sprites oriented along the analytic heading, `colorWeights`
915
+ * (mostly white stars, occasional blue/amber), and light `ghosting` for smear.
916
+ */
917
+ declare const WARP_TUNNEL_SPEC: SaverSpec;
918
+
486
919
  /** Catalog entry for a bundled schema example. */
487
920
  interface SchemaExample {
488
921
  id: string;
@@ -496,4 +929,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
496
929
  /** Lookup by saver id. */
497
930
  declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
498
931
 
499
- export { AQUARIUM_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COMETS_SPEC, CONSTELLATION_SPEC, type CycleSpec, DASHBOARD_SPEC, EXAMPLE_BY_ID, EXAMPLE_SPECS, type Entity, type GradientStop, LANTERNS_SPEC, LIMITS, type LayerSnapshot, type LayerSpec, type MotionSpec, ORRERY_SPEC, type Placed, RAIN_SPEC, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SNOWFALL_SPEC, type SaverSpec, type SceneDescription, type SceneSnapshot, type SchemaExample, type SpecError, type SpecWarning, type SpriteSpec, type SteerDelta, type ValidationResult, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSpec, buildEntities, compileSaver, describeScene, easeSmooth, lerpSpec, linkPairs, manifestFor, positionAt, resolveSpecPath, rotationAt, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, validateSpec };
932
+ export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COMETS_SPEC, CONSTELLATION_SPEC, type CycleSpec, DASHBOARD_SPEC, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SPECS, type Entity, type GradientStop, LANTERNS_SPEC, LIMITS, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, type MotionSpec, ORRERY_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type Placed, RAIN_SPEC, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SNOWFALL_SPEC, type SaverSpec, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type SpecError, type SpecWarning, type SpriteSpec, type SteerDelta, type ValidationResult, WARP_NEAR, WARP_TUNNEL_SPEC, type WanderOsc, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSpec, buildEntities, compileSaver, describeScene, diffScenes, dominanceRanking, easeSmooth, headingAt, lerpSpec, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, motionStats, perceiveScene, positionAt, renderBrailleMap, resolveSpecPath, rotationAt, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, validateSpec };