@idle-screens/schema 3.11.1 → 3.12.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 CHANGED
@@ -471,9 +471,11 @@ and configured the second.
471
471
  - `{ "type": "bounce", "speed": [0.12, 0.24] }` — diagonal, reflecting off edges
472
472
  - `{ "type": "static" }` — stays exactly where placed (use with `position` for HUD text)
473
473
  - `{ "type": "orbit", "speed": [10, 40], "radius": [0.05, 0.2], "center": { "x": 0.5, "y": 0.5 } }`
474
- — angular speed in deg/sec; `center` may instead be `{ "layer": "planet" }`
475
- to orbit a single-entity parent layer (moons around a wandering planet;
476
- strictly one level deep)
474
+ — angular speed in deg/sec; a **negative** speed orbits the other way
475
+ (counter-rotating rings, meshing gears); `center` may instead be
476
+ `{ "layer": "planet" }` to orbit a single-entity parent layer (moons around a
477
+ wandering planet; strictly one level deep — and the parent must have
478
+ `count: 1`)
477
479
  - `{ "type": "wander", "speed": [0.004, 0.012], "angle": 0, "meander": 0.1, "coherence": 0.7 }`
478
480
  — organic harmonic drift: base velocity plus 3 seeded sine octaves per axis.
479
481
  Flowing curved paths with zero simulation state. `meander` scales the
@@ -486,7 +488,10 @@ and configured the second.
486
488
  - `{ "type": "path", "points": [{"x":0.1,"y":0.7}, ...], "duration": 30000, "curve": "smooth", "closed": true, "scatter": 0.02 }`
487
489
  — choreographed spline (Catmull-Rom, or `linear`); 2..24 waypoints, lap time
488
490
  ≥ 2 s; `closed` loops (default), open paths ping-pong; `scatter` offsets
489
- entities sharing the path; each entity gets a seeded phase along it
491
+ entities sharing the path (**must be > 0** — omit it for an exact path
492
+ rather than writing `0`); each entity gets a seeded phase along it. A single
493
+ entity cannot show a curve — use `count` ≥ 12 (plus `trail`/`ghosting`) to
494
+ make the whole path read
490
495
 
491
496
  All speeds are ranges; each entity draws its own value (seeded).
492
497
 
