@idle-screens/schema 2.1.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 +549 -28
- package/dist/index.js +2211 -371
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/saver-spec.schema.json +205 -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';
|
|
@@ -32,6 +46,7 @@ type BackgroundSpec = {
|
|
|
32
46
|
type: 'gradient';
|
|
33
47
|
stops: GradientStop[];
|
|
34
48
|
band?: BandSpec;
|
|
49
|
+
drift?: BackgroundDrift;
|
|
35
50
|
};
|
|
36
51
|
/** A vertical gradient stop (`at` 0 = top, 1 = bottom). */
|
|
37
52
|
interface GradientStop {
|
|
@@ -41,9 +56,16 @@ interface GradientStop {
|
|
|
41
56
|
/** An optional solid band at the bottom (e.g. an aquarium seafloor). */
|
|
42
57
|
interface BandSpec {
|
|
43
58
|
color: string;
|
|
44
|
-
/** Band height
|
|
59
|
+
/** Band height — px or a viewport fraction, per the spec's `units`. */
|
|
45
60
|
height: number;
|
|
46
61
|
}
|
|
62
|
+
/** Slow oscillation of gradient stop positions — makes the background feel alive. */
|
|
63
|
+
interface BackgroundDrift {
|
|
64
|
+
/** Full cycle period in ms. Floor: LIMITS.minDriftPeriod (10 s). */
|
|
65
|
+
period: number;
|
|
66
|
+
/** How far stops shift (fraction of the 0..1 range). Default 0.15, max 0.3. */
|
|
67
|
+
amount?: number;
|
|
68
|
+
}
|
|
47
69
|
interface LayerSpec {
|
|
48
70
|
count: number;
|
|
49
71
|
sprite: SpriteSpec;
|
|
@@ -56,8 +78,12 @@ interface LayerSpec {
|
|
|
56
78
|
flip?: boolean;
|
|
57
79
|
/** Per-entity opacity range, both 0..1. Default [1,1]. */
|
|
58
80
|
alpha?: [number, number];
|
|
59
|
-
/**
|
|
60
|
-
|
|
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';
|
|
61
87
|
/**
|
|
62
88
|
* Fractional spawn window (0 = left/top, 1 = right/bottom). Constrains where
|
|
63
89
|
* entities are PLACED, not where they may travel. Default full viewport.
|
|
@@ -71,10 +97,20 @@ interface LayerSpec {
|
|
|
71
97
|
* period has a floor (LIMITS.minPulsePeriod — max 2 Hz), and every entity gets
|
|
72
98
|
* its own seeded phase, so a layer can never strobe in unison. Effective alpha
|
|
73
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.
|
|
74
106
|
*/
|
|
75
107
|
pulse?: {
|
|
76
108
|
amp: number;
|
|
77
109
|
period: number;
|
|
110
|
+
wave?: {
|
|
111
|
+
wavelength: number;
|
|
112
|
+
angle?: number;
|
|
113
|
+
};
|
|
78
114
|
};
|
|
79
115
|
/**
|
|
80
116
|
* Per-entity rotation speed in degrees/sec (positive = clockwise).
|
|
@@ -104,7 +140,13 @@ interface LayerSpec {
|
|
|
104
140
|
y: number;
|
|
105
141
|
};
|
|
106
142
|
/**
|
|
107
|
-
* 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.
|
|
108
150
|
* Capped at LIMITS.maxLinksK. Layer count must be <= LIMITS.maxLinkLayerCount when set.
|
|
109
151
|
*/
|
|
110
152
|
links?: {
|
|
@@ -113,6 +155,42 @@ interface LayerSpec {
|
|
|
113
155
|
color?: string;
|
|
114
156
|
alpha?: number;
|
|
115
157
|
width?: number;
|
|
158
|
+
mode?: 'nearest' | 'chain' | 'random';
|
|
159
|
+
falloff?: boolean;
|
|
160
|
+
closed?: boolean;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Afterglow trail behind moving entities. Samples past positions analytically
|
|
164
|
+
* (no state, fully deterministic). `length` in ms, `fade` 0..1 (default 1 = full fade).
|
|
165
|
+
*/
|
|
166
|
+
trail?: {
|
|
167
|
+
length: number;
|
|
168
|
+
fade?: number;
|
|
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;
|
|
116
194
|
};
|
|
117
195
|
}
|
|
118
196
|
type SpriteSpec = {
|
|
@@ -129,13 +207,52 @@ type SpriteSpec = {
|
|
|
129
207
|
maxWidth?: number;
|
|
130
208
|
cycle?: CycleSpec;
|
|
131
209
|
}
|
|
132
|
-
/** `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. */
|
|
133
213
|
| {
|
|
134
214
|
kind: 'circle';
|
|
135
215
|
radius: [number, number];
|
|
136
216
|
color: string;
|
|
137
217
|
soft?: boolean;
|
|
138
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[];
|
|
139
256
|
};
|
|
140
257
|
/** Rotate through sprite variants over time. Each entity offsets by its seeded phase. */
|
|
141
258
|
interface CycleSpec {
|
|
@@ -174,7 +291,10 @@ type MotionSpec =
|
|
|
174
291
|
/**
|
|
175
292
|
* Orbit around a center point. Each entity gets a seeded radius from `radius`
|
|
176
293
|
* and a seeded phase. `speed` is angular velocity in degrees/sec. `center` is
|
|
177
|
-
* 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`.
|
|
178
298
|
*/
|
|
179
299
|
| {
|
|
180
300
|
type: 'orbit';
|
|
@@ -183,7 +303,59 @@ type MotionSpec =
|
|
|
183
303
|
center?: {
|
|
184
304
|
x: number;
|
|
185
305
|
y: number;
|
|
306
|
+
} | {
|
|
307
|
+
layer: string;
|
|
186
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;
|
|
187
359
|
};
|
|
188
360
|
/** A validation problem, pointing at a JSON path within the spec. */
|
|
189
361
|
interface SpecError {
|
|
@@ -193,6 +365,8 @@ interface SpecError {
|
|
|
193
365
|
interface ValidationResult {
|
|
194
366
|
valid: boolean;
|
|
195
367
|
errors: SpecError[];
|
|
368
|
+
/** Non-blocking warnings about unknown/misplaced properties or likely authoring mistakes. */
|
|
369
|
+
warnings?: SpecWarning[];
|
|
196
370
|
}
|
|
197
371
|
/** An advisory warning (non-blocking). Returned by `adviseSpec`. */
|
|
198
372
|
interface SpecWarning {
|
|
@@ -215,18 +389,46 @@ declare const LIMITS: {
|
|
|
215
389
|
readonly maxLinkLayerCount: 200;
|
|
216
390
|
readonly minCyclePeriod: 500;
|
|
217
391
|
readonly referenceViewport: 1080;
|
|
392
|
+
readonly maxTrailLength: 5000;
|
|
393
|
+
readonly maxTrailSamples: 24;
|
|
394
|
+
readonly minDriftPeriod: 10000;
|
|
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;
|
|
218
404
|
};
|
|
219
405
|
|
|
220
406
|
/**
|
|
221
407
|
* Validate an untrusted (agent- or JSON-authored) spec structurally + semantically.
|
|
222
|
-
* Returns typed errors; never throws. `compileSaver` refuses to run an invalid spec.
|
|
408
|
+
* Returns typed errors and warnings; never throws. `compileSaver` refuses to run an invalid spec.
|
|
409
|
+
*
|
|
410
|
+
* Warnings are non-blocking advisories about unknown/misplaced properties and
|
|
411
|
+
* near-zero speeds. They don't prevent compilation but indicate likely authoring mistakes.
|
|
223
412
|
*/
|
|
224
413
|
declare function validateSpec(spec: unknown): ValidationResult;
|
|
225
414
|
/** Narrowing helper: validate + cast. Throws with a joined message when invalid. */
|
|
226
415
|
declare function assertValidSpec(spec: unknown): SaverSpec;
|
|
227
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
|
+
}
|
|
228
428
|
/** A seeded entity: initial state + resolved velocity. `positionAt` is a pure function
|
|
229
|
-
* 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. */
|
|
230
432
|
interface Entity {
|
|
231
433
|
x0: number;
|
|
232
434
|
y0: number;
|
|
@@ -236,7 +438,7 @@ interface Entity {
|
|
|
236
438
|
vx: number;
|
|
237
439
|
vy: number;
|
|
238
440
|
bob: number;
|
|
239
|
-
motion: 'drift' | 'rise' | 'bounce' | 'static' | 'orbit';
|
|
441
|
+
motion: 'drift' | 'rise' | 'bounce' | 'static' | 'orbit' | 'wander' | 'warp' | 'path';
|
|
240
442
|
headingLeft: boolean;
|
|
241
443
|
alpha: number;
|
|
242
444
|
pulseAmp: number;
|
|
@@ -252,6 +454,39 @@ interface Entity {
|
|
|
252
454
|
orbitCy: number;
|
|
253
455
|
colorIndex: number;
|
|
254
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
|
+
};
|
|
255
490
|
}
|
|
256
491
|
interface Placed {
|
|
257
492
|
x: number;
|
|
@@ -260,18 +495,52 @@ interface Placed {
|
|
|
260
495
|
}
|
|
261
496
|
declare function spriteVariants(sprite: SpriteSpec): number;
|
|
262
497
|
/** Deterministically place a layer's entities using the seeded RNG (never Math.random).
|
|
263
|
-
* `scale` multiplies every dimensional draw (default 1). Used for viewport units.
|
|
264
|
-
|
|
498
|
+
* `scale` multiplies every dimensional draw (default 1). Used for viewport units.
|
|
499
|
+
* `countScale` multiplies entity count for density-aware scaling (default 1). */
|
|
500
|
+
declare function buildEntities(layer: LayerSpec, rng: Rng, w: number, h: number, scale?: number, countScale?: number): Entity[];
|
|
265
501
|
/** Analytic opacity of an entity at logical time `t` (ms). Pure, clamped to 0..1. */
|
|
266
502
|
declare function alphaAt(e: Entity, t: number): number;
|
|
267
503
|
/** Analytic size multiplier at time `t` (ms). Pure, clamped to > 0. */
|
|
268
504
|
declare function sizeAt(e: Entity, t: number): number;
|
|
269
505
|
/** Analytic rotation angle at time `t` (ms) in radians. Pure. */
|
|
270
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;
|
|
271
513
|
/** Analytic position of an entity at logical time `t` (ms). Pure & deterministic. */
|
|
272
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;
|
|
273
521
|
/** Time-varying sprite index for text/emoji cycling. Returns static index when cyclePeriod is 0. */
|
|
274
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[];
|
|
275
544
|
/** K-nearest-neighbor edge list. Deterministic tie-break: (dist, index).
|
|
276
545
|
* When `doWrap` is true, uses toroidal distance so links don't streak across wrap seams. */
|
|
277
546
|
declare function linkPairs(positions: Array<{
|
|
@@ -318,6 +587,16 @@ declare function applyDeltasToSpec(spec: SaverSpec, deltas: SteerDelta[]): Saver
|
|
|
318
587
|
* target immediately (k > 0). Layer counts round to integers.
|
|
319
588
|
*/
|
|
320
589
|
declare function lerpSpec(from: SaverSpec, to: SaverSpec, k: number): SaverSpec;
|
|
590
|
+
/**
|
|
591
|
+
* Enumerate all steerable leaf paths in a (resolved) spec. Returns dot-paths
|
|
592
|
+
* like "layers.0.count", "background.stops.1.color", etc. Metadata fields
|
|
593
|
+
* (id, label, schemaVersion, seed, units, kind, type, key) are excluded —
|
|
594
|
+
* they describe structure, not tuneable values.
|
|
595
|
+
*
|
|
596
|
+
* Layer keys are NOT substituted: paths always use numeric indices.
|
|
597
|
+
* Consumers can map to key-based paths via resolveSpecPath if needed.
|
|
598
|
+
*/
|
|
599
|
+
declare function steerablePaths(spec: unknown): string[];
|
|
321
600
|
/** Smooth (ease-in-out) progress curve used for glides. */
|
|
322
601
|
declare function easeSmooth(k: number): number;
|
|
323
602
|
/**
|
|
@@ -335,6 +614,202 @@ declare function adviseSpec(spec: SaverSpec, viewport?: {
|
|
|
335
614
|
height: number;
|
|
336
615
|
}): SpecWarning[];
|
|
337
616
|
|
|
617
|
+
interface LayerSnapshot {
|
|
618
|
+
key: string | undefined;
|
|
619
|
+
count: number;
|
|
620
|
+
centroid: {
|
|
621
|
+
x: number;
|
|
622
|
+
y: number;
|
|
623
|
+
};
|
|
624
|
+
coverage: number;
|
|
625
|
+
meanAlpha: number;
|
|
626
|
+
linksDrawn: number | null;
|
|
627
|
+
linksExpected: number | null;
|
|
628
|
+
connectedComponents: number | null;
|
|
629
|
+
isolatedNodes: number | null;
|
|
630
|
+
}
|
|
631
|
+
interface SceneSnapshot {
|
|
632
|
+
t: number;
|
|
633
|
+
viewport: {
|
|
634
|
+
w: number;
|
|
635
|
+
h: number;
|
|
636
|
+
};
|
|
637
|
+
layers: LayerSnapshot[];
|
|
638
|
+
warnings: string[];
|
|
639
|
+
}
|
|
640
|
+
interface SceneDescription {
|
|
641
|
+
spec: {
|
|
642
|
+
id: string;
|
|
643
|
+
label: string;
|
|
644
|
+
units: string;
|
|
645
|
+
};
|
|
646
|
+
snapshots: SceneSnapshot[];
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Deterministic scene analysis at one or more time values. Reuses `buildEntities`
|
|
650
|
+
* and `linkPairs` — no render needed. Returns structured data a non-vision model
|
|
651
|
+
* can use to understand what a spec looks like when rendered.
|
|
652
|
+
*/
|
|
653
|
+
declare function describeScene(spec: SaverSpec, options?: {
|
|
654
|
+
viewport?: {
|
|
655
|
+
width: number;
|
|
656
|
+
height: number;
|
|
657
|
+
};
|
|
658
|
+
seed?: number;
|
|
659
|
+
times?: number[];
|
|
660
|
+
}): SceneDescription;
|
|
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
|
+
|
|
338
813
|
/**
|
|
339
814
|
* Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test
|
|
340
815
|
* that the schema is actually expressive: two layers (fish drifting both ways at varied
|
|
@@ -342,10 +817,17 @@ declare function adviseSpec(spec: SaverSpec, viewport?: {
|
|
|
342
817
|
*/
|
|
343
818
|
declare const AQUARIUM_SPEC: SaverSpec;
|
|
344
819
|
|
|
820
|
+
/**
|
|
821
|
+
* Comet Shower: showcases trails (afterglow behind moving entities) and background
|
|
822
|
+
* drift (gradient stops breathing slowly). Three layers: a static star field, fast
|
|
823
|
+
* comets with long trails, and slow fireflies with short trails and pulse.
|
|
824
|
+
*/
|
|
825
|
+
declare const COMETS_SPEC: SaverSpec;
|
|
826
|
+
|
|
345
827
|
/**
|
|
346
828
|
* Constellation: drifting stars connected by faint k-nearest links. Exercises
|
|
347
829
|
* colors[] (multi-hued stars), links (k-nearest neighbor lines), and cycle
|
|
348
|
-
* (emoji asterisms rotate glyphs). Stars are soft circles in
|
|
830
|
+
* (emoji asterisms rotate glyphs). Stars are soft circles in viewport units.
|
|
349
831
|
*/
|
|
350
832
|
declare const CONSTELLATION_SPEC: SaverSpec;
|
|
351
833
|
|
|
@@ -367,23 +849,24 @@ declare const SNOWFALL_SPEC: SaverSpec;
|
|
|
367
849
|
declare const LANTERNS_SPEC: SaverSpec;
|
|
368
850
|
|
|
369
851
|
/**
|
|
370
|
-
* Sakura Drift: cherry-blossom petals carried on a spring twilight breeze.
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* every moving layer; petals differ from snowfall by glyph, colour, palette and the
|
|
378
|
-
* 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.
|
|
379
859
|
*/
|
|
380
860
|
declare const SAKURA_SPEC: SaverSpec;
|
|
381
861
|
|
|
382
862
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
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`, ...).
|
|
387
870
|
*/
|
|
388
871
|
declare const DASHBOARD_SPEC: SaverSpec;
|
|
389
872
|
|
|
@@ -395,6 +878,44 @@ declare const DASHBOARD_SPEC: SaverSpec;
|
|
|
395
878
|
*/
|
|
396
879
|
declare const ORRERY_SPEC: SaverSpec;
|
|
397
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
|
+
|
|
398
919
|
/** Catalog entry for a bundled schema example. */
|
|
399
920
|
interface SchemaExample {
|
|
400
921
|
id: string;
|
|
@@ -408,4 +929,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
|
|
|
408
929
|
/** Lookup by saver id. */
|
|
409
930
|
declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
|
|
410
931
|
|
|
411
|
-
export { AQUARIUM_SPEC, type BackgroundSpec, type BandSpec, CONSTELLATION_SPEC, type CycleSpec, DASHBOARD_SPEC, EXAMPLE_BY_ID, EXAMPLE_SPECS, type Entity, type GradientStop, LANTERNS_SPEC, LIMITS, type LayerSpec, type MotionSpec, ORRERY_SPEC, type Placed, RAIN_SPEC, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SNOWFALL_SPEC, type SaverSpec, type SchemaExample, type SpecError, type SpecWarning, type SpriteSpec, type SteerDelta, type ValidationResult, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSpec, buildEntities, compileSaver, easeSmooth, lerpSpec, linkPairs, manifestFor, positionAt, resolveSpecPath, rotationAt, sizeAt, spriteIndexAt, spriteVariants, 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 };
|