@idle-screens/saver-metaquarium 0.7.1 → 0.8.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/README.md CHANGED
@@ -8,7 +8,9 @@ paths through a dark, fogged tank.
8
8
  ```
9
9
  mount
10
10
  ├─ WebGLRenderer (stencil off, high-performance, sRGB, linear tone)
11
- ├─ Scene: fog (near 60, far 500) + flat MeshBasicMaterial floor
11
+ ├─ Scene: fog (fogNear/fogFar) + terrain floor, and — when `environment`
12
+ │ is not `void` — a terrain silhouette and light shafts (a water
13
+ │ ceiling too, for the rooms that have one: reef, kelp, ice, lagoon)
12
14
  ├─ PerspectiveCamera on param-steered spherical orbit
13
15
  └─ populate():
14
16
  for each fish index:
@@ -16,11 +18,13 @@ mount
16
18
  2. SkeletonUtils.clone() → per-fish skinned mesh
17
19
  3. applyNpcMaterials(): seeded palette body + glow colors (all unlit)
18
20
  4. compileSwimPlan(rng.fork(i), BOUNDS) → closed Catmull-Rom loop
19
- with arc-length table + speed-wobble harmonics
21
+ on the chosen pathShape, with arc-length table + speed-wobble
22
+ harmonics; swimStyle assigns depth band, formation slot or bond
20
23
  5. add to scene
21
24
 
22
25
  frame loop (rAF or renderFrame(t)):
23
- 1. governor: median frame time > 21ms → step render scale down 0.8×
26
+ 1. governor: median frame time > 21ms → render scale ×0.8 (floor 0.56);
27
+ back under 14ms → step it up again
24
28
  2. setState(t):
25
29
  - sample control track → live params
26
30
  - camera orbit from cameraAzimuth + autoRotate * t
@@ -31,7 +35,8 @@ frame loop (rAF or renderFrame(t)):
31
35
  group.position ← pose.xyz
32
36
  group.lookAt ← pose.forward
33
37
  group.rotateZ ← pose.roll (bank into turns)
34
- mixer.setTime ← distance * 0.045 (tail beat)
38
+ + maneuver displacement (seeded per-fish event schedule)
39
+ mixer.setTime ← beat * 0.045, wrapped to clip length (tail beat)
35
40
  3. renderer.render(scene, camera)
36
41
  ```
37
42
 
@@ -39,42 +44,103 @@ frame loop (rAF or renderFrame(t)):
39
44
 
40
45
  - **Deterministic**: seeded RNG only, closed-form swim. `renderFrame(t, seed)`
41
46
  is frame-addressable — same inputs, same frame.
42
- - **Steerable**: camera, fish count, swim speed, fog color — all live via
43
- control track.
47
+ - **Steerable**: camera, cast, room, swim style, maneuvers and palette — every
48
+ param below rides the control track.
49
+ - **Additive by default**: each param's default reproduces the behaviour that
50
+ existed before it was added, so a bump never changes a scene already running.
44
51
  - **Device-tiered**: `@idle-screens/capabilities` scales pixel ratio, AA, and
45
52
  fish cap per device.
46
53
  - **Adaptive governor**: steps render resolution down when frames exceed budget.
47
54
  - **Zero-dep manifest subpath**: servers validate params without pulling three.js.
55
+ - **Lofi backend**: `createMetaquarium({ backend: 'lofi' })` swaps three.js for
56
+ the Apple TV's 2D aquarium — each fish's `_transparent_icon.png` swimming a
57
+ Canvas2D After Dark tank (kelp, bubbles, light shafts). Same seed, same
58
+ layout as the TV. It reads **only** `environment` and `fishMix`, and always
59
+ swims 8 fish (13 on high-tier devices) like the TV — so a default scene is
60
+ one hero fish in WebGL and a full tank in lofi, by design. Other params
61
+ (`swimSpeed`, camera, fog, …) are no-ops there. A host choice for QA and
62
+ nostalgia, not a scene param; the playground exposes it as `?lofi=1`.
48
63
 
49
64
  ## Params
50
65
 
