@idle-screens/schema 3.12.0 → 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 +44 -17
- package/dist/index.d.ts +2 -1
- package/dist/index.js +498 -78
- 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
|
|
@@ -479,7 +500,10 @@ and configured the second.
|
|
|
479
500
|
- `{ "type": "wander", "speed": [0.004, 0.012], "angle": 0, "meander": 0.1, "coherence": 0.7 }`
|
|
480
501
|
— organic harmonic drift: base velocity plus 3 seeded sine octaves per axis.
|
|
481
502
|
Flowing curved paths with zero simulation state. `meander` scales the
|
|
482
|
-
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
|
|
483
507
|
(1 = the layer undulates as one body — fake flocking)
|
|
484
508
|
- `{ "type": "warp", "speed": [0.1, 0.5], "center": { "x": 0.5, "y": 0.5 } }`
|
|
485
509
|
— perspective starfield: entities stream toward the viewer on a depth axis,
|
|
@@ -860,11 +884,14 @@ moves and scales its positions and sizes (sprite rotation is not modelled).
|
|
|
860
884
|
its own × its group's — is 0 in the base scene and at every key boundary of
|
|
861
885
|
its `timeline` never shows, so it is not judged and its entities do not count
|
|
862
886
|
toward `dense-scene`; coverage is weighed by paint opacity) (also on
|
|
863
|
-
`perceiveScene().advisories`).
|
|
887
|
+
`perceiveScene().advisories`). Three of them are **spatial**, for static text:
|
|
864
888
|
`text-off-screen` (a text/textBlock box crosses the viewport edge by more
|
|
865
|
-
than 1%)
|
|
889
|
+
than 1%), `text-overlap` (two static text layers share more than 10% of
|
|
866
890
|
the smaller box; it carries both boxes as `boxes: [{x, y, w, h}]` in
|
|
867
|
-
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
|
|
868
895
|
same character-class width table the textBlock line-breaker uses, so they
|
|
869
896
|
are estimates of the renderer's own layout — a caption that fails these
|
|
870
897
|
will look wrong on the wall; one that passes may still sit a few px off.
|
package/dist/index.d.ts
CHANGED
|
@@ -1379,7 +1379,8 @@ declare function applyDeltasToSpec(spec: SaverSpec, deltas: SteerDelta[]): Saver
|
|
|
1379
1379
|
/**
|
|
1380
1380
|
* Interpolate between two structurally-similar specs at progress k (0..1).
|
|
1381
1381
|
* Numbers lerp, hex colours lerp per channel, everything else steps to the
|
|
1382
|
-
* 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.
|
|
1383
1384
|
*/
|
|
1384
1385
|
declare function lerpSpec(from: SaverSpec, to: SaverSpec, k: number): SaverSpec;
|
|
1385
1386
|
/**
|