@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/FORMAT.md +157 -36
- package/dist/index.d.ts +458 -25
- package/dist/index.js +1777 -347
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/saver-spec.schema.json +185 -7
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
|
-
/**
|
|
26
|
-
|
|
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
|
|
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
|
-
/**
|
|
68
|
-
|
|
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
|
|
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
|
|
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.
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
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
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
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 };
|