66
+ The paramSpace in `src/manifest.ts` is the source of truth — it carries the
67
+ bounds, eases and the reasoning behind each default. Every param defaults to
68
+ the behaviour that existed before it was added, so a scene already on a wall
69
+ never changes because a dependency was bumped.
70
+
71
+ ### Camera
72
+
73
+ | Param | Type | Default | Description |
74
+ |-------|------|---------|-------------|
75
+ | cameraAzimuth | number | 35 | Orbit angle (degrees), 0–360 |
76
+ | cameraElevation | number | 15 | Height angle above the waterline, −5–60 |
77
+ | cameraDistance | number | 110 | Distance from tank center, 80–400 |
78
+ | autoRotate | number | 0 | Continuous orbit speed (deg/s), 0–12 |
79
+
80
+ ### Cast
81
+
82
+ | Param | Type | Default | Description |
83
+ |-------|------|---------|-------------|
84
+ | fishCount | number | 1 | Visible fish, 1–24 (step). Default 1 = hero mode; the pool grows on demand and never shrinks |
85
+ | fishUrl | string | `ipfs://…/fish_257_….glb` | GLB model URL, single-breed mode (`ipfs://` supported; the playground overrides to a local asset) |
86
+ | fishMix | string | `""` | Mixed population DSL: `id[:count][@style]` comma-separated, catalog ids or breed aliases (`"257:2,100:1"`, `"457:3@hover,257:6@school"`). A minted id is an INDIVIDUAL — no id twice in a scene. Non-empty overrides fishUrl + fishCount; counts absolute, tier-capped |
87
+ | dracoPath | string | `""` | Where the Draco decoder lives (most Metaquarium models are Draco-compressed). Empty = the copy shipped beside this package |
88
+
89
+ ### Motion
90
+
51
91
  | Param | Type | Default | Description |
52
92
  |-------|------|---------|-------------|
