@idle-screens/schema 3.11.1 → 3.12.1
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 +77 -22
- package/dist/index.d.ts +113 -3
- package/dist/index.js +2059 -1475
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/FORMAT.md
CHANGED
|
@@ -192,8 +192,11 @@ exceeds 2 %, or a `dense` one that would have tripped `sparse-scene`, gets a
|
|
|
192
192
|
|
|
193
193
|
**Steering.** Every numeric knob and every band is a paint path —
|
|
194
194
|
`background.scale`, `background.warp`, `background.quantize`,
|
|
195
|
-
`background.bands.2`, `background.drift.amount` — and glides
|
|
196
|
-
|
|
195
|
+
`background.bands.2`, `background.drift.amount` — and glides. `quantize`
|
|
196
|
+
and `octaves` are level counts, so a glide (`setParam`, a `timeline` key, a
|
|
197
|
+
`morph`) steps through **whole** levels: the in-between value rounds, and a
|
|
198
|
+
`quantize` under 1.5 reads `0` (smooth) — so `quantize` 0 → 6 eases smooth,
|
|
199
|
+
then 2, 3 … 6 bands. Authored values must still be whole. `background.seed` is not
|
|
197
200
|
steerable. The background is paint, not structure, so a `morph` between
|
|
198
201
|
two field segments glides the field; a gradient → field morph steps
|
|
199
202
|
(different shape).
|
|
@@ -269,7 +272,7 @@ own `finish` is not steerable; steer a segment's, or republish.
|
|
|
269
272
|
|
|
270
273
|
| Field | Type | Default | Meaning |
|
|
271
274
|
|---|---|---|---|
|
|
272
|
-
| `count` | int 1..400 | — | entities in this layer |
|
|
275
|
+
| `count` | int 1..400 | — | entities in this layer. **At most 800 entities across all layers** (and ≤ 36 layers): the validator rejects a spec whose counts sum past 800, and density scaling never takes a screen past it |
|
|
273
276
|
| `sprite` | SpriteSpec | — | what each entity looks like |
|
|
274
277
|
| `motion` | MotionSpec | — | how entities move |
|
|
275
278
|
| `size` | `[min,max]` > 0 | sprite-dependent | font size for emoji/text; ignored for shaped sprites |
|
|
@@ -278,13 +281,13 @@ own `finish` is not steerable; steer a segment's, or republish.
|
|
|
278
281
|
| `alpha` | `[min,max]` 0..1 | `[1,1]` | per-entity opacity range |
|
|
279
282
|
| `blend` | `lighter` \| `screen` \| `multiply` | source-over | additive glow / gentle additive / darkening |
|
|
280
283
|
| `region` | `{x?, y?}` ranges 0..1 | full viewport | fractional spawn window (placement only, not travel) |
|
|
281
|
-
| `pulse` | `{amp ≤ 0.5, period ≥ 500, wave?}` | none | opacity breathing; `wave: {wavelength, angle?}` turns it into a traveling wave across the field |
|
|
282
|
-
| `spin` | number \| `[min,max]` ±360 deg/sec | none | per-entity rotation (seeded start angle); a range gives each entity a seeded speed (confetti, tumbling debris). `[0, 0]` does **not** hold the seeded angle — a zero speed renders at 0; use `rotate` |
|
|
284
|
+
| `pulse` | `{amp ≤ 0.5, period ≥ 500, wave?}` | none | opacity breathing: `amp` is **added** to the entity's `alpha` (alpha ± amp, clamped to 0..1), not a fraction of it — a dim layer (`alpha` 0.2, `amp` 0.3) swings through 0 and sits fully dark for part of every period; keep `amp` ≤ the alpha floor for a breath that never blacks out. `wave: {wavelength, angle?}` turns it into a traveling wave across the field |
|
|
285
|
+
| `spin` | number \| `[min,max]` ±360 deg/sec | none | per-entity rotation (seeded start angle — drawn from the scene's one shared seeded stream, so it is reproducible for a given spec, seed **and viewport** but not a fixed per-entity value: density scaling changes how many entities draw before it, and declaring a `rotate` range adds a draw per entity, so either one changes the angles; use `rotate` for an angle you need to pin); a range gives each entity a seeded speed (confetti, tumbling debris). `[0, 0]` does **not** hold the seeded angle — a zero speed renders at 0; use `rotate` |
|
|
283
286
|
| `rotate` | number \| `[min,max]` ±360 deg | none | **static** per-entity rotation: a scalar turns the whole layer, a range gives each entity a seeded angle (thrown blades, scattered glyphs, a tilted grid). Adds to `spin`'s start angle. Structural. **Native:** tvOS reads `spin` only ⇒ 0 |
|
|
284
287
|
| `grow` | `{amp ≤ 0.8, period ≥ 500}` | none | size breathing (seeded phase) |
|
|
285
|
-
| `trail` | `{length ≤ 5000, fade?}` | none | analytic afterglow trail; `length` is **milliseconds** of history (max 5000), `fade` a number 0..1 (not a boolean — `links.falloff` in the next row is the boolean) |
|
|
288
|
+
| `trail` | `{length ≤ 5000, fade?}` | none | analytic afterglow trail; `length` is **milliseconds** of history (max 5000), `fade` a number 0..1 (not a boolean — `links.falloff` in the next row is the boolean). Echoes of a shaped sprite (`polygon`, `stroke`, `rect`, `ring`, `streak`, `bar`) are the sprite's own shape as it was then, shrinking toward the tail; `circle`, `emoji` and text trail discs. A hard `circle`'s trail is one continuous ribbon tapering behind it, so a quick path draws a line rather than a string of dots (a layer whose ribbons would cover more than a tenth of the screen draws its echo discs instead, to bound the cost); a `soft` circle's echoes are filled in where they would part, within a small per-layer budget |
|
|
286
289
|
| `links` | see below | none | inter-entity lines |
|
|
287
|
-
| `layout` | `{type: "grid", columns?, jitter?}` \| `{type: "list", gap?}` \| `{type: "table", columns, gap?}` | scatter | `grid`: cells fill `region`, `jitter` scalar or `{x?, y?}` 0..1 per axis. **`list` / `table`: data layouts** — entities in reading order from `position` (any count) or centred in `region`, `gap` apart (viewport units of `min(w,h)`, default 0.06
|
|
290
|
+
| `layout` | `{type: "grid", columns?, jitter?}` \| `{type: "list", gap?}` \| `{type: "table", columns, gap?}` | scatter | `grid`: cells fill `region`, `jitter` scalar or `{x?, y?}` 0..1 per axis. **`list` / `table`: data layouts** — entities in reading order from `position` (any count) or centred in `region`, `gap` apart (viewport units of `min(w,h)`, default 0.06; a number or `{x?, y?}`). **`gap` is the pitch between cell anchors, not the space between cells** — a label wider than the column pitch runs into its neighbour (the `layout-text-overlap` advisory names the gap that clears it), so size `gap.x` to the widest label. Text/emoji take `strings[i]` / `glyphs[i]` **in order**, so N labels are one layer; a shaped sprite's palette (`colors[i]` on `bar`, `rect`, `circle`…) is read in order too. **Text and emoji have no `colors`**: a `text` layer paints every label in its one `color` (a `colors` on it is an unknown property, ignored with a warning) — use one layer per label colour |
|
|
288
291
|
| `life` | `{enter?, exit?, fade?}` ms | always on | act structure: fade the layer in at `enter`, out at `exit` |
|
|
289
292
|
| `emit` | `{every ≥ 1000, life ≥ 500, jitter?, grow?}` (`life ≤ every`) | always lit | **sparse events**: each entity is dark except a `life`-ms window every `every` ms, fading in fast and out slow; `jitter` 0 staggers entities evenly (one event at a time while `life ≤ every / count`), 1 scatters the offsets (a fixed sequence, not seeded — declaring `emit` disturbs no other draw); `grow: [from, to]` scales size across the window — expansion rather than travel |
|
|
290
293
|
| `clock` | `{phase?, rate?}` | seeded phases | **phase-lock**: `pulse`, `grow` and `cycle` share one phase (turns, 0..1) and run at `rate` × time; two layers with the same clock breathe in step. Clocked periods must satisfy `period / rate ≥ 1000` |
|
|
@@ -308,20 +311,24 @@ with distance, removing pop-in at the cutoff.
|
|
|
308
311
|
- `{ "kind": "text", "strings": ["HELLO"], "color": "#e6e8ef", "font": "bold monospace", "align": "center", "baseline": "middle", "maxWidth": 300, "cycle": ..., "role": "read" }`
|
|
309
312
|
— a `font` **with** a px size keeps that shorthand, its number rescaled to the
|
|
310
313
|
viewport (verbatim only under `units: "px"`); a family/weight only
|
|
311
|
-
(`"bold monospace"`) composes with the seeded per-entity `size
|
|
314
|
+
(`"bold monospace"`) composes with the seeded per-entity `size` (faces
|
|
315
|
+
installed on the viewer only — see `font` under `textBlock`); `role` is
|
|
312
316
|
the legibility opt-in described under `textBlock` below (same field, same
|
|
313
317
|
meaning on both text sprites)
|
|
314
318
|
- `{ "kind": "circle", "radius": [0.001, 0.003], "color": "#ffffff", "soft": true }` —
|
|
315
319
|
`soft` renders a radial-falloff glow orb instead of a hard disc
|
|
316
320
|
- `{ "kind": "ring", "radius": [0.002, 0.005], "color": "#d8f6ff", "width": 0.001 }` —
|
|
317
|
-
unfilled circle (bubbles, portals, sonar)
|
|
321
|
+
unfilled circle (bubbles, portals, sonar). The line is **centred on the
|
|
322
|
+
radius**: half of `width` falls inside, half outside, so the outer edge is
|
|
323
|
+
`radius + width / 2`
|
|
318
324
|
- `{ "kind": "streak", "length": [0.01, 0.03], "color": "#cfd8ff", "width": 0.002 }` —
|
|
319
325
|
a line oriented along the entity's analytic heading with a faded tail
|
|
320
326
|
(rain that reads as rain, shooting stars, warp stars)
|
|
321
327
|
- `{ "kind": "rect", "width": [0.012, 0.02], "aspect": [1.3, 1.7], "color": "#ffb347", "feather": 0.6 }` —
|
|
322
328
|
rectangle; `aspect` is the height/width ratio range (rotates with `spin`);
|
|
323
329
|
`feather` 0..1 softens the edges — that fraction of the half-size fades out
|
|
324
|
-
toward the border (Rothko's block
|
|
330
|
+
toward the border in one smooth linear falloff on each axis (Rothko's block
|
|
331
|
+
at 0.5–0.8)
|
|
325
332
|
- `{ "kind": "bar", "values": [82, 64, 91], "max": 100, "length": 0.5, "thickness": 0.02, "color": "#17e8c8", "direction": "right" }` —
|
|
326
333
|
a data bar: entity *i* draws `length × values[i] / max` (viewport units),
|
|
327
334
|
`thickness` thick, growing from its position toward `direction` (`right`
|
|
@@ -334,14 +341,22 @@ with distance, removing pop-in at the cutoff.
|
|
|
334
341
|
6); or `"points": [[-1, 0.9], [0.2, -1], [1, 0.4]]` — 3..24 unit
|
|
335
342
|
coordinates in −1..1 scaled by the radius — for any facet (a Picasso shard,
|
|
336
343
|
a Kandinsky triangle, a Mondrian plane). `soft` feathers the fill from the
|
|
337
|
-
centre. Rotates with `spin`.
|
|
338
|
-
|
|
344
|
+
centre. Rotates with `spin`. The outline fills with the **nonzero** winding
|
|
345
|
+
rule: a self-crossing outline paints both lobes, and a hole needs its inner
|
|
346
|
+
loop traced in the **opposite** direction (a keyhole: outer ring clockwise,
|
|
347
|
+
then back round the hole counter-clockwise, joined by a zero-width slit). `points` are paint, so two polygons with the
|
|
348
|
+
same point count `morph` into each other — and a `setParam`, `timeline` key
|
|
349
|
+
or `morph` target for `sprite.points` is in the **same unit coordinates**,
|
|
350
|
+
scaled by each entity's seeded radius (not by the keyed shape's own extent):
|
|
351
|
+
keep every keyed point inside −1..1 (the validator rejects one outside), and
|
|
352
|
+
shrink a target shape by scaling its points, not by exceeding the box
|
|
339
353
|
- `{ "kind": "stroke", "length": [0.08, 0.18], "points": [[-1, 0.3], [-0.5, -0.6], [0.3, -0.7], [1, 0.1]], "width": 0.004, "taper": true, "color": "#f2e8c9" }` —
|
|
340
354
|
a freehand mark: a path through 2..24 unit-coordinate points, scaled so the
|
|
341
355
|
unit box spans the seeded `length`, stroked `width` wide with round joins.
|
|
342
356
|
`curve: "smooth"` (default) is a Catmull-Rom spline, `"linear"` a polyline;
|
|
343
357
|
`taper` thins the mark to almost nothing at both ends (a brush stroke rather
|
|
344
|
-
than a line
|
|
358
|
+
than a line; it paints as one filled mark, so a translucent stroke has no
|
|
359
|
+
overlap beads); `orient: true` turns the path's +x along the entity's heading,
|
|
345
360
|
like `streak`. Rotates with `spin`. Van Gogh's stroke, Hokusai's contour,
|
|
346
361
|
O'Keeffe's petal, Basquiat's scrawl
|
|
347
362
|
- `{ "kind": "textBlock", "text": "Multi-line text with wrapping.", "maxWidth": 0.8, "fontSize": 0.04, "lineHeight": 1.4, "align": "left", "color": "#e6e8ef", "anchor": "top-left", "font": "system-ui, sans-serif", "opacity": 1 }` —
|
|
@@ -384,6 +399,12 @@ with distance, removing pop-in at the cutoff.
|
|
|
384
399
|
advance so mono wraps land where the real face puts them; every other
|
|
385
400
|
family uses the proportional table. Default `system-ui, sans-serif`.
|
|
386
401
|
**Native clients fall back to the system face** until a face table exists.
|
|
402
|
+
`font` names faces **installed on the viewing device** — nothing is
|
|
403
|
+
bundled and no web font is fetched, so `'Inter'` only appears where Inter
|
|
404
|
+
is installed and every other screen falls through the stack. End every
|
|
405
|
+
stack in a generic `sans-serif`, `serif` or `monospace`; avoid ending on
|
|
406
|
+
`cursive` or `fantasy`, which resolve to a different (sometimes absurd)
|
|
407
|
+
face on every platform.
|
|
387
408
|
|
|
388
409
|
`opacity` (0–1, default 1) multiplies the block's paint alpha — the whole
|
|
389
410
|
block, on top of the layer's per-entity `alpha`. It is **paint**, not
|
|
@@ -471,13 +492,18 @@ and configured the second.
|
|
|
471
492
|
- `{ "type": "bounce", "speed": [0.12, 0.24] }` — diagonal, reflecting off edges
|
|
472
493
|
- `{ "type": "static" }` — stays exactly where placed (use with `position` for HUD text)
|
|
473
494
|
- `{ "type": "orbit", "speed": [10, 40], "radius": [0.05, 0.2], "center": { "x": 0.5, "y": 0.5 } }`
|
|
474
|
-
— angular speed in deg/sec;
|
|
475
|
-
|
|
476
|
-
|
|
495
|
+
— angular speed in deg/sec; a **negative** speed orbits the other way
|
|
496
|
+
(counter-rotating rings, meshing gears); `center` may instead be
|
|
497
|
+
`{ "layer": "planet" }` to orbit a single-entity parent layer (moons around a
|
|
498
|
+
wandering planet; strictly one level deep — and the parent must have
|
|
499
|
+
`count: 1`)
|
|
477
500
|
- `{ "type": "wander", "speed": [0.004, 0.012], "angle": 0, "meander": 0.1, "coherence": 0.7 }`
|
|
478
501
|
— organic harmonic drift: base velocity plus 3 seeded sine octaves per axis.
|
|
479
502
|
Flowing curved paths with zero simulation state. `meander` scales the
|
|
480
|
-
curvature
|
|
503
|
+
curvature (it is the harmonics' amplitude, so it also sets how fast an
|
|
504
|
+
entity sways — keep it ≤ ~0.015 for a slow drift); it is capped at
|
|
505
|
+
**0.463** (500 px ÷ `referenceViewport`, 1080 by default — 500 under
|
|
506
|
+
`units: "px"`), and the validator rejects anything above; `coherence` 0..1 blends every entity toward a shared harmonic set
|
|
481
507
|
(1 = the layer undulates as one body — fake flocking)
|
|
482
508
|
- `{ "type": "warp", "speed": [0.1, 0.5], "center": { "x": 0.5, "y": 0.5 } }`
|
|
483
509
|
— perspective starfield: entities stream toward the viewer on a depth axis,
|
|
@@ -486,7 +512,10 @@ and configured the second.
|
|
|
486
512
|
- `{ "type": "path", "points": [{"x":0.1,"y":0.7}, ...], "duration": 30000, "curve": "smooth", "closed": true, "scatter": 0.02 }`
|
|
487
513
|
— choreographed spline (Catmull-Rom, or `linear`); 2..24 waypoints, lap time
|
|
488
514
|
≥ 2 s; `closed` loops (default), open paths ping-pong; `scatter` offsets
|
|
489
|
-
entities sharing the path
|
|
515
|
+
entities sharing the path (**must be > 0** — omit it for an exact path
|
|
516
|
+
rather than writing `0`); each entity gets a seeded phase along it. A single
|
|
517
|
+
entity cannot show a curve — use `count` ≥ 12 (plus `trail`/`ghosting`) to
|
|
518
|
+
make the whole path read
|
|
490
519
|
|
|
491
520
|
All speeds are ranges; each entity draws its own value (seeded).
|
|
492
521
|
|
|
@@ -697,7 +726,9 @@ its drift, orbit and pulse run on (from mount; a sequence segment's `localT`):
|
|
|
697
726
|
```
|
|
698
727
|
|
|
699
728
|
- A key glides `path` from wherever it is at `t` to `value` over `dur` ms
|
|
700
|
-
(default 1000, the live-steer default; `0` is a cut
|
|
729
|
+
(default 1000, the live-steer default; `0` is a cut; at most 30000 — a
|
|
730
|
+
longer `dur` fails validation for the whole spec, so split a slower glide
|
|
731
|
+
into consecutive keys of ≤ 30 s each), eased `smooth`
|
|
701
732
|
(default), `linear` or `step` (holds, then switches at the glide's end).
|
|
702
733
|
Keys address the same dot-paths as `setParam` — index or `key` form — and a
|
|
703
734
|
path must already exist on the spec (a key cannot add a field). The spec must
|
|
@@ -853,11 +884,14 @@ moves and scales its positions and sizes (sprite rotation is not modelled).
|
|
|
853
884
|
its own × its group's — is 0 in the base scene and at every key boundary of
|
|
854
885
|
its `timeline` never shows, so it is not judged and its entities do not count
|
|
855
886
|
toward `dense-scene`; coverage is weighed by paint opacity) (also on
|
|
856
|
-
`perceiveScene().advisories`).
|
|
887
|
+
`perceiveScene().advisories`). Three of them are **spatial**, for static text:
|
|
857
888
|
`text-off-screen` (a text/textBlock box crosses the viewport edge by more
|
|
858
|
-
than 1%)
|
|
889
|
+
than 1%), `text-overlap` (two static text layers share more than 10% of
|
|
859
890
|
the smaller box; it carries both boxes as `boxes: [{x, y, w, h}]` in
|
|
860
|
-
viewport fractions so the fix needs no re-derivation)
|
|
891
|
+
viewport fractions so the fix needs no re-derivation) and
|
|
892
|
+
`layout-text-overlap` (two cells of one `list`/`table` layer do — its
|
|
893
|
+
`gap` pitch is narrower than its labels; the message names the `gap` that
|
|
894
|
+
clears them, and it carries the two boxes too). Boxes come from the
|
|
861
895
|
same character-class width table the textBlock line-breaker uses, so they
|
|
862
896
|
are estimates of the renderer's own layout — a caption that fails these
|
|
863
897
|
will look wrong on the wall; one that passes may still sit a few px off.
|
|
@@ -891,6 +925,27 @@ is estimated from font size × string length. Good enough to perceive
|
|
|
891
925
|
composition, focus, balance, and motion — not a substitute for a human (or VLM)
|
|
892
926
|
judgement of beauty.
|
|
893
927
|
|
|
928
|
+
**Motion comes in both units.** `motion[].meanSpeed` / `maxSpeed` are px per
|
|
929
|
+
second at the perceived size; `meanSpeedVp` / `maxSpeedVp` are the same speeds
|
|
930
|
+
in viewport units per second (fractions of `min(w,h)` — the unit specs are
|
|
931
|
+
authored in), which do not change with the preview size; `1.0` crosses the
|
|
932
|
+
short side of the screen every second. `moving` is judged in viewport units,
|
|
933
|
+
so it gives the same answer at every size. Each layer also carries `heading` (screen degrees: 0 right, 90 down) and
|
|
934
|
+
`headingSpread` (0 = a conveyor, everything moving the same way — the
|
|
935
|
+
"screen-wipe" to avoid; 1 = scattered). Sampling a centroid over time gives the
|
|
936
|
+
WRONG sign for a wrapping full-viewport scatter, which is why heading is
|
|
937
|
+
reported directly.
|
|
938
|
+
|
|
939
|
+
**Colour is its own channel.** `perceiveScene(...).colour` and
|
|
940
|
+
`diffScenes(...).colour` carry the hue / saturation / warmth (red minus blue,
|
|
941
|
+
−1 cold … 1 warm) of the alpha × area weighted sprite mass and of the
|
|
942
|
+
background plate. Judge "warmer / cooler" on `colour.warmthDelta`, not on
|
|
943
|
+
`meanLuminance` — plausible cool tones weigh MORE under luma than the reds they
|
|
944
|
+
replace, so a calming edit pushes luminance up. `diffScenes` also reports a
|
|
945
|
+
`motion` channel (overall and per-layer speed deltas with headings) and
|
|
946
|
+
advises at the diff's own viewport, so its `advisories.added/removed` agree
|
|
947
|
+
with `adviseSpec` at that size.
|
|
948
|
+
|
|
894
949
|
## Sequence envelope (`idle-sequence`)
|
|
895
950
|
|
|
896
951
|
A second top-level format that composes multiple SaverSpecs into a sequenced
|
package/dist/index.d.ts
CHANGED
|
@@ -992,6 +992,25 @@ declare function assertValidSpec(spec: unknown): SaverSpec;
|
|
|
992
992
|
declare function validateSequence(seq: unknown): ValidationResult;
|
|
993
993
|
declare function assertValidSequence(seq: unknown): IdleSequence;
|
|
994
994
|
|
|
995
|
+
/**
|
|
996
|
+
* The two scale factors every entity builder must agree on, computed in ONE
|
|
997
|
+
* place. `scale` turns viewport-unit dimensions into px (1 for `units: "px"`,
|
|
998
|
+
* min(w,h) otherwise — the default is viewport, so an omitted `units` scales);
|
|
999
|
+
* `countScale` thins or thickens scatter density against `referenceViewport`
|
|
1000
|
+
* and is clamped so the projected total never exceeds LIMITS.maxTotal.
|
|
1001
|
+
*
|
|
1002
|
+
* Four callers (compile, perceive, advise, describe) each carried their own
|
|
1003
|
+
* copy of this arithmetic and they drifted twice: describeScene inverted the
|
|
1004
|
+
* units default and built every shipped example at scale 1 (F48), and
|
|
1005
|
+
* advise/describe skipped the maxTotal clamp so above ~4.6K they measured a
|
|
1006
|
+
* scene the renderer never draws — and because the RNG stream is shared across
|
|
1007
|
+
* layers, one early layer's different count shifts every later layer's
|
|
1008
|
+
* positions too (F49).
|
|
1009
|
+
*/
|
|
1010
|
+
declare function entityScales(spec: SaverSpec, w: number, h: number): {
|
|
1011
|
+
scale: number;
|
|
1012
|
+
countScale: number;
|
|
1013
|
+
};
|
|
995
1014
|
/** Near plane for warp motion. z lives in [WARP_NEAR, 1]; screen scale is 1/z. */
|
|
996
1015
|
declare const WARP_NEAR = 0.08;
|
|
997
1016
|
/** Cap on the 1/z size multiplier so near stars don't blow out. */
|
|
@@ -1360,7 +1379,8 @@ declare function applyDeltasToSpec(spec: SaverSpec, deltas: SteerDelta[]): Saver
|
|
|
1360
1379
|
/**
|
|
1361
1380
|
* Interpolate between two structurally-similar specs at progress k (0..1).
|
|
1362
1381
|
* Numbers lerp, hex colours lerp per channel, everything else steps to the
|
|
1363
|
-
* target immediately (k > 0). Layer counts
|
|
1382
|
+
* target immediately (k > 0). Layer counts, and a field background's
|
|
1383
|
+
* `octaves` / `quantize`, round to whole values.
|
|
1364
1384
|
*/
|
|
1365
1385
|
declare function lerpSpec(from: SaverSpec, to: SaverSpec, k: number): SaverSpec;
|
|
1366
1386
|
/**
|
|
@@ -1694,10 +1714,32 @@ declare function layerCohesion(spec: SaverSpec, opts?: PerceiveOptions): LayerCo
|
|
|
1694
1714
|
interface LayerMotionStats {
|
|
1695
1715
|
layerIndex: number;
|
|
1696
1716
|
key: string | undefined;
|
|
1697
|
-
/** Mean / max on-screen speed in px per second (wrap jumps excluded). */
|
|
1717
|
+
/** Mean / max on-screen speed in px per second at the perceived size (wrap jumps excluded). */
|
|
1698
1718
|
meanSpeed: number;
|
|
1699
1719
|
maxSpeed: number;
|
|
1720
|
+
/**
|
|
1721
|
+
* The same speeds in **viewport units per second** — fractions of
|
|
1722
|
+
* min(w, h), the unit a spec's `speed` is authored in — so they do not
|
|
1723
|
+
* change with the size the scene is perceived at. 1.0 crosses the short side
|
|
1724
|
+
* of the screen every second.
|
|
1725
|
+
*/
|
|
1726
|
+
meanSpeedVp: number;
|
|
1727
|
+
maxSpeedVp: number;
|
|
1728
|
+
/** meanSpeedVp above MOVING_FLOOR — the same answer at every preview size. */
|
|
1700
1729
|
moving: boolean;
|
|
1730
|
+
/**
|
|
1731
|
+
* Direction the layer's mass is going, in screen degrees (0 = right,
|
|
1732
|
+
* 90 = down, 180 = left, 270 = up), or null when nothing moves. Sampling a
|
|
1733
|
+
* centroid over time gives the WRONG sign for a wrapping full-viewport
|
|
1734
|
+
* scatter, which is why this is reported directly.
|
|
1735
|
+
*/
|
|
1736
|
+
heading: number | null;
|
|
1737
|
+
/**
|
|
1738
|
+
* 0 = every entity moves the same way (a conveyor — the "screen-wipe" the
|
|
1739
|
+
* schema warns about), 1 = headings cancel out (a scatter, an orbit, a
|
|
1740
|
+
* bounce field). |mean unit vector| inverted.
|
|
1741
|
+
*/
|
|
1742
|
+
headingSpread: number | null;
|
|
1701
1743
|
}
|
|
1702
1744
|
/**
|
|
1703
1745
|
* Per-layer displacement between t and t+dt — choreography as numbers.
|
|
@@ -1711,6 +1753,34 @@ interface LayerMotionStats {
|
|
|
1711
1753
|
declare function motionStats(spec: SaverSpec, opts?: PerceiveOptions & {
|
|
1712
1754
|
dt?: number;
|
|
1713
1755
|
}): LayerMotionStats[];
|
|
1756
|
+
interface SceneColour {
|
|
1757
|
+
/** Hue of the visible sprite mass in degrees (0 red, 120 green, 240 blue), null when unsaturated. */
|
|
1758
|
+
hue: number | null;
|
|
1759
|
+
/** HSL saturation 0..1 of the sprite mass. */
|
|
1760
|
+
saturation: number;
|
|
1761
|
+
/** red minus blue of the mean sprite colour, -1 (cold) .. 1 (warm). The "cooler/warmer" axis. */
|
|
1762
|
+
warmth: number;
|
|
1763
|
+
/** Mean sprite colour as hex, for a model that reads hex better than angles. */
|
|
1764
|
+
hex: string;
|
|
1765
|
+
/** The same three numbers for the background plate. */
|
|
1766
|
+
background: {
|
|
1767
|
+
hue: number | null;
|
|
1768
|
+
saturation: number;
|
|
1769
|
+
warmth: number;
|
|
1770
|
+
hex: string;
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* What colour the scene IS — the alpha × area weighted mean of every hex a
|
|
1775
|
+
* sprite paints with, beside the background plate. A luminance map cannot
|
|
1776
|
+
* carry this: a luma-matched red→cyan swap moves every other channel by
|
|
1777
|
+
* ~0.001 (comprehension eval 2026-07-28, P2 F3), and `meanLuminance` reads
|
|
1778
|
+
* plausible cool tones as *brighter* than the reds they replace, so "make it
|
|
1779
|
+
* cooler" pushed the only number an agent had in the wrong direction. Emoji
|
|
1780
|
+
* carry their own palette and are excluded; a scene of only emoji reports the
|
|
1781
|
+
* background.
|
|
1782
|
+
*/
|
|
1783
|
+
declare function sceneColour(spec: SaverSpec, opts?: PerceiveOptions): SceneColour;
|
|
1714
1784
|
declare const REGION_NAMES: readonly ["top-left", "top-center", "top-right", "mid-left", "center", "mid-right", "bottom-left", "bottom-center", "bottom-right"];
|
|
1715
1785
|
interface SceneDiff {
|
|
1716
1786
|
t: number;
|
|
@@ -1744,6 +1814,44 @@ interface SceneDiff {
|
|
|
1744
1814
|
shareA: number;
|
|
1745
1815
|
shareB: number;
|
|
1746
1816
|
}>;
|
|
1817
|
+
/**
|
|
1818
|
+
* The hue channel. `hueShift` is the signed shortest rotation A→B in
|
|
1819
|
+
* degrees; `warmthDelta` is the cooler/warmer axis (negative = B is cooler)
|
|
1820
|
+
* — read THIS for "cooler", not `meanLuminance`, which weights cool tones
|
|
1821
|
+
* brighter than the reds they replace.
|
|
1822
|
+
*/
|
|
1823
|
+
colour: {
|
|
1824
|
+
a: SceneColour;
|
|
1825
|
+
b: SceneColour;
|
|
1826
|
+
hueShift: number | null;
|
|
1827
|
+
saturationDelta: number;
|
|
1828
|
+
warmthDelta: number;
|
|
1829
|
+
background: {
|
|
1830
|
+
hueShift: number | null;
|
|
1831
|
+
warmthDelta: number;
|
|
1832
|
+
};
|
|
1833
|
+
};
|
|
1834
|
+
/**
|
|
1835
|
+
* The motion channel, in viewport units per second. `overall` is the
|
|
1836
|
+
* entity-weighted mean across layers; `layers` lists every layer whose speed
|
|
1837
|
+
* changed, largest change first. Halving every speed used to leave the diff
|
|
1838
|
+
* bit-identical (comprehension eval 2026-07-28, P2 F4).
|
|
1839
|
+
*/
|
|
1840
|
+
motion: {
|
|
1841
|
+
overall: {
|
|
1842
|
+
a: number;
|
|
1843
|
+
b: number;
|
|
1844
|
+
delta: number;
|
|
1845
|
+
};
|
|
1846
|
+
layers: Array<{
|
|
1847
|
+
key: string;
|
|
1848
|
+
a: number;
|
|
1849
|
+
b: number;
|
|
1850
|
+
delta: number;
|
|
1851
|
+
headingA: number | null;
|
|
1852
|
+
headingB: number | null;
|
|
1853
|
+
}>;
|
|
1854
|
+
};
|
|
1747
1855
|
advisories: {
|
|
1748
1856
|
added: string[];
|
|
1749
1857
|
removed: string[];
|
|
@@ -1778,6 +1886,8 @@ interface ScenePerception {
|
|
|
1778
1886
|
motion: LayerMotionStats[];
|
|
1779
1887
|
/** Per layer: merged silhouette, seamed overlap, or separate marks. */
|
|
1780
1888
|
form: LayerCohesion[];
|
|
1889
|
+
/** What colour the scene is — hue/saturation/warmth of the sprite mass and the plate. */
|
|
1890
|
+
colour: SceneColour;
|
|
1781
1891
|
/** Literal strings + sizes of any text layers (glyphs don't show in the maps). */
|
|
1782
1892
|
text: TextSpriteInfo[];
|
|
1783
1893
|
advisories: ReturnType<typeof adviseSpec>;
|
|
@@ -2568,4 +2678,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
|
|
|
2568
2678
|
/** Lookup by saver id. */
|
|
2569
2679
|
declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
|
|
2570
2680
|
|
|
2571
|
-
export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COHESION_T, COMETS_SPEC, CONSTELLATION_SPEC, type CapabilityTier, type CohesionRead, type CycleSpec, DASHBOARD_SPEC, DEFAULT_KEY_DUR, DITHER_ALPHA, DITHER_TILE, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SEQUENCES, EXAMPLE_SPECS, type Entity, FACETS_SPEC, FIELD_BUCKET_MS, FIELD_DRIFT_DEFAULT_AMOUNT, FIELD_RASTER_SHORT_SIDE, FIELD_RASTER_SHORT_SIDE_LOW, FINISH_ANIMATE_HZ, type FieldBackground, type FieldDrift, type FinishPass, type FinishSpec, GRAIN_ALPHA, GRAIN_TILE, type GradientStop, HAIKU_SPEC, type IdleSequence, LANTERNS_SPEC, LIMITS, LOBBY_TALK_SPEC, type LayerCohesion, type LayerGroup, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LayerTransform, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, MURMUR_SPEC, type MotionEase, type MotionSpec, NOSTALGHIA_CANDLE_SPEC, ORRERY_SPEC, PHASE_DUET_SPEC, PINGS_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type PerceiveSequenceOptions, type Placed, RAIN_SPEC, RELAY_BOARD_SPEC, type ResolveOptions, type ResolvedSegment, type RevealState, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SHARD_FALL_SPEC, SIGNAL_BOARD_SPEC, SNOWFALL_SPEC, SPARSE_NIGHT_SPEC, type SaverSpec, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type ScreenOp, type SequenceExample, type SequenceFramePerception, type SequenceMountContext, type SequenceSaverInstance, type SequenceSaverPlugin, type SequenceSegment, type SequenceTransition, type SpecError, type SpecMountContext, type SpecWarning, type SpriteSpec, type SteerDelta, THERMAL_FIELD_SPEC, THREE_MOVEMENTS_SEQUENCE, TYPED_CUE_SPEC, type TextBlockAnchor, type TextBlockLine, type TextMetricsClass, type TextRevealInput, type TextRevealSpec, type TextRole, type TextSpriteInfo, type Timeline, type TimelineKey, type ValidationResult, WARP_MAX_SCALE, WARP_NEAR, WARP_TUNNEL_SPEC, WEB_WORK_SPEC, type WanderOsc, type WarningBox, adviseSequence, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, bayerMatrix, bayerTilePixels, bedRenderSeed, breakTextBlock, buildEntities, canMorph, canWrapMorph, canonicalSpecPath, cohesionOf, compileSaver, compileSequence, createFinishPass, createSceneCanvas, describeScene, diffScenes, dominanceRanking, easeSmooth, emitEnvelope, emitWindow, fieldAt, fieldOctaves, fieldRgb, fieldRgbAt, fieldSampleTime, finishStrength, grainOffset, grainTilePixels, graphemeClusters, hasHotSwapSequence, headingAt, hexToRgb255, ignoredPropertyPaths, latticeHash, layerCohesion, lerpSpec, lerpValue, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, morphChainRoot, morphNothingMorphable, motionStats, nextKeyAfter, normalizeSeed, perceiveScene, perceiveSequenceFrame, pickScreenOp, positionAt, presentWithFinish, readSpecPath, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, resolveTimelineAt, revealState, rgb255Luma, rotationAt, seamCauseOf, seamsWorthWarning, segmentRenderSeed, segmentStart, sequenceSteerablePaths, sequenceSwapCompatible, sequenceWantsFinish, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textBlockAnchorOffset, textMetricsClassFor, textSprites, textStringsDiffer, textStringsOf, textWidthEm, timelineLocalTime, timelineSampleTimes, timelineSampleTimesAfter, timelineTracks, validateSequence, validateSpec, validateSpecPaths, withoutTimeline };
|
|
2681
|
+
export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COHESION_T, COMETS_SPEC, CONSTELLATION_SPEC, type CapabilityTier, type CohesionRead, type CycleSpec, DASHBOARD_SPEC, DEFAULT_KEY_DUR, DITHER_ALPHA, DITHER_TILE, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SEQUENCES, EXAMPLE_SPECS, type Entity, FACETS_SPEC, FIELD_BUCKET_MS, FIELD_DRIFT_DEFAULT_AMOUNT, FIELD_RASTER_SHORT_SIDE, FIELD_RASTER_SHORT_SIDE_LOW, FINISH_ANIMATE_HZ, type FieldBackground, type FieldDrift, type FinishPass, type FinishSpec, GRAIN_ALPHA, GRAIN_TILE, type GradientStop, HAIKU_SPEC, type IdleSequence, LANTERNS_SPEC, LIMITS, LOBBY_TALK_SPEC, type LayerCohesion, type LayerGroup, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LayerTransform, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, MURMUR_SPEC, type MotionEase, type MotionSpec, NOSTALGHIA_CANDLE_SPEC, ORRERY_SPEC, PHASE_DUET_SPEC, PINGS_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type PerceiveSequenceOptions, type Placed, RAIN_SPEC, RELAY_BOARD_SPEC, type ResolveOptions, type ResolvedSegment, type RevealState, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SHARD_FALL_SPEC, SIGNAL_BOARD_SPEC, SNOWFALL_SPEC, SPARSE_NIGHT_SPEC, type SaverSpec, type SceneColour, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type ScreenOp, type SequenceExample, type SequenceFramePerception, type SequenceMountContext, type SequenceSaverInstance, type SequenceSaverPlugin, type SequenceSegment, type SequenceTransition, type SpecError, type SpecMountContext, type SpecWarning, type SpriteSpec, type SteerDelta, THERMAL_FIELD_SPEC, THREE_MOVEMENTS_SEQUENCE, TYPED_CUE_SPEC, type TextBlockAnchor, type TextBlockLine, type TextMetricsClass, type TextRevealInput, type TextRevealSpec, type TextRole, type TextSpriteInfo, type Timeline, type TimelineKey, type ValidationResult, WARP_MAX_SCALE, WARP_NEAR, WARP_TUNNEL_SPEC, WEB_WORK_SPEC, type WanderOsc, type WarningBox, adviseSequence, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, bayerMatrix, bayerTilePixels, bedRenderSeed, breakTextBlock, buildEntities, canMorph, canWrapMorph, canonicalSpecPath, cohesionOf, compileSaver, compileSequence, createFinishPass, createSceneCanvas, describeScene, diffScenes, dominanceRanking, easeSmooth, emitEnvelope, emitWindow, entityScales, fieldAt, fieldOctaves, fieldRgb, fieldRgbAt, fieldSampleTime, finishStrength, grainOffset, grainTilePixels, graphemeClusters, hasHotSwapSequence, headingAt, hexToRgb255, ignoredPropertyPaths, latticeHash, layerCohesion, lerpSpec, lerpValue, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, morphChainRoot, morphNothingMorphable, motionStats, nextKeyAfter, normalizeSeed, perceiveScene, perceiveSequenceFrame, pickScreenOp, positionAt, presentWithFinish, readSpecPath, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, resolveTimelineAt, revealState, rgb255Luma, rotationAt, sceneColour, seamCauseOf, seamsWorthWarning, segmentRenderSeed, segmentStart, sequenceSteerablePaths, sequenceSwapCompatible, sequenceWantsFinish, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textBlockAnchorOffset, textMetricsClassFor, textSprites, textStringsDiffer, textStringsOf, textWidthEm, timelineLocalTime, timelineSampleTimes, timelineSampleTimesAfter, timelineTracks, validateSequence, validateSpec, validateSpecPaths, withoutTimeline };
|