@@ -697,7 +702,9 @@ its drift, orbit and pulse run on (from mount; a sequence segment's `localT`):
697
702
  ```
698
703
 
699
704
  - 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), eased `smooth`
705
+ (default 1000, the live-steer default; `0` is a cut; at most 30000 — a
706
+ longer `dur` fails validation for the whole spec, so split a slower glide
707
+ into consecutive keys of ≤ 30 s each), eased `smooth`
701
708
  (default), `linear` or `step` (holds, then switches at the glide's end).
702
709
  Keys address the same dot-paths as `setParam` — index or `key` form — and a
703
710
  path must already exist on the spec (a key cannot add a field). The spec must
@@ -891,6 +898,27 @@ is estimated from font size × string length. Good enough to perceive
891
898
  composition, focus, balance, and motion — not a substitute for a human (or VLM)
892
899
  judgement of beauty.
893
900
 
901
+ **Motion comes in both units.** `motion[].meanSpeed` / `maxSpeed` are px per
902
+ second at the perceived size; `meanSpeedVp` / `maxSpeedVp` are the same speeds
903
+ in viewport units per second (fractions of `min(w,h)` — the unit specs are
904
+ authored in), which do not change with the preview size; `1.0` crosses the
905
+ short side of the screen every second. `moving` is judged in viewport units,
906
+ so it gives the same answer at every size. Each layer also carries `heading` (screen degrees: 0 right, 90 down) and
907
+ `headingSpread` (0 = a conveyor, everything moving the same way — the
908
+ "screen-wipe" to avoid; 1 = scattered). Sampling a centroid over time gives the
909
+ WRONG sign for a wrapping full-viewport scatter, which is why heading is
910
+ reported directly.
911
+
912
+ **Colour is its own channel.** `perceiveScene(...).colour` and
913
+ `diffScenes(...).colour` carry the hue / saturation / warmth (red minus blue,
914
+ −1 cold … 1 warm) of the alpha × area weighted sprite mass and of the
915
+ background plate. Judge "warmer / cooler" on `colour.warmthDelta`, not on
916
+ `meanLuminance` — plausible cool tones weigh MORE under luma than the reds they
917
+ replace, so a calming edit pushes luminance up. `diffScenes` also reports a
918
+ `motion` channel (overall and per-layer speed deltas with headings) and
919
+ advises at the diff's own viewport, so its `advisories.added/removed` agree
920
+ with `adviseSpec` at that size.
921
+
894
922
  ## Sequence envelope (`idle-sequence`)
895
923
 
896
924
  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. */
@@ -1694,10 +1713,32 @@ declare function layerCohesion(spec: SaverSpec, opts?: PerceiveOptions): LayerCo
1694
1713
  interface LayerMotionStats {
1695
1714
  layerIndex: number;
1696
1715
  key: string | undefined;
1697
- /** Mean / max on-screen speed in px per second (wrap jumps excluded). */
1716
+ /** Mean / max on-screen speed in px per second at the perceived size (wrap jumps excluded). */
1698
1717
  meanSpeed: number;
1699
1718
  maxSpeed: number;
1719
+ /**
1720
+ * The same speeds in **viewport units per second** — fractions of
1721
+ * min(w, h), the unit a spec's `speed` is authored in — so they do not
1722
+ * change with the size the scene is perceived at. 1.0 crosses the short side
1723
+ * of the screen every second.
1724
+ */
1725
+ meanSpeedVp: number;
1726
+ maxSpeedVp: number;
1727
+ /** meanSpeedVp above MOVING_FLOOR — the same answer at every preview size. */
1700
1728
  moving: boolean;
1729
+ /**
1730
+ * Direction the layer's mass is going, in screen degrees (0 = right,
1731
+ * 90 = down, 180 = left, 270 = up), or null when nothing moves. Sampling a
1732
+ * centroid over time gives the WRONG sign for a wrapping full-viewport
1733
+ * scatter, which is why this is reported directly.
1734
+ */
1735
+ heading: number | null;
1736
+ /**
1737
+ * 0 = every entity moves the same way (a conveyor — the "screen-wipe" the
1738
+ * schema warns about), 1 = headings cancel out (a scatter, an orbit, a
1739
+ * bounce field). |mean unit vector| inverted.
1740
+ */
1741
+ headingSpread: number | null;
1701
1742
  }
1702
1743
  /**
1703
1744
  * Per-layer displacement between t and t+dt — choreography as numbers.
@@ -1711,6 +1752,34 @@ interface LayerMotionStats {
1711
1752
  declare function motionStats(spec: SaverSpec, opts?: PerceiveOptions & {
1712
1753
  dt?: number;
1713
1754
  }): LayerMotionStats[];
1755
+ interface SceneColour {
1756
+ /** Hue of the visible sprite mass in degrees (0 red, 120 green, 240 blue), null when unsaturated. */
1757
+ hue: number | null;
1758
+ /** HSL saturation 0..1 of the sprite mass. */
1759
+ saturation: number;
1760
+ /** red minus blue of the mean sprite colour, -1 (cold) .. 1 (warm). The "cooler/warmer" axis. */
1761
+ warmth: number;
1762
+ /** Mean sprite colour as hex, for a model that reads hex better than angles. */
1763
+ hex: string;
1764
+ /** The same three numbers for the background plate. */
1765
+ background: {
1766
+ hue: number | null;
1767
+ saturation: number;
1768
+ warmth: number;
1769
+ hex: string;
1770
+ };
1771
+ }
1772
+ /**
1773
+ * What colour the scene IS — the alpha × area weighted mean of every hex a
1774
+ * sprite paints with, beside the background plate. A luminance map cannot
1775
+ * carry this: a luma-matched red→cyan swap moves every other channel by
1776
+ * ~0.001 (comprehension eval 2026-07-28, P2 F3), and `meanLuminance` reads
1777
+ * plausible cool tones as *brighter* than the reds they replace, so "make it
1778
+ * cooler" pushed the only number an agent had in the wrong direction. Emoji
1779
+ * carry their own palette and are excluded; a scene of only emoji reports the
1780
+ * background.
1781
+ */
1782
+ declare function sceneColour(spec: SaverSpec, opts?: PerceiveOptions): SceneColour;
1714
1783
  declare const REGION_NAMES: readonly ["top-left", "top-center", "top-right", "mid-left", "center", "mid-right", "bottom-left", "bottom-center", "bottom-right"];
1715
1784
  interface SceneDiff {
1716
1785
  t: number;
@@ -1744,6 +1813,44 @@ interface SceneDiff {
1744
1813
  shareA: number;
1745
1814
  shareB: number;
1746
1815
  }>;
1816
+ /**
1817
+ * The hue channel. `hueShift` is the signed shortest rotation A→B in
1818
+ * degrees; `warmthDelta` is the cooler/warmer axis (negative = B is cooler)
1819
+ * — read THIS for "cooler", not `meanLuminance`, which weights cool tones
1820
+ * brighter than the reds they replace.
1821
+ */
1822
+ colour: {
1823
+ a: SceneColour;
1824
+ b: SceneColour;
1825
+ hueShift: number | null;
1826
+ saturationDelta: number;
1827
+ warmthDelta: number;
1828
+ background: {
1829
+ hueShift: number | null;
1830
+ warmthDelta: number;
1831
+ };
1832
+ };
1833
+ /**
1834
+ * The motion channel, in viewport units per second. `overall` is the
1835
+ * entity-weighted mean across layers; `layers` lists every layer whose speed
1836
+ * changed, largest change first. Halving every speed used to leave the diff
1837
+ * bit-identical (comprehension eval 2026-07-28, P2 F4).
1838
+ */
1839
+ motion: {
1840
+ overall: {
1841
+ a: number;
1842
+ b: number;
1843
+ delta: number;
1844
+ };
1845
+ layers: Array<{
1846
+ key: string;
1847
+ a: number;
1848
+ b: number;
1849
+ delta: number;
1850
+ headingA: number | null;
1851
+ headingB: number | null;
1852
+ }>;
1853
+ };
1747
1854
  advisories: {
1748
1855
  added: string[];
1749
1856
  removed: string[];
@@ -1778,6 +1885,8 @@ interface ScenePerception {
1778
1885
  motion: LayerMotionStats[];
1779
1886
  /** Per layer: merged silhouette, seamed overlap, or separate marks. */
1780
1887
  form: LayerCohesion[];
1888
+ /** What colour the scene is — hue/saturation/warmth of the sprite mass and the plate. */
1889
+ colour: SceneColour;
1781
1890
  /** Literal strings + sizes of any text layers (glyphs don't show in the maps). */
1782
1891
  text: TextSpriteInfo[];
1783
1892
  advisories: ReturnType<typeof adviseSpec>;
@@ -2568,4 +2677,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
2568
2677
  /** Lookup by saver id. */
2569
2678
  declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
2570
2679
 
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 };
2680
+ 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 };