53
- | cameraAzimuth | number | 35 | Orbit angle (degrees) |
54
- | cameraElevation | number | 15 | Height angle above waterline |
55
- | cameraDistance | number | 110 | Distance from tank center |
56
- | autoRotate | number | 0 | Orbit speed (deg/s) |
57
- | fishCount | number | 1 | Visible fish (step) |
58
- | swimSpeed | number | 1 | Swim time-scale multiplier |
59
- | fogColor | color | #030009 | Atmosphere / background |
60
- | fishUrl | string | ipfs://…/fish_257_….glb | GLB model URL, single-breed mode (playground can override to a local asset) |
61
- | fishMix | string | "" | Mixed population DSL: `id[:count][@style]` comma-separated, catalog ids or breed aliases (`"257:2,100:1"`, `"457:3@hover,257:6@school"` — per-token swim style; untagged tokens follow `swimStyle`). Non-empty overrides fishUrl + fishCount; counts absolute, tier-capped |
62
- | fogNear | number | 60 | Fog start distance (the old hardcoded Fog near) |
63
- | fogFar | number | 500 | Fog full-opacity distance; tank enforces > near + 20 |
64
- | moteDensity | number | 0 | Plankton motes, 0–1 of the tier budget (400/250/120). 0 = off |
65
- | moteColor | color | #7fd6ff | Mote tint |
66
- | floorColor | color | #0a1d33 | Floor disc color (the old hardcoded navy) |
67
- | swimStyle | enum | loop | `loop` (pre-style), `school`, `drift`, `hover`, `patrol`, `bottom`, `surface`; relationship styles `follow` / `pair` / `chase` bond a fish to the nearest preceding unbonded fish in the mix (and swim in its depth band); `auto` gives each untagged token its breed's default |
68
- | lightSeek | number | 0 | Free fish drawn toward the room's light shafts, each to its own pool (needs rays) |
69
- | formationBreathe | number | 0 | The school relaxes outward and back on a ~15 s cycle; only ever expands |
93
+ | swimSpeed | number | 1 | Swim time-scale multiplier, 0.2–3 |
94
+ | swimStyle | enum | `loop` | `loop` (pre-style), `school`, `drift`, `hover`, `patrol`, `bottom`, `surface`; relationship styles `follow` / `pair` / `chase` bond a fish to the nearest preceding unbonded fish; `auto` gives each untagged token its breed's default |
95
+ | pathShape | enum | `wander` | The shape a loop is drawn on: `wander`, `orbit`, `eight`, `helix`, `canyon`, `crossing` (camera-relative parade lane) |
96
+ | formationShape | enum | `phalanx` | How a `school` holds together: `phalanx`, `line`, `ring`, `wedge`, `ball`, `wheel`. Ignored by non-formation styles |
97
+ | swimVariance | number | 0 | Per-fish spread, 0–1: 0 a uniform shoal, 1 every fish its own animal (±40% speed, ±25% size, own phase) |
98
+ | bodyWiggle | number | 0 | Procedural body yaw for models with no animation clip, 0–1. Clipped models ignore it; 0.3–0.4 recommended for a clip-less cast |
99
+ | maneuver | enum | `none` | Named event layered over the swim style: `dart`, `startle`, `graze`, `curious`, `zoomies`. Each fish runs its own seeded schedule |
100
+ | maneuverRate | number | 0.5 | How often events fire, 0–3: 0 never, 1 the maneuver's own tempo (~14–20 s per fish), 3 nearly back to back |
101
+ | maneuverIntensity | number | 0.7 | How hard — scales the surge, the kick and the tail flurry together, 0–1 |
102
+ | lightSeek | number | 0 | Free fish drawn toward the room's light shafts, each to its own pool, 0–1 (needs rays) |
103
+ | formationBreathe | number | 0 | The school relaxes outward and back on a ~15 s cycle, 0–1; only ever expands |
104
+
105
+ ### The room
106
+
107
+ | Param | Type | Default | Description |
108
+ |-------|------|---------|-------------|
109
+ | environment | enum | `void` | The ROOM: `void` (exactly the pre-environment scene), `abyss`, `reef`, `kelp`, `ice`, `vent`, `lagoon`, `universe`. Adds terrain and light shafts, plus a water ceiling for the rooms that have one (reef, kelp, ice, lagoon); never overrides your palette params |
110
+ | floorKind | enum | `auto` | Override the environment's terrain: `auto`, `flat`, `dunes`, `ridges`, `basin` |
111
+ | waterY | number | −1 | Water-ceiling height, −1–220. −1 follows the environment (step, not smooth — the sentinel can't be interpolated through) |
112
+ | rayStrength | number | −1 | Light-shaft strength, −1–1. −1 follows the environment, 0 = off (step, same sentinel reason) |
113
+
114
+ ### Palette
115
+
116
+ | Param | Type | Default | Description |
117
+ |-------|------|---------|-------------|
118
+ | fogColor | color | `#030009` | Water / atmosphere (background + fog) |
119
+ | fogNear | number | 60 | Fog start distance, 20–200 |
120
+ | fogFar | number | 500 | Fog full-opacity distance, 120–1100; the tank enforces far > near + 20 |
121
+ | floorColor | color | `#0a1d33` | Floor disc color |
122
+ | moteDensity | number | 0 | Plankton motes, 0–1 of the tier budget. 0 = off |
123
+ | moteColor | color | `#7fd6ff` | Mote tint |
70
124
 
71
125
  ## File map
72
126
 
73
- | File | Lines | Role |
74
- |------|-------|------|
75
- | tank.ts | ~480 | Renderer, scene, fish spawn, setState, governor, dispose |
76
- | plan.ts | ~180 | Catmull-Rom swim: compile, arc-length, pose-at-distance |
77
- | materials.ts | ~90 | Seeded palette coat (unlit MeshBasicMaterial) |
78
- | manifest.ts | ~100 | Param space, palettes, manifest metadata |
79
- | quality.ts | ~35 | Device-tier quality caps |
80
- | metaquarium.ts | ~40 | Plugin factory + demo track |
127
+ `ls src/` is the truth if this drifts; the roles below are what each module owns.
128
+
129
+ | File | Role |
130
+ |------|------|
131
+ | tank.ts | Renderer, scene, fish spawn, environments, setState, governor, dispose |
132
+ | lofi.ts | Lofi backend, pure half: the Apple TV's 2D aquarium layout, poses, palettes |
133
+ | lofi-tank.ts | Lofi backend, canvas half: Canvas2D draw + transparent-icon loading |
134
+ | plan.ts | Catmull-Rom swim: compile, arc-length, pose-at-distance, path shapes |
135
+ | swim.ts | Swim styles, formations, depth bands, relationship bonds |
136
+ | maneuver.ts | Named seeded events (dart, startle, graze, curious, zoomies) |
137
+ | environments.ts | The named rooms: water ceiling, terrain, light shafts, cost budget |
138
+ | materials.ts | Seeded palette coat (unlit MeshBasicMaterial) |
139
+ | ipfs.ts | `ipfs://` resolution, gateway ladder, fishMix parsing, fish catalog |
140
+ | farm.ts | Metaquarium farm lookup (breed aliases, minted token ids) |
141
+ | asset-cids.ts | Pinned CIDs for the default catalog |
142
+ | tank-draco.ts | Draco decoder wiring, scoped to a decode |
143
+ | runtime.ts | three.js runtime resolution seam |
144
+ | manifest.ts | Param space, palettes, manifest metadata (zero-dep subpath) |
145
+ | quality.ts | Device-tier quality caps |
146
+ | metaquarium.ts | Plugin factory + demo track |
@@ -1610,4 +1610,4 @@ export {
1610
1610
  paramSpaceWith,
1611
1611
  coerceNum
1612
1612
  };
1613
- //# sourceMappingURL=chunk-RTUYWDDZ.js.map
1613
+ //# sourceMappingURL=chunk-BX2S4OKL.js.map