Compare commits

...

19 Commits

Author SHA1 Message Date
BenStullsBets 61bd4a7943 Merge remote-tracking branch 'origin/main' into worktree-fix+audio-and-tracking-labels 2026-06-30 09:36:17 -07:00
BenStullsBets 2d54023f94 chore(i18n): re-extract en.json catalog to match per-clip affect
Keeps the translator source-of-record in sync with the manifest's English.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:33:46 -07:00
BenStullsBets bf1013be74 feat(content): per-clip right-brain feelings drawn from each clip's footage
Affect was authored per SCALE — every clip in an altitude shared the same 4
emotion words. Now every clip carries its OWN feelings, read from that specific
footage (mood, motion, light), so e.g. the violent Tarantula nebula evokes "a
violent ferment" while the dying-star Southern Ring evokes "a star's slow dying /
a luminous farewell," and a drowsing seal haul-out evokes "a sun-warmed drowse."

Adds AFFECT_CLIP (per-clip override; falls back to the scale register) covering
all 41 clips — 41 distinct feeling-sets, one per clip. Keeps each scale's
emotional family as a base and swaps in 1-2 footage-specific feelings, reusing the
existing feel.* palette where it fits and adding 19 new nuance keys (sublime,
tenderness, turbulence, radiance, mortality, poignancy, eeriness, purity,
solitude, verdancy, freshness, repose, hush, poise, flow, intricacy, spectral,
alienness, strangeness) with full es/fr/ja. 42 distinct feeling keys now in use.

Drafted by viewing each clip's footage (per-scale passes); first-pass reads that
want operator by-eye review. Data-only (build_pool_manifest.py + manifest + i18n
catalogs). Verified: 34/34 catalog+i18n+build pytest, 16/16 node unit, 4-language
parity for every clip, live render shows distinct feelings per clip (incl. new
keys) with Think/Feel overlap still 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:33:21 -07:00
BenStullsBets fa3bbef0d6 add sessions/0032/SESSION-0032.0-TRANSCRIPT-2026-06-30T09-20--2026-06-30T09-24.md + replace placeholder/variant SESSION-0032.0-TRANSCRIPT-2026-06-30T09-20--INPROGRESS.md 2026-06-30 09:26:01 -07:00
BenStullsBets 64e8f658f6 claim human-experience-filter-art session 0032 (placeholder) + sessions.json entry 2026-06-30 09:21:27 -07:00
BenStullsBets 760fdc7550 add sessions/0031/SESSION-0031.0-TRANSCRIPT-2026-06-30T09-06--2026-06-30T09-11.md + replace placeholder/variant SESSION-0031.0-TRANSCRIPT-2026-06-30T09-06--INPROGRESS.md 2026-06-30 09:13:37 -07:00
BenStullsBets e62382c5e5 claim human-experience-filter-art session 0031 (placeholder) + sessions.json entry 2026-06-30 09:08:16 -07:00
BenStullsBets 041fcdeb7d fix(sim): keep Feel (affect) words from overlapping Think (label) chips
Denser left labels (now >=3/clip) plus the always-4 right affect words made the
two HUD layers collide. renderAffect now nudges each Feel word vertically off the
Think chip plates (and off already-placed words + the ANALYSIS status tag),
staying on-screen, keeping the least-overlapping spot if nothing is fully clear.

Feelings are scene-level (soft position) so we move THEM, never a chip off its
subject. Obstacles are computed from the manifest, not the DOM, and a tracked
label's path is sampled across the loop — so the clearance holds for the whole
loop even though the per-frame track re-render keeps moving the chips (no per-frame
re-layout, no jitter). New diagnostic seam window.__hefNoDeconflict (sibling of
__hefState) A/B-toggles it.

Verified across all 6 scales at max knobs: authored overlaps drop to 0 with none
introduced (cosmos 3->0), and a tracked abyss clip stays at 0 overlaps sampled
over loop time as its chip drifts. No affect-position test exists to break;
16/16 node unit + frontend pytest green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:06:36 -07:00
BenStullsBets 31956bfdfc add sessions/0030/SESSION-0030.0-TRANSCRIPT-2026-06-30T08-10--2026-06-30T08-50.md + replace placeholder/variant SESSION-0030.0-TRANSCRIPT-2026-06-30T08-10--INPROGRESS.md 2026-06-30 08:53:05 -07:00
BenStullsBets 5a08d1dd35 claim human-experience-filter-art session 0030 (placeholder) + sessions.json entry 2026-06-30 08:50:53 -07:00
BenStullsBets 432fbd7703 add sessions/0029/SESSION-0029.0-TRANSCRIPT-2026-06-30T08-09--2026-06-30T08-41.md + replace placeholder/variant SESSION-0029.0-TRANSCRIPT-2026-06-30T08-09--INPROGRESS.md 2026-06-30 08:43:06 -07:00
BenStullsBets 4e4a5c256d Merge remote-tracking branch 'origin/main' into worktree-fix+audio-and-tracking-labels 2026-06-30 08:36:40 -07:00
BenStullsBets 8cfceffb76 feat(content): enrich left (Think) labels to >=3/clip + motion-track abyss creatures
Every clip now carries at least 3 factual LEFT annotations to match the 4 RIGHT
(Feel) affect words — fixes the "many clips missing Think labels" asymmetry
(right read "everywhere", left felt sparse). Adds one researched factual label to
each of the 23 thin clips (orbit_bluemarble had only 1; 22 had 2), with full
es/fr/ja parity via the catalogs, and converts the 4 still-static abyss creature
subjects (octopus, bigfin squid, dandelion siphonophore, sea pig) from fixed
boxes to 3-keyframe motion-tracked labels (tracked clips 7 -> 11).

Boxes/tracks are a first pass sampled by eye from base footage (t=0.05/0.5/0.95);
they want operator by-eye review in author mode to fine-tune positions. Remaining
static clips are landscapes (correctly static) plus 2 diffuse fish schools and 2
surface-animal scenes, available for author-mode tracking if wanted.

Data-only change (build_pool_manifest.py LABELS + manifest + i18n catalogs); no
app/engine code touched. Verified: 43/43 manifest+catalog+i18n pytest, 16/16 node
unit, live render shows 3 chips/clip in all 4 languages with no JS errors. (The 3
failing pytest are pre-existing cv2/ffmpeg-absent env limits, unrelated.)

Note: the reported "audio not playing" was a false alarm — exhaustive repro
(Chromium+WebKit, strict autoplay, both servers, RMS-verified non-silent files)
found the audio engine correct; the operator's Safari output was muted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:36:05 -07:00
BenStullsBets 5f8b8f14e6 claim human-experience-filter-art session 0029 (placeholder) + sessions.json entry 2026-06-30 08:12:39 -07:00
BenStullsBets 59c1c3a895 add sessions/0028/SESSION-0028.0-TRANSCRIPT-2026-06-30T06-53--2026-06-30T07-21.md + replace placeholder/variant SESSION-0028.0-TRANSCRIPT-2026-06-30T06-53--INPROGRESS.md 2026-06-30 07:23:18 -07:00
benstull 582183db2f Merge pull request 'fix(sim): kill the reverse-landing frame jump (D3)' (#30) from fix/d3-reverse-landing-jump into main 2026-06-30 14:17:56 +00:00
BenStullsBets c13ce36787 fix(sim): kill the reverse-landing frame jump (D3)
Ascending the altitude dial and landing on the higher-altitude clip jumped ~3
seconds. A baked morph `transitions/{h}__{l}.mp4` spans src@0 (frac 0) ->
dst@~LOOP_TAIL_S (frac 1) and is scrubbed bidirectionally, but the steady loop
element always seeked LOOP_TAIL_S (3.0s) on landing. Descending lands on dst at
its tail (matches the seek — the s0026 loop-from-tail fix); ascending lands on
src at the morph's HEAD (src@0), so seeking the tail was a 3s jump.

Make the loop landing frame direction-aware: pure `HEFScrub.loopLandFrame(dir,
loopTailS)` returns 0 when ascending, loopTailS otherwise. `loadLoop` takes a
`landFrame`, stores it as `dataset.loopStart`, and the timeupdate/ended wrap
handlers honor it (the base clips are already seamless crossfade-loops, so a
reverse landing wraps [0,dur] cleanly). The settle path lands explicitly on the
direction's frame so a fast ascent never jumps even if the heading clip wasn't
preloaded in time. App-side only — no 154-morph LFS re-bake.

Verify: node unit (loopLandFrame) + a new reverse-landing e2e asserting the loop
anchors at the head (loopStart "0"), and the existing forward "loops from tail"
e2e still green (12/12 altitude-lock).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:16:47 -07:00
BenStullsBets 21830069ad claim human-experience-filter-art session 0028 (placeholder) + sessions.json entry 2026-06-30 06:56:42 -07:00
BenStullsBets 50366c056e add sessions/0027/SESSION-0027.0-TRANSCRIPT-2026-06-29T22-04--2026-06-30T06-47.md + replace placeholder/variant SESSION-0027.0-TRANSCRIPT-2026-06-29T22-04--INPROGRESS.md 2026-06-30 06:49:13 -07:00
18 changed files with 6964 additions and 2950 deletions
@@ -0,0 +1,94 @@
# Session 0027.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-29T22-04 (PST)
> End: 2026-06-30T06-47 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: 3e51cce1-9023-4a16-b607-99894dfe0597
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Status: **FINALIZED**
## Launch prompt
```
Add left and right brain annotations (including localization) for any that are missing.
```
## Plan
> Anchor: operator direct instruction (standalone leaf content-fill task, §4.3 R2b).
**Goal:** Author the missing **left-brain** factual `LABELS` (tiered general→scientific→+fact
labels) for the rotating-pool clips that lack them, plus their **es/fr/ja** translations,
then rebuild + merge the manifest. Right-brain `affect` is auto-generated per scale for
every clip via `_affect_for_clip` — nothing missing there.
## Pre-state
- `main` at `5beee55` locally but **diverged from `origin/main`**: 11 merged-but-unpushed
commits (the entire i18n localization feature + a video-loop safety-net fix) stranded
locally; `origin/main` still at `eff43bd` (PR #29). Plus 3 files with an uncommitted
"boots-silent / no auto-start" change. Two stale `--INPROGRESS` placeholders (0015, 0017).
- Annotation reality (from a read-only survey of `build_pool_manifest.py` + catalogs):
right-brain `affect` present on all 41 clips (auto-per-scale); left-brain `LABELS`
present on only 13 of 41. 28 pool clips had `"annotations": []`.
## Arc
1. **Scoped the task** — explored the annotation/i18n system; established that the gap was
left-brain only (right-brain is auto-generated), 28 clips, + their es/fr/ja strings.
2. **Claimed session 0027** (peek showed the two stale placeholders; noted + proceeded).
3. **Surfaced the unclean baseline**; operator chose *"Push stranded, also commit dirty."*
- Stashed the 3 dirty files, merged `origin/main`'s claim commit, **pushed the 11
stranded commits** (`020219f`).
- Committed the boots-silent change on `fix/boot-silent-no-autostart`, verified
(299 pytest pass), **merged + pushed** (`7bd6c9a`).
4. **Annotation work** on `feat/left-brain-annotations`:
- Added 28 `LABELS` entries (static tiered labels) to `build_pool_manifest.py`
(Round-5 block); rebuilt manifest → all 41 clips annotated; extracted en catalog.
- Dispatched **3 parallel translator subagents** (es/fr/ja) for the 67 new keys,
matching existing catalog voice; fixed 2 ja `measure.*` values to the `約`/localized
convention; merged fragments into catalogs; merged all 3 langs into the manifest.
- Verified: 41 clips × en/es/fr/ja full key + tier-length parity; 299 pytest pass
(3 pre-existing `cv2` env failures only); 37 i18n/manifest tests green; spot-checked
translations. **Merged + pushed** (`934f60c`), deleted both branches.
## Cut state
- `main` == `origin/main` @ `934f60c`, clean tree, branches removed.
- 41/41 clips carry left+right annotations with full en/es/fr/ja parity.
- §9: local tests green. No PPE/E2E machinery exists for this app yet (pipeline is policy,
not automated — §10.6); prod promotion stays operator-gated. Visual review deferred to
operator (no Chrome on box).
## Operator plate
- **Review by eye** the 28 newly-labelled clips across all 4 languages in the running sim.
- The labels are **static**; the abyss/reef *creature* clips would benefit from a later
**tracked-label** upgrade via author mode (couldn't auto-file as a tracker issue — the
capture token returned 401 in this env; full context is in the session-0027 memory note).
- A few species IDs were inferred from clip META, not the frame (`reef_redsea`→anthias,
`reef_flowergarden`→jack/scad+sergeant-major) — worth confirming at review.
- Two stale `--INPROGRESS` sessions (0015, 0017) remain unfinalized — likely the crashed
sessions that stranded the 11 commits; left untouched for the operator.
## Next-session prompt
```
/goal Review the new left-brain annotations (all 28 newly-labelled pool clips, across en/es/fr/ja) by eye in the running simulator; correct any species/box that reads wrong, and decide whether the abyss/reef creature clips should get tracked labels via author mode.
```
## Deferred decisions
- **Static labels, not tracked.** Authored all 28 clips' left-brain labels as fixed-box
`static_label`s rather than motion-tracked labels. Alternative: tracked labels (as the
reef/abyss showcase clips use) so the box follows drifting creatures. Chose static because
the footage wasn't eyeballed — fabricating track coordinates would claim precision I don't
have. The abyss/reef creature clips are the ones that would most benefit from a later
tracked-label upgrade via author mode.
- **Some species IDs inferred from clip metadata, not the footage.** `reef_redsea`→anthias,
`reef_flowergarden`→jack/scad + sergeant-major — read from the clip META title/source.
High-confidence from provenance, but worth an eyeball check.
- **`O-type` kept verbatim in es/fr/ja** (per the translation brief) rather than the more
natural `tipo O` / `type O`. Minor; flagged by the es translator.
@@ -1,28 +0,0 @@
# Session 0027.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-29T22-04 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: 3e51cce1-9023-4a16-b607-99894dfe0597
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Status: **PLACEHOLDER — claimed at session start; finalized at session end.**
>
> This file reserves session ID 0027 for human-experience-filter-art. The driver replaces this
> body with the full transcript and renames the file to its final
> SESSION-0027.0-TRANSCRIPT-2026-06-29T22-04--<end>.md form at session end.
## Launch prompt
```
Add left and right brain annotations (including localization) for any that are missing.
Resolved scope: right-brain `affect` is auto-generated per scale for every clip (nothing missing). The gap is left-brain `LABELS` (factual/scientific tiered labels) on 28 rotating-pool clips that have no entry in simulator/build_pool_manifest.py, plus their es/fr/ja translations in tools/i18n/catalogs. Author them following the existing static_label pattern, rebuild the manifest, merge translations, run tests.
```
## Deferred decisions
_Autonomous-mode low-confidence calls the driver made and would have
liked operator input on. Appended as the session runs; surfaced at
finalize. Empty if none._
@@ -0,0 +1,125 @@
# Session 0028.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-30T06-53 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: d9da7994-e036-4b7c-b2d4-d2675ac4e5a1
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Status: **FINALIZED.**
> Worktree: `worktree-session-0028` (isolated; concurrent session in flight). Torn down at finalize.
> Outcome: D3 reverse-landing frame jump FIXED, app-side. PR #30 → main (`582183d`).
## Launch prompt
`/goal next` — operator then directed: "There's a concurrent session — do this one out
of a worktree in a different dir." Session 0028 runs isolated in worktree
`worktree-session-0028` while a concurrent session owns the simulator startup ("Run
simulation" button: `index.html`/`style.css`/`i18n.js`).
## Plan
> Anchor: leaf bug — D3 reverse-landing frame jump (no tracker issue; the s0027 capture
> token returned 401. Residual flagged in the altitude-morph-and-scrub memory note +
> scrub-driven-transitions design.)
**Goal:** kill the ~3-second frame jump when ASCENDING the altitude dial and landing on
the higher-altitude clip.
**Root cause (verified by reading the bake + player):** each baked morph
`transitions/{h}__{l}.mp4` spans `src(h)@0s` (frac 0) → `dst(l)@~LOOP_TAIL_S` (frac 1),
and the scrub uses this single forward file in BOTH directions. The steady loop element
always seeks to `LOOP_TAIL_S` (3.0s) on landing. So:
- DESCEND (dir>0) lands on `dst` at frac 1 → morph ends `dst@3` ≈ loop@3 → seamless ✓
(the s0026 "loop-from-tail" fix).
- ASCEND (dir<0) lands on `src` at frac 0 → morph shows `src@0`, but loop seeks `src@3`
→ ~3s jump ✗ (D3).
**Fix (app-side, no media re-bake — beats re-baking 154 LFS morphs):** make the loop
landing frame DIRECTION-AWARE. On a reverse landing seek the loop to 0 (= the morph's
frac-0 frame) and wrap `[0,dur]` (the base is already a seamless crossfade-loop); forward
keeps `[LOOP_TAIL_S,dur]`. Pure decision extracted to `scrub.js loopLandFrame(dir,
loopTailS)` + node tests; wired through `loadLoop`/settle/wrap handlers in `app.js`.
**Tasks:**
1. TDD `HEFScrub.loopLandFrame(dir, loopTailS)` in `scrub.js` (+ `scrub.test.js`).
2. Wire `app.js`: `loadLoop(clip, landFrame)`, directional settle seek, `dataset.loopStart`
in the `timeupdate`/`ended` wrap handlers.
3. Add a reverse-landing e2e (mirror of the existing forward "loops from tail" test).
4. Verify: node unit tests + pytest + e2e (best-effort) all green.
5. Branch → PR → merge (yolo). PPE/E2E machinery not built for this app (§10.6) — local
green is the bar; prod promotion stays operator-gated.
## Deferred decisions
_Autonomous-mode low-confidence calls the driver made and would have
liked operator input on. Appended as the session runs; surfaced at
finalize. Empty if none._
- **Fix shape: app-side direction-aware loop landing, NOT a 154-morph re-bake.** The
s0026 memory note guessed the D3 fix would be "per-clip loop files." Reading the bake +
player showed the root cause is simpler: the morph's frac-0 frame is `src@0` but the
loop always seeked the tail (`LOOP_TAIL_S`). Fixed entirely in `app.js`/`scrub.js` by
landing the loop on the morph's actual frac-0 frame (0) when ascending. This avoids
re-baking 154 LFS morphs (no media churn, a tiny reviewable diff, the base clips are
already seamless crossfade-loops so `[0,dur]` wraps cleanly). Alternative considered:
re-bake morphs to start at `src@LOOP_TAIL_S` — heavier, no benefit.
- **Pre-existing e2e failure left as-is:** `loop-recovery.spec.ts` fails in this
environment on the CLEAN baseline too (verified by stashing my changes). It boots
without enabling video, so the loop's `loopTail` is never armed and the `ended` guard
returns early. Not introduced here; my change preserves that guard exactly. Worth a
separate look (the test likely needs to `enableVideo` first), but out of scope for D3.
- **Could not verify by eye.** The fix is logic- and test-verified (node + the new
reverse-landing e2e asserting the loop anchors at the head, not the tail); the actual
visual seamlessness of an ascending landing still wants an operator eyeball in the
running sim.
## Session arc
1. Claimed session **0028** (race-free, via git). Two stale `--INPROGRESS` placeholders
(0015, 0017) noted as orphaned, left untouched.
2. Baseline survey surfaced uncommitted `run-sim` button edits + main behind by 2
(session commits). Operator confirmed a **concurrent session** owns that startup work
and directed isolation → created worktree `worktree-session-0028` off `origin/main`,
leaving the concurrent session's edits in the canonical clone untouched.
3. Orientation: in-repo `docs/ROADMAP.md` is stale (formal frontier = deferred hardware);
live frontier is in memory. Stored `/goal next` (0027's by-eye annotation review) is
operator-eyes work I can't complete; the run-sim button is the concurrent session's.
Asked the operator to pick 0028's disjoint item → **D3 reverse-landing fix**.
4. Read the bake (`build_pool_manifest.transition_cmd`) + player (`app.js` loop/morph) to
pin the root cause (morph spans `src@0``dst@tail`; loop always seeks the tail).
5. TDD: `HEFScrub.loopLandFrame` (red → green), then wired `app.js`
(`loadLoop(clip,landFrame)`, `dataset.loopStart`, directional settle, wrap handlers).
6. Added a reverse-landing e2e. Verified: node 16/16, **altitude-lock e2e 12/12** (forward
no-regression + new reverse test), pytest 299 pass. Confirmed `loop-recovery.spec.ts`
fails on the clean baseline too (pre-existing, not mine) by stashing.
7. Committed → PR #30 → merged to `main` (`582183d`) → deleted branch. Updated memory.
## Cut state
- **On `main` (`582183d`):** D3 fix — `simulator/static/scrub.js` (+`loopLandFrame`),
`simulator/static/app.js` (direction-aware loop landing), `simulator/unit/scrub.test.js`,
`simulator/e2e/tests/altitude-lock.spec.ts` (reverse-landing test).
- **Tests:** node 16/16; altitude-lock e2e 12/12; pytest 299 pass / 3 pre-existing
env-only failures (real-ffprobe/ffmpeg + ML detect; untouched — no Python changed) / 4
skipped. `loop-recovery.spec.ts` is a pre-existing baseline failure in this env.
- **§9 / deploy:** this whole project is **exempt** from flotilla/PPE/§9 (memory:
project-exempt-from-wiggleverse-deploy; public target is static Cloudflare). Local-green
is the bar — met. No PPE stage to run.
- **No plan artifact archived:** fused leaf fix, plan inline in this transcript; app has
**no content repo** (`CONTENT_REMOTE` empty) — nothing to archive.
## Operator plate
- **Review by eye** an ASCENDING altitude landing in the running sim — confirm the
~3s reverse-landing jump is gone (logic/test-verified only; no Chrome on box here).
- Still standing from s0027: by-eye review of the 28 left-brain annotations (4 langs);
decide on tracked labels for abyss/reef creature clips.
- A concurrent session was shipping the "Run simulation" startup button — reconcile/land
that separately (its edits are in the canonical clone, not touched here).
## Next-session prompt
```
/goal Review by eye in the running simulator: (1) an ASCENDING altitude landing — confirm the D3 reverse-landing frame jump is gone; (2) the 28 left-brain annotations across en/es/fr/ja, correcting any species/box that reads wrong; then decide whether the abyss/reef creature clips should get tracked labels via author mode.
```
@@ -0,0 +1,76 @@
# Session 0029.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-30T08-09 (PST)
> End: 2026-06-30T08-41 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: c87a07d3-917c-44bc-82cf-c6d8edee952b
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Branch: design/cloudflare-static-publish
> Status: **FINALIZED**
## Launch prompt
"We need to make sure credits for Creative Commons licensed videos are compliant.
What are some options?" → after presenting options the operator chose **Option A**
(manifest-driven static credits/colophon page surfaced to the public) + **Option D1**
(declare the altered footage CC BY-SA 4.0). Non-commercial.
## Pre-state
- Branch `design/cloudflare-static-publish` (static-publish work, pushed). Working
tree had uncommitted, unrelated in-flight work: a "Run simulation" start button
(6 files) **plus** an `r2-cors.json` reformat (surfaced via stash, 7 total).
- Per-clip `license`/`source` already in `clips.json` (baked from `/api/clips`) but
only shown in the dev panel + internal review pages — public experience showed none.
- A concurrent session was live in worktree `fix+audio-and-tracking-labels` (off main);
the operator was also committing directly to `design/cloudflare-static-publish`.
## Arc
1. **Audited** the license surface: `tools/licensing.py`, `pipeline/provenance.py`,
manifest, frontend. Found credits exist in data + review tooling but not the public
experience → that's the gap. Presented options AD; operator chose A + D1.
2. **Claimed session 0029** (planning-and-executing, yolo). Two stale `--INPROGRESS`
placeholders (0015, 0017) noted as long-dead, left alone.
3. **Operator-requested pre-work:** the uncommitted in-flight work. Stashed it,
created an isolated worktree (`.claude/worktrees/session-0029` off the branch HEAD,
since the static work lives there not on main), applied the stash.
- Discovered the branch had advanced under me to `8cc472c` (operator's direct
commit). It **intentionally** keeps `r2-cors.json` in S3-array form alongside a
new `r2-cors.wrangler.json` (runbook documents both) → **dropped** my conflicting
`r2-cors.json` reformat commit; kept + rebased only the Run-simulation button.
- Committed the button (`3a45e33`), ff-merged + pushed.
4. **Built Option A + D1** (TDD): node test for the pure credits logic first (red) →
`credits.js` (UMD: `creditEntries`/`creditsListHtml`, escaped, sorted) → `credits.html`
colophon (D1 CC BY-SA declaration + "altered" note; JS-filled video attributions;
PD/CC0 audio courtesy block; back link) → header "ⓘ Credits" link (`credits.link`
i18n en/es/fr/ja) → `style.css``PUBLIC_ASSETS` in `build_static.py` + its test →
Playwright e2e (`tests/test_e2e_credits.py`).
5. **Verified:** node 28/28, pytest 315 passed / 2 skipped. Real build render: 41/41
clips credited, all 7 CC-BY/BY-SA clips show required attribution, 0 missing.
Committed (`1c7f2b7`), ff-merged + pushed, removed the worktree, deleted the branch.
## End state
- `design/cloudflare-static-publish` @ `1c7f2b7`, clean, pushed (origin in sync).
- Public site now attribution-compliant: credits reachable from the work; altered-work
license declared. §9 is **exempt** for this project (static Cloudflare target), so
merged+pushed to the design branch is the completion state — no PPE/prod.
## Deferred decisions
- **Dropped my `r2-cors.json` reformat** rather than merging it — concluded from the
operator's `8cc472c` + runbook that the S3-array form is intentional. (Alternative:
keep both forms / ask. Chose to honor the documented two-file split.)
- **Audio credits as a static courtesy block** in `credits.html` rather than plumbing
`audio_license` through the manifest/API. Rationale: audio is all PD/CC0 (no legal
attribution duty) and not machine-readable; data-plumbing was out of scope. (Alternative:
add audio license fields — deferred, low value for non-commercial PD/CC0.)
## Next /goal
```
/goal operator by-eye review of the credits colophon + Run-simulation button on design/cloudflare-static-publish, then the actual Cloudflare deploy (provision R2 human-experience-simulator-media + CORS, run tools.build_static, Pages deploy) per deploy/cloudflare/README.md
```
@@ -0,0 +1,128 @@
# Session 0030.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-30T08-10 (PST)
> End: 2026-06-30T08-50 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: c4cec6c6-26d8-46f3-80ee-4ff4035a4f72
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Date: 2026-06-30
> Goal: Add an obvious "Run simulation" start button to the simulator's main
> screen (shown after the "Loading Universe…" preload); it turns Video on and
> sets Audio to level 2. Turning Video on directly should also start the
> experience, snap Audio to 2, and dismiss the button. Move the default
> first-Video-on audio coupling from 3/10 → 2/10.
> Outcome: Built + verified (303 pytest, 8 pytest-playwright incl. 3 new, 12
> Playwright TS incl. 1 updated + 1 new, node unit tests green). Work committed
> as 3a45e33 and pushed to design/cloudflare-static-publish — **the commit was
> landed by a CONCURRENT session (0029)** that swept up this session's
> working-tree changes while doing the CC-credits/Cloudflare-publish work.
## Plan
1. Map the splash/loading flow and the Video/Audio toggle wiring (Explore agent
+ direct reads of `simulator/static/{index.html,app.js,style.css,i18n.js}`).
2. Add a `#run-sim` button inside the stage `.screen`, hidden until media
preloads, styled to sit above the black cover (`z-index: 60`).
3. Add a `beginExperience()` helper: Video on + audio snapped to a
`START_AUDIO_LEVEL = "2"` + button dismissed + soundtrack played in-gesture
(Safari unlock). Wire it to both the button click and the first direct
Video-on (`!videoEverOn`).
4. Reveal the button after `hideLoading()`; change the old 3/10 coupling → 2/10.
5. Localize `run.button` in en/es/fr/ja (4-lang parity invariant).
6. Update E2E coverage (pytest-playwright + Playwright TS) from the old
"couples to 3" assertions to 2; add button-shown / button-starts /
direct-video-on tests.
7. Run the full suite + confirm a pre-existing failure is not mine.
## Pre-session state
- Branch `main` clean at `934f60c` (left-brain annotations merged). Simulator
boots SILENT (video off, audio 0) and waited for the operator to flip the
unlabelled **Video** toggle as the start gesture — not obvious to a first-time
viewer. First-Video-on coupled audio to **3/10**.
- No session-init gate was run at the top of this session — it was treated as a
small, fully-specified frontend change (see Deferred decisions).
## Turn-by-turn arc
1. **Explore + direct reads.** Dispatched an `Explore` subagent to map the
splash + AV-toggle code; in parallel read `index.html`, `app.js`
(`main()`/`hideLoading()`/`beginExperience` region, the audio dial, the
`videoEverOn` coupling), `style.css` (`#loading`, `.black z-index:50`,
`.hidden`), and `i18n.js` (UI_STRINGS shape, 4 languages). Confirmed the
stage `.screen` is `position: relative` and the black cover is `z-index: 50`,
so a start button needs `z-index > 50` inside the screen and the side panel
(Video toggle) stays reachable.
2. **Implement.** Added `#run-sim` to the stage; `.run-sim` CSS (centered pill,
`z-index: 60`); `run.button` i18n string (en/es/fr/ja); `beginExperience()`
unifying both entry paths; revealed the button after `hideLoading()`;
simplified the Video-toggle handler to call `beginExperience()` on first
on; changed coupling 3 → 2.
3. **Update tests.** Replaced the old `test_video_on_couples_audio_to_three…`
(pytest) and the "couples audio to 3/10" Playwright TS test with 2/10
variants; added button-shown-after-load, button-starts-video+audio, and
direct-Video-on-dismisses-button cases on both harnesses.
4. **Verify.** `303 passed, 2 skipped` (non-E2E pytest); `8 passed`
pytest-playwright (incl. 3 new); `12 passed` Playwright `altitude-lock.spec`
(incl. updated + new). Node unit tests (i18n / index-i18n / scrub) green; the
i18n completeness invariant confirms `run.button` has all 4 langs.
5. **Isolate a failure.** `loop-recovery.spec.ts` failed; stashed only the
source files and re-ran on a clean baseline — it **fails identically without
my changes**, so it's pre-existing, not introduced here. (During that stash
round-trip a `test-results/.last-run.json` artifact briefly blocked
`git stash pop`; recovered by `git checkout`-ing the artifact and popping —
no source lost.)
6. **Handed the working tree back uncommitted** for an operator by-eye review
(the project's usual cadence — no Chrome on the box). Offered to branch + PR.
## Cut state (end of session)
| Item | State |
| --- | --- |
| Run-simulation button + audio-2 change | Committed `3a45e33` on `design/cloudflare-static-publish`, **pushed** |
| Committed by | Concurrent **session 0029** (swept up this session's working-tree changes) |
| Files | `simulator/static/{index.html,app.js,style.css,i18n.js}`, `simulator/e2e/tests/altitude-lock.spec.ts`, `tests/test_e2e_audio.py` |
| This session's working tree | Clean; nothing of mine left to commit/push/merge |
| Branch `design/cloudflare-static-publish` | Not merged to `main` — intentional; awaits operator review + Cloudflare deploy (session 0029's frontier) |
| Deploy pipeline (§9) | **N/A** — project is exempt (static Cloudflare target; see memory `project-exempt-from-wiggleverse-deploy`) |
| Verification | 303 pytest + 8 pytest-playwright + 12 Playwright TS + node units green; `loop-recovery.spec.ts` red **pre-existing** |
## What lands on the operator's plate
- **By-eye / by-ear review** of the Run-simulation button in a real browser —
especially the **in-gesture audio unlock on real Safari/iOS** (headless tests
relax autoplay, so it's wired-but-unconfirmed on a real device).
- The button work lives on `design/cloudflare-static-publish` alongside the
CC-credits + Cloudflare-publish work; it merges to `main` / ships when that
branch does (session 0029's frontier), not separately.
- Note the concurrency: this session authored the change; session 0029 committed
it. Two transcripts (0029, 0030) touch the same single commit `3a45e33`
expected, not a duplicate of the commit.
## Prompt the operator can paste into the next session
```
/goal operator eyeball review of the static build (incl. the new Run-simulation button) + execute the real Cloudflare deploy to benstull.art/human-experience-simulator, per the cloudflare-static-publish design on branch design/cloudflare-static-publish
```
## Deferred decisions
_Autonomous-mode low-confidence calls; surfaced at finalize._
1. **No session-init gate at the top.** Treated the task as a small,
fully-specified frontend tweak and implemented directly without claiming a
transcript first. Consequence: the work landed on a shared checkout and was
committed by a concurrent session (0029) rather than carrying its own
end-to-end branch→PR. Alternative: claim a session + isolated worktree up
front. Flagged because shared-checkout work is what the worktree rule guards
against.
2. **`beginExperience()` snaps audio to 2 unconditionally** on first Video-on,
overriding a viewer who pre-set a higher level before pressing start /
flipping Video. Matched the operator's explicit "snap to audio level 2"
wording; alternative was to only raise from 0 (the prior 3/10 behavior). Easy
to soften to "only if currently 0" if undesired.
3. **Real-device audio autoplay unverified.** Headless Chromium ran with
`--autoplay-policy=no-user-gesture-required`, masking real Safari/iOS
behavior; the in-gesture unlock is wired but needs a by-ear check.
@@ -0,0 +1,80 @@
# Session 0031.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-30T09-06 (PST)
> End: 2026-06-30T09-11 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: c87a07d3-917c-44bc-82cf-c6d8edee952b
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Branch: design/cloudflare-static-publish
> Status: **FINALIZED**
## Launch prompt
Follow-up to session 0029's credits work. Operator's first ask was to rename the
credits URL → "about" (one page holding both about + credits) and add an
Accessibility table. Mid-clarification the operator redirected: **keep About and
Credits separate** (the About page is owned by a parallel session) and simply
**"Remove the 'About this work' from the credits page. I'll merge that into the
About page on the other session."**
## What I found before acting
- The Cloudflare site is now **LIVE** (`benstull.art/human-experience-simulator`);
the branch had advanced several commits since s0029 (live deploy, progressive
boot, an audio mediaBase fix), and moved again mid-session (`a68d0bc``d10ffca`).
- A **live concurrent session** owns `feat/accessibility-pass` with an
approved-pending design spec (`docs/superpowers/specs/2026-06-30-accessibility-and-about-page-design.md`):
a full WCAG 2.1 AA pass + a **separate `about.html`**. I surfaced the overlap with
the operator's original (one-page) ask; the operator resolved it by keeping the
pages separate and narrowing my task to the section removal.
## Arc
1. Surveyed git + the concurrent worktrees; read the a11y design spec.
2. Started to raise a coordination question; operator interrupted with the narrow
instruction (remove "About this work" from credits; they move it to About).
3. Claimed **session 0031**, isolated worktree off the live branch tip (`d10ffca`)
— only `credits.html` + its tests touched, so no overlap with the a11y session's
files (`index.html`/`style.css`/`app.js`/`about.html`).
4. Removed the `<section class="declaration">` "About this work" block from
`credits.html`; rewrote the footage note to be self-contained ("All footage is
altered in real time in the experience" — keeps the per-clip modification cue,
drops the dangling "as described above").
5. Updated tests to the new intent: `credits.test.js` and `tests/test_e2e_credits.py`
now assert the page notes footage is altered but the **CC BY-SA 4.0 declaration is
NOT** on credits (it moved to About).
6. Left the `.declaration` CSS rule in `style.css` (the About page reuses these styles).
7. Verified: node 28/28, pytest 315 passed / 2 skipped. Committed `8277417`,
ff-merged + pushed, removed the worktree, deleted the branch.
## End state
- `design/cloudflare-static-publish` @ `8277417`, clean, pushed.
- Credits page = per-clip attributions + "footage altered" note + audio courtesy
block. The CC BY-SA work-license declaration (D1) is **no longer on credits**
it now belongs on the About page (operator + `feat/accessibility-pass` own it).
- Site is live; this change reaches `benstull.art` only after a **redeploy**.
## Deferred decisions
- **Kept a brief "footage is altered" note on the credits page** rather than
stripping all alteration mention. Rationale: CC BY/BY-SA attribution wants the
modification indicated alongside the credit; the full license declaration is what
moved to About. (Alternative: remove every alteration mention from credits —
judged worse for the per-clip attribution's "indicate changes" cue.)
- **Left `.declaration` CSS in `style.css`** though now unused on credits — the
About page (other session) will reuse it; removing it would break that page.
## Compliance note
D1 (the altered-work CC BY-SA declaration) now depends on the About page actually
carrying it. **Confirm it landed on about.html before considering D1 done** — it is
currently in neither place on the live site until the About page ships + a redeploy.
## Next /goal
```
/goal confirm the About page (feat/accessibility-pass) carries the CC BY-SA 4.0 work-license declaration (D1, moved off credits in s0031), then redeploy design/cloudflare-static-publish to benstull.art per deploy/cloudflare/README.md
```
@@ -0,0 +1,56 @@
# Session 0032.0 — Transcript
> App: human-experience-filter-art
> Start: 2026-06-30T09-20 (PST)
> End: 2026-06-30T09-24 (PST)
> Type: planning-and-executing
> Posture: yolo
> Claude-Session: c87a07d3-917c-44bc-82cf-c6d8edee952b
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art
> Branch: design/cloudflare-static-publish
> Status: **FINALIZED**
## Launch prompt
After confirming that the "About this work" section removed in s0031 *was* the D1
compliance (the CC BY-SA share-alike declaration, not the per-clip attribution),
the operator: "Ok let's add that content back but can we name the section something
other than 'About This Work' so it isn't confused with the about page?"
## Arc
1. Confirmed for the operator that "About this work" carried D1 specifically — the
ShareAlike license declaration for the BY-SA source clips — while the per-clip
attribution (the original goal) was untouched and stayed on credits.
2. Claimed **session 0032**, isolated worktree off the branch tip (`8277417`).
3. Re-added the CC BY-SA 4.0 declaration to `credits.html` under the heading
**"License & reuse"** (deliberately not "About this work" — that name is reserved
for the separate About page from `feat/accessibility-pass`). Restored the footage
note's "altered as described above" now that the section is back above it. Reused
the `.declaration` CSS left in place by s0031.
4. Reverted the s0031 test changes: `credits.test.js` + `tests/test_e2e_credits.py`
now assert the declaration IS present, plus pin the rename (heading ≠ "About this
work", contains "License & reuse").
5. Verified: node 28/28, pytest 315 passed / 2 skipped (e2e confirms "CC BY-SA 4.0"
visible in-browser again). Committed `ee7c65a`, ff-merged + pushed, removed the
worktree, deleted the branch.
## End state
- `design/cloudflare-static-publish` @ `ee7c65a`, clean, pushed.
- Credits page now carries BOTH halves of CC compliance: per-clip CC-BY/BY-SA
attribution **and** the CC BY-SA 4.0 derivative declaration (under "License &
reuse"). The s0031 gap (live site missing the share-alike declaration) is closed.
- Site is live; reaches `benstull.art` only after a **redeploy**.
## Deferred decisions
- **Section heading "License & reuse"** chosen (operator delegated the name). It pairs
with the "Credits & licenses" page title and is unambiguous vs. the About page.
(Alternatives weighed: "This work's license", "License & alteration".)
## Next /goal
```
/goal redeploy design/cloudflare-static-publish to benstull.art (CC-BY/BY-SA credits + "License & reuse" D1 declaration now both on the credits page) per deploy/cloudflare/README.md; the About page lands separately via the feat/accessibility-pass session
```
+15
View File
@@ -79,5 +79,20 @@
},
"0027": {
"title": ""
},
"0028": {
"title": ""
},
"0029": {
"title": ""
},
"0030": {
"title": ""
},
"0031": {
"title": ""
},
"0032": {
"title": ""
}
}
+320 -8
View File
@@ -233,6 +233,268 @@ AFFECT: dict[str, list[tuple]] = {
],
}
# --- Per-CLIP affect override (feelings drawn from THAT specific footage) --------
# When a clip is here, these feelings replace the scale's shared register for it —
# tailored to what the actual video evokes (mood, motion, light), keeping the
# scale's emotional family as a base. Reuses the scale `feel.*` keys where they fit
# (their es/fr/ja already exist) and adds new keys (with catalog translations) for
# nuances a clip needs. Same tuple shape: (key, [x,y], min_level, [4 tiers]).
AFFECT_CLIP: dict[str, list[tuple]] = {
"orbit_planetearth": [
("feel.wonder", [0.5, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.radiance", [0.22, 0.68], 2, ["bright", "radiance", "brilliance", "a jeweled brilliance"]),
("feel.serenity", [0.66, 0.58], 3, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.fragility", [0.42, 0.82], 4, ["thin", "fragility", "vulnerability", "a tender fragility"]),
],
"orbit_bluemarble": [
("feel.unity", [0.5, 0.44], 1, ["one", "unity", "belonging", "a borderless belonging"]),
("feel.fragility", [0.22, 0.68], 2, ["thin", "fragility", "vulnerability", "a tender fragility"]),
("feel.distance", [0.66, 0.58], 3, ["far", "distance", "remoteness", "an exquisite remoteness"]),
("feel.vastness", [0.42, 0.82], 4, ["big", "vastness", "immensity", "a dizzying immensity"]),
],
"orbit_aurora2025": [
("feel.wonder", [0.5, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.sublime", [0.22, 0.68], 2, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
("feel.eeriness", [0.66, 0.58], 3, ["strange", "eeriness", "an otherworldly charge", "an electric, otherworldly hush"]),
("feel.serenity", [0.42, 0.82], 4, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
],
"orbit_citylights": [
("feel.tenderness", [0.5, 0.44], 1, ["soft", "tenderness", "warmth", "a cradles warmth"]),
("feel.longing", [0.22, 0.68], 2, ["want", "longing", "yearning", "an aching yearning"]),
("feel.fragility", [0.66, 0.58], 3, ["thin", "fragility", "vulnerability", "a tender fragility"]),
("feel.unity", [0.42, 0.82], 4, ["one", "unity", "belonging", "a borderless belonging"]),
],
"orbit_helene": [
("feel.sublime", [0.5, 0.44], 1, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
("feel.turbulence", [0.22, 0.68], 2, ["churn", "turbulence", "ferment", "a violent ferment"]),
("feel.vastness", [0.66, 0.58], 3, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.fragility", [0.42, 0.82], 4, ["thin", "fragility", "vulnerability", "a tender fragility"]),
],
"orbit_epic": [
("feel.distance", [0.5, 0.44], 1, ["far", "distance", "remoteness", "an exquisite remoteness"]),
("feel.insignificance", [0.22, 0.68], 2, ["small", "smallness", "insignificance", "a humbling insignificance"]),
("feel.unity", [0.66, 0.58], 3, ["one", "unity", "belonging", "a borderless belonging"]),
("feel.serenity", [0.42, 0.82], 4, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
],
"sky_grca_templesa": [
("feel.wonder", [0.5, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.sublime", [0.22, 0.68], 2, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
("feel.vastness", [0.66, 0.58], 3, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.exhilaration", [0.42, 0.82], 4, ["whee", "exhilaration", "elation", "a soaring elation"]),
],
"sky_greenland_landice": [
("feel.exhilaration", [0.5, 0.44], 1, ["whee", "exhilaration", "elation", "a soaring elation"]),
("feel.vastness", [0.22, 0.68], 2, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.purity", [0.66, 0.58], 3, ["pure", "purity", "stillness", "a glacial hush"]),
("feel.sublime", [0.42, 0.82], 4, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
],
"sky_greenland_suture": [
("feel.serenity", [0.5, 0.44], 1, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.vastness", [0.22, 0.68], 2, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.solitude", [0.66, 0.58], 3, ["alone", "solitude", "isolation", "a vast frozen solitude"]),
("feel.lightness", [0.42, 0.82], 4, ["light", "lightness", "buoyancy", "a weightless buoyancy"]),
],
"sky_jungle_amazon": [
("feel.freedom", [0.5, 0.44], 1, ["free", "freedom", "release", "a boundless release"]),
("feel.verdancy", [0.22, 0.68], 2, ["lush", "verdancy", "abundance", "a teeming green abundance"]),
("feel.exhilaration", [0.66, 0.58], 3, ["whee", "exhilaration", "elation", "a soaring elation"]),
("feel.serenity", [0.42, 0.82], 4, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
],
"sky_jungle_waterfall": [
("feel.wonder", [0.5, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.vertigo", [0.22, 0.68], 2, ["whoa", "vertigo", "giddiness", "a giddy, falling vertigo"]),
("feel.freshness", [0.66, 0.58], 3, ["cool", "freshness", "vitality", "a cool, cascading freshness"]),
("feel.exhilaration", [0.42, 0.82], 4, ["whee", "exhilaration", "elation", "a soaring elation"]),
],
"sky_coast_cliffspain": [
("feel.sublime", [0.5, 0.44], 1, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
("feel.exhilaration", [0.22, 0.68], 2, ["whee", "exhilaration", "elation", "a soaring elation"]),
("feel.turbulence", [0.66, 0.58], 3, ["churn", "turbulence", "ferment", "a violent ferment"]),
("feel.vastness", [0.42, 0.82], 4, ["big", "vastness", "immensity", "a dizzying immensity"]),
],
"sky_mtn_castlecrags": [
("feel.freedom", [0.5, 0.44], 1, ["free", "freedom", "release", "a boundless release"]),
("feel.serenity", [0.22, 0.68], 2, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.vastness", [0.66, 0.58], 3, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.lightness", [0.42, 0.82], 4, ["light", "lightness", "buoyancy", "a weightless buoyancy"]),
],
"sky_mtn_rocky": [
("feel.sublime", [0.5, 0.44], 1, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
("feel.vertigo", [0.22, 0.68], 2, ["whoa", "vertigo", "giddiness", "a giddy, falling vertigo"]),
("feel.exhilaration", [0.66, 0.58], 3, ["whee", "exhilaration", "elation", "a soaring elation"]),
("feel.vastness", [0.42, 0.82], 4, ["big", "vastness", "immensity", "a dizzying immensity"]),
],
"coast_birdrock": [
("feel.melancholy", [0.5, 0.44], 1, ["sad", "melancholy", "longing", "a soft seaward longing"]),
("feel.fragility", [0.22, 0.68], 2, ["thin", "fragility", "vulnerability", "a tender fragility"]),
("feel.nostalgia", [0.66, 0.58], 3, ["miss", "nostalgia", "wistfulness", "a salt-air wistfulness"]),
("feel.serenity", [0.42, 0.82], 4, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
],
"coast_surfgrass": [
("feel.abundance", [0.5, 0.44], 1, ["full", "abundance", "richness", "a teeming richness"]),
("feel.immersion", [0.22, 0.68], 2, ["in", "immersion", "absorption", "a held, breathless absorption"]),
("feel.curiosity", [0.66, 0.58], 3, ["look", "curiosity", "fascination", "an absorbed fascination"]),
("feel.ease", [0.42, 0.82], 4, ["nice", "ease", "calm", "an unhurried calm"]),
],
"coast_kelp": [
("feel.immersion", [0.5, 0.44], 1, ["in", "immersion", "absorption", "a held, breathless absorption"]),
("feel.wonder", [0.22, 0.68], 2, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.serenity", [0.66, 0.58], 3, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.freedom", [0.42, 0.82], 4, ["free", "freedom", "release", "a boundless release"]),
],
"coast_otters": [
("feel.tenderness", [0.5, 0.44], 1, ["soft", "tenderness", "warmth", "a cradles warmth"]),
("feel.delight", [0.22, 0.68], 2, ["fun", "delight", "joy", "a darting, bright joy"]),
("feel.ease", [0.66, 0.58], 3, ["nice", "ease", "calm", "an unhurried calm"]),
("feel.belonging", [0.42, 0.82], 4, ["home", "belonging", "rootedness", "a tidal rootedness"]),
],
"coast_kalaloch": [
("feel.nostalgia", [0.5, 0.44], 1, ["miss", "nostalgia", "wistfulness", "a salt-air wistfulness"]),
("feel.melancholy", [0.22, 0.68], 2, ["sad", "melancholy", "longing", "a soft seaward longing"]),
("feel.serenity", [0.66, 0.58], 3, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.belonging", [0.42, 0.82], 4, ["home", "belonging", "rootedness", "a tidal rootedness"]),
],
"coast_seals": [
("feel.tenderness", [0.5, 0.44], 1, ["soft", "tenderness", "warmth", "a cradles warmth"]),
("feel.repose", [0.22, 0.68], 2, ["rest", "repose", "drowse", "a sun-warmed drowse"]),
("feel.belonging", [0.66, 0.58], 3, ["home", "belonging", "rootedness", "a tidal rootedness"]),
("feel.delight", [0.42, 0.82], 4, ["fun", "delight", "joy", "a darting, bright joy"]),
],
"coast_mist": [
("feel.serenity", [0.5, 0.44], 1, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.wonder", [0.22, 0.68], 2, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.hush", [0.66, 0.58], 3, ["quiet", "hush", "stillness", "a breath-held hush"]),
("feel.nostalgia", [0.42, 0.82], 4, ["miss", "nostalgia", "wistfulness", "a salt-air wistfulness"]),
],
"reef_lionfish": [
("feel.curiosity", [0.5, 0.44], 1, ["look", "curiosity", "fascination", "an absorbed fascination"]),
("feel.poise", [0.22, 0.68], 2, ["still", "poise", "grace", "a hovering, ornate poise"]),
("feel.fragility", [0.66, 0.58], 3, ["thin", "fragility", "vulnerability", "a tender fragility"]),
("feel.immersion", [0.42, 0.82], 4, ["in", "immersion", "absorption", "a held, breathless absorption"]),
],
"reef_spawning": [
("feel.abundance", [0.5, 0.44], 1, ["full", "abundance", "richness", "a teeming richness"]),
("feel.flow", [0.22, 0.68], 2, ["go", "flow", "streaming", "a sweeping, schooling flow"]),
("feel.vastness", [0.66, 0.58], 3, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.immersion", [0.42, 0.82], 4, ["in", "immersion", "absorption", "a held, breathless absorption"]),
],
"reef_hawkfish": [
("feel.radiance", [0.5, 0.44], 1, ["bright", "radiance", "brilliance", "a jeweled brilliance"]),
("feel.curiosity", [0.22, 0.68], 2, ["look", "curiosity", "fascination", "an absorbed fascination"]),
("feel.delight", [0.66, 0.58], 3, ["fun", "delight", "joy", "a darting, bright joy"]),
("feel.immersion", [0.42, 0.82], 4, ["in", "immersion", "absorption", "a held, breathless absorption"]),
],
"reef_coralspacific": [
("feel.intricacy", [0.5, 0.44], 1, ["fine", "intricacy", "detail", "an intricate, woven density"]),
("feel.fragility", [0.22, 0.68], 2, ["thin", "fragility", "vulnerability", "a tender fragility"]),
("feel.abundance", [0.66, 0.58], 3, ["full", "abundance", "richness", "a teeming richness"]),
("feel.curiosity", [0.42, 0.82], 4, ["look", "curiosity", "fascination", "an absorbed fascination"]),
],
"reef_redsea": [
("feel.serenity", [0.5, 0.44], 1, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.abundance", [0.22, 0.68], 2, ["full", "abundance", "richness", "a teeming richness"]),
("feel.radiance", [0.66, 0.58], 3, ["bright", "radiance", "brilliance", "a jeweled brilliance"]),
("feel.delight", [0.42, 0.82], 4, ["fun", "delight", "joy", "a darting, bright joy"]),
],
"reef_flowergarden": [
("feel.delight", [0.5, 0.44], 1, ["fun", "delight", "joy", "a darting, bright joy"]),
("feel.abundance", [0.22, 0.68], 2, ["full", "abundance", "richness", "a teeming richness"]),
("feel.freedom", [0.66, 0.58], 3, ["free", "freedom", "release", "a boundless release"]),
("feel.immersion", [0.42, 0.82], 4, ["in", "immersion", "absorption", "a held, breathless absorption"]),
],
"abyss_wow": [
("feel.vastness", [0.5, 0.44], 1, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.isolation", [0.22, 0.68], 2, ["alone", "isolation", "solitude", "a crushing solitude"]),
("feel.fascination", [0.66, 0.58], 3, ["ooh", "fascination", "wonder", "a forbidden wonder"]),
("feel.unease", [0.42, 0.82], 4, ["uh", "unease", "disquiet", "a creeping disquiet"]),
],
"abyss_midwaterexp": [
("feel.fragility", [0.5, 0.44], 1, ["thin", "fragility", "vulnerability", "a tender fragility"]),
("feel.fascination", [0.22, 0.68], 2, ["ooh", "fascination", "wonder", "a forbidden wonder"]),
("feel.poignancy", [0.66, 0.58], 3, ["ache", "poignancy", "bittersweetness", "a luminous farewell"]),
("feel.isolation", [0.42, 0.82], 4, ["alone", "isolation", "solitude", "a crushing solitude"]),
],
"abyss_hiding": [
("feel.spectral", [0.5, 0.44], 1, ["ghost", "the spectral", "hauntedness", "a ghost in the water"]),
("feel.unease", [0.22, 0.68], 2, ["uh", "unease", "disquiet", "a creeping disquiet"]),
("feel.fascination", [0.66, 0.58], 3, ["ooh", "fascination", "wonder", "a forbidden wonder"]),
("feel.fragility", [0.42, 0.82], 4, ["thin", "fragility", "vulnerability", "a tender fragility"]),
],
"abyss_bigfin": [
("feel.alienness", [0.5, 0.44], 1, ["odd", "alienness", "strangeness", "an alien grace"]),
("feel.fascination", [0.22, 0.68], 2, ["ooh", "fascination", "wonder", "a forbidden wonder"]),
("feel.unease", [0.66, 0.58], 3, ["uh", "unease", "disquiet", "a creeping disquiet"]),
("feel.isolation", [0.42, 0.82], 4, ["alone", "isolation", "solitude", "a crushing solitude"]),
],
"abyss_dandelion": [
("feel.radiance", [0.5, 0.44], 1, ["bright", "radiance", "brilliance", "a jeweled brilliance"]),
("feel.wonder", [0.22, 0.68], 2, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.fascination", [0.66, 0.58], 3, ["ooh", "fascination", "wonder", "a forbidden wonder"]),
("feel.fragility", [0.42, 0.82], 4, ["thin", "fragility", "vulnerability", "a tender fragility"]),
],
"abyss_octopus": [
("feel.curiosity", [0.5, 0.44], 1, ["look", "curiosity", "fascination", "an absorbed fascination"]),
("feel.isolation", [0.22, 0.68], 2, ["alone", "isolation", "solitude", "a crushing solitude"]),
("feel.tenderness", [0.66, 0.58], 3, ["soft", "tenderness", "warmth", "a cradles warmth"]),
("feel.unease", [0.42, 0.82], 4, ["uh", "unease", "disquiet", "a creeping disquiet"]),
],
"abyss_seapig": [
("feel.strangeness", [0.5, 0.44], 1, ["weird", "strangeness", "the uncanny", "a fleshy strangeness"]),
("feel.unease", [0.22, 0.68], 2, ["uh", "unease", "disquiet", "a creeping disquiet"]),
("feel.fascination", [0.66, 0.58], 3, ["ooh", "fascination", "wonder", "a forbidden wonder"]),
("feel.curiosity", [0.42, 0.82], 4, ["look", "curiosity", "fascination", "an absorbed fascination"]),
],
# cosmos — Webb "Cosmic Cliffs": monumental golden ridges, cathedral-scale.
"cosmos": [
("feel.wonder", [0.50, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.sublime", [0.22, 0.68], 2, ["grand", "the sublime", "grandeur", "a cathedral grandeur"]),
("feel.vastness", [0.66, 0.58], 3, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.insignificance", [0.42, 0.82], 4, ["small", "smallness", "insignificance", "a humbling insignificance"]),
],
# cosmos_galaxies — drifting through a dark field of galaxies: lonely, remote.
"cosmos_galaxies": [
("feel.vastness", [0.50, 0.44], 1, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.distance", [0.22, 0.68], 2, ["far", "distance", "remoteness", "an exquisite remoteness"]),
("feel.insignificance", [0.66, 0.58], 3, ["small", "smallness", "insignificance", "a humbling insignificance"]),
("feel.longing", [0.42, 0.82], 4, ["want", "longing", "yearning", "an aching yearning"]),
],
# cosmos_orion — soft rose-lit star nursery: tender, dreamy, warm.
"cosmos_orion": [
("feel.wonder", [0.50, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.tenderness", [0.22, 0.68], 2, ["soft", "tenderness", "warmth", "a cradles warmth"]),
("feel.serenity", [0.66, 0.58], 3, ["calm", "serenity", "peace", "a quiet, weightless peace"]),
("feel.longing", [0.42, 0.82], 4, ["want", "longing", "yearning", "an aching yearning"]),
],
# cosmos_tarantula — chaotic, fierce star-forming nebula: turbulent, intense.
"cosmos_tarantula": [
("feel.wonder", [0.50, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.turbulence", [0.22, 0.68], 2, ["churn", "turbulence", "ferment", "a violent ferment"]),
("feel.vastness", [0.66, 0.58], 3, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.insignificance", [0.42, 0.82], 4, ["small", "smallness", "insignificance", "a humbling insignificance"]),
],
# cosmos_westerlund — sparkling jewel-box star cluster: dazzling, radiant.
"cosmos_westerlund": [
("feel.wonder", [0.50, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.radiance", [0.22, 0.68], 2, ["bright", "radiance", "brilliance", "a jeweled brilliance"]),
("feel.exhilaration", [0.66, 0.58], 3, ["whee", "exhilaration", "elation", "a soaring elation"]),
("feel.vastness", [0.42, 0.82], 4, ["big", "vastness", "immensity", "a dizzying immensity"]),
],
# cosmos_southernring — gas shed by a dying star: elegiac, mortal, poignant.
"cosmos_southernring": [
("feel.wonder", [0.50, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.mortality", [0.22, 0.68], 2, ["end", "mortality", "impermanence", "a stars slow dying"]),
("feel.poignancy", [0.66, 0.58], 3, ["ache", "poignancy", "bittersweetness", "a luminous farewell"]),
("feel.longing", [0.42, 0.82], 4, ["want", "longing", "yearning", "an aching yearning"]),
],
# cosmos_carina_eso — wide teeming Milky Way star field: panoramic richness.
"cosmos_carina_eso": [
("feel.wonder", [0.50, 0.44], 1, ["wow", "wonder", "awe", "transcendent awe"]),
("feel.vastness", [0.22, 0.68], 2, ["big", "vastness", "immensity", "a dizzying immensity"]),
("feel.abundance", [0.66, 0.58], 3, ["full", "abundance", "richness", "a teeming richness"]),
("feel.insignificance", [0.42, 0.82], 4, ["small", "smallness", "insignificance", "a humbling insignificance"]),
],
}
def static_label(key, salience, tiers, box):
"""A fixed-box tiered label (always on-screen, salience-gated by Left)."""
@@ -269,6 +531,7 @@ LABELS: dict[str, list[dict]] = {
measure("measure.distance", "≈7,500 ly", [0.06, 0.06, 0.2, 0.08], 3),
],
"cosmos_galaxies": [
measure("measure.count", "~2T galaxies", [0.06, 0.16, 0.24, 0.08], 2),
static_label("detected.galaxy", 4, ["galaxy", "spiral galaxy", "barred spiral", "barred spiral · ~10¹¹ stars"], [0.34, 0.30, 0.30, 0.34]),
measure("measure.distance", "~Mly", [0.06, 0.06, 0.18, 0.08], 3),
],
@@ -291,13 +554,19 @@ LABELS: dict[str, list[dict]] = {
],
"orbit_bluemarble": [
static_label("detected.globe", 4, ["Earth", "the globe", "terrestrial planet", "terrestrial planet · 12,742 km across"], [0.28, 0.18, 0.44, 0.6]),
# Rotating full-disk globe: continents drift through frame, so label the
# PERSISTENT features (clouds, starfield) with static boxes, not a continent.
static_label("detected.cloud_band", 3, ["clouds", "weather systems", "cloud systems", "global cloud systems · clouds cover ~67% of Earth"], [0.34, 0.42, 0.34, 0.32]),
static_label("detected.starfield", 1, ["stars", "starfield", "background stars", "background starfield · a rendered backdrop, not to scale"], [0.74, 0.05, 0.22, 0.30]),
],
# ---------- coast ----------
"coast_birdrock": [
static_label("detected.seabirds", 2, ["birds", "seabirds", "nesting seabirds", "seabird colony · the rookery that names the rock"], [0.35, 0.18, 0.25, 0.2]),
static_label("detected.surf", 4, ["waves", "surf", "breaking swell", "breaking swell · wind-driven, ~10 s period"], [0.20, 0.55, 0.6, 0.3]),
static_label("detected.searock", 2, ["rock", "sea stack", "coastal sea stack", "sea stack · wave-cut residual rock"], [0.30, 0.25, 0.22, 0.3]),
],
"coast_surfgrass": [
static_label("detected.tidepool", 2, ["pool", "tidepool", "intertidal pool", "tidepool · a pocket sea bared at low tide"], [0.15, 0.6, 0.4, 0.25]),
static_label("detected.surfgrass", 4, ["grass", "surfgrass", "Phyllospadix", "Phyllospadix · a marine seagrass, not algae"], [0.18, 0.40, 0.5, 0.4]),
static_label("detected.coralline", 2, ["pink", "coralline algae", "crustose coralline", "crustose coralline · calcified red algae"], [0.6, 0.55, 0.2, 0.2]),
],
@@ -336,6 +605,7 @@ LABELS: dict[str, list[dict]] = {
measure("measure.depth", "22 m", [0.06, 0.06, 0.16, 0.08], 3),
],
"reef_hawkfish": [
static_label("detected.coral", 2, ["coral", "reef coral", "hard coral", "hard coral · the reef this fish grazes into sand"], [0.1, 0.6, 0.55, 0.3]),
tracked_label(
"detected.parrotfish", 4,
["fish", "parrotfish", "Bolbometopon muricatum", "B. muricatum · humphead, grazes reef into sand"],
@@ -353,6 +623,7 @@ LABELS: dict[str, list[dict]] = {
),
],
"reef_coralspacific": [
static_label("detected.polyp", 1, ["polyps", "coral polyps", "living polyps", "polyps · each a tiny animal, the colonys builders"], [0.25, 0.4, 0.3, 0.3]),
static_label("detected.coral", 4, ["coral", "coral colony", "Pacific scleractinian", "Pacific scleractinian · a symbiosis with algae"], [0.2, 0.4, 0.4, 0.4]),
tracked_label(
"detected.spotfish", 2,
@@ -393,6 +664,7 @@ LABELS: dict[str, list[dict]] = {
measure("measure.depth", "1,600 m", [0.06, 0.06, 0.16, 0.08], 2),
],
"abyss_hiding": [
static_label("detected.marinesnow", 1, ["specks", "marine snow", "falling detritus", "marine snow · organic debris drifting down from above"], [0.1, 0.1, 0.8, 0.6]),
tracked_label(
"detected.jelly", 4,
["jelly", "crimson jelly", "Scyphozoa", "Scyphozoa · red is invisible in the lightless deep"],
@@ -454,55 +726,68 @@ LABELS: dict[str, list[dict]] = {
],
# ---------- sky ----------
"sky_grca_templesa": [
static_label("detected.butte", 2, ["tower", "butte", "rock temple", "rock temple · an erosional butte left standing"], [0.4, 0.3, 0.2, 0.35]),
static_label("detected.canyon", 4, ["canyon", "gorge", "the Grand Canyon", "Grand Canyon · ~1.8 Gyr of rock, cut by the Colorado"], [0.10, 0.38, 0.8, 0.52]),
static_label("detected.strata", 2, ["layers", "rock strata", "sedimentary beds", "strata · stacked epochs of deposition"], [0.15, 0.55, 0.6, 0.25]),
],
"sky_greenland_landice": [
static_label("detected.crevasse", 2, ["cracks", "crevasses", "glacial crevasses", "crevasses · the ice fracturing as it flows"], [0.2, 0.55, 0.5, 0.25]),
static_label("detected.icesheet", 4, ["ice", "ice sheet", "the Greenland ice sheet", "ice sheet · up to ~3 km thick, flowing slowly seaward"], [0.08, 0.40, 0.84, 0.5]),
static_label("detected.snow", 2, ["snow", "snowfield", "firn", "firn · old snow compacting toward glacial ice"], [0.20, 0.20, 0.6, 0.25]),
],
"sky_greenland_suture": [
static_label("detected.floe", 2, ["plates", "ice floes", "pack-ice floes", "pack-ice floes · drifting plates of frozen sea"], [0.15, 0.2, 0.3, 0.3]),
static_label("detected.seaice", 4, ["ice", "sea ice", "drift ice", "drift ice · a frozen ocean skin, cracking and refreezing"], [0.08, 0.10, 0.84, 0.8]),
static_label("detected.lead", 2, ["crack", "lead", "open lead", "lead · a fracture of open water between floes"], [0.30, 0.40, 0.4, 0.2]),
],
"sky_jungle_amazon": [
static_label("detected.mist", 1, ["haze", "canopy mist", "transpiration haze", "transpiration haze · the forest exhaling water vapor"], [0.06, 0.1, 0.88, 0.18]),
static_label("detected.canopy", 4, ["trees", "forest canopy", "the Amazon canopy", "rainforest canopy · among the densest biodiversity on Earth"], [0.06, 0.30, 0.88, 0.6]),
static_label("detected.emergent", 2, ["tree", "tall tree", "emergent tree", "emergent · a giant breaking above the canopy roof"], [0.42, 0.40, 0.16, 0.2]),
],
"sky_jungle_waterfall": [
static_label("detected.spray", 2, ["mist", "spray", "plunge spray", "plunge spray · the river aerosolized on impact"], [0.36, 0.62, 0.28, 0.25]),
static_label("detected.waterfall", 4, ["falls", "waterfall", "cataract", "cataract · a river plunging off the canopys edge"], [0.34, 0.20, 0.3, 0.7]),
static_label("detected.canopy", 2, ["trees", "jungle canopy", "rainforest canopy", "canopy · dense forest crowding the gorge"], [0.05, 0.30, 0.9, 0.5]),
],
"sky_coast_cliffspain": [
static_label("detected.headland", 1, ["point", "headland", "rock promontory", "promontory · a cliffed arm of land into the sea"], [0.1, 0.2, 0.3, 0.4]),
static_label("detected.seacliff", 4, ["cliff", "sea cliff", "coastal headland", "sea cliff · Atlantic rock cut back by the surf"], [0.10, 0.25, 0.8, 0.55]),
static_label("detected.surf", 2, ["waves", "surf", "breaking swell", "breaking swell · ocean meeting stone"], [0.15, 0.70, 0.7, 0.25]),
],
"sky_mtn_castlecrags": [
static_label("detected.talus", 2, ["scree", "talus", "talus slope", "talus · frost-shattered rock piled below the crags"], [0.3, 0.55, 0.4, 0.25]),
static_label("detected.spire", 4, ["rock", "spire", "granite spire", "granite spire · a glacier-carved pluton, exhumed and weathered"], [0.30, 0.20, 0.4, 0.6]),
static_label("detected.forest", 2, ["trees", "conifer forest", "montane forest", "montane forest · cloaking the slopes below the crags"], [0.05, 0.60, 0.9, 0.35]),
],
"sky_mtn_rocky": [
static_label("detected.snowfield", 2, ["snow", "snowfield", "alpine snowfield", "alpine snowfield · lingering high-elevation snow"], [0.3, 0.22, 0.3, 0.22]),
static_label("detected.summit", 4, ["peak", "summit", "alpine summit", "alpine summit · above tree line, snow-streaked granite"], [0.25, 0.20, 0.5, 0.5]),
static_label("detected.tundra", 2, ["meadow", "alpine tundra", "alpine tundra", "alpine tundra · low cushion plants above the trees"], [0.10, 0.62, 0.8, 0.3]),
],
# ---------- coast ----------
"coast_kelp": [
static_label("detected.pneumatocyst", 1, ["floats", "gas bladders", "pneumatocysts", "pneumatocysts · gas floats lifting the blades to light"], [0.4, 0.18, 0.2, 0.25]),
static_label("detected.kelp", 4, ["kelp", "giant kelp", "Macrocystis pyrifera", "Macrocystis · can grow ~0.5 m a day toward the light"], [0.20, 0.10, 0.6, 0.8]),
static_label("detected.frond", 2, ["leaf", "blade", "kelp frond", "frond · gas-filled floats hold it upright"], [0.40, 0.30, 0.2, 0.4]),
],
"coast_otters": [
static_label("detected.fur", 1, ["fur", "dense fur", "the densest fur", "densest fur on Earth · ~1M hairs/in², no blubber"], [0.34, 0.38, 0.3, 0.25]),
static_label("detected.otter", 4, ["otter", "sea otter", "Enhydra lutris", "Enhydra lutris · eats ~25% of its weight a day to stay warm"], [0.30, 0.35, 0.4, 0.35]),
static_label("detected.water", 2, ["water", "estuary", "tidal slough", "tidal slough · sheltered nursery water"], [0.05, 0.05, 0.9, 0.25]),
],
"coast_kalaloch": [
static_label("detected.sunset", 2, ["glow", "sunset", "golden hour", "golden hour · low sun reddened through more air"], [0.05, 0.05, 0.9, 0.22]),
static_label("detected.surf", 4, ["waves", "surf", "breaking swell", "breaking swell · long-period Pacific swell"], [0.15, 0.50, 0.7, 0.35]),
static_label("detected.searock", 2, ["rock", "sea stack", "coastal sea stack", "sea stack · wave-cut residual rock"], [0.30, 0.25, 0.3, 0.3]),
],
"coast_seals": [
static_label("detected.whiskers", 1, ["whiskers", "vibrissae", "sensing whiskers", "vibrissae · whiskers that feel prey in murky water"], [0.3, 0.42, 0.2, 0.15]),
static_label("detected.seal", 4, ["seals", "harbor seals", "Phoca vitulina", "Phoca vitulina · hauls out on rock to rest and warm"], [0.20, 0.40, 0.6, 0.35]),
static_label("detected.searock", 2, ["rock", "haul-out rock", "intertidal rock", "haul-out · a tide-washed resting ledge"], [0.05, 0.6, 0.9, 0.3]),
],
"coast_mist": [
static_label("detected.swell", 2, ["waves", "swell", "ocean swell", "ocean swell · wind-built waves from distant storms"], [0.1, 0.58, 0.8, 0.25]),
static_label("detected.mist", 4, ["mist", "sea mist", "advection fog", "advection fog · warm air cooling over cold upwelling"], [0.05, 0.10, 0.9, 0.5]),
static_label("detected.searock", 2, ["rock", "shore rock", "coastal rock", "coastal rock · the standing edge of the land"], [0.20, 0.55, 0.6, 0.35]),
],
@@ -519,19 +804,43 @@ LABELS: dict[str, list[dict]] = {
],
# ---------- abyss ----------
"abyss_bigfin": [
static_label("detected.squid", 4, ["squid", "bigfin squid", "Magnapinna", "Magnapinna · elbowed arms trailing meters into the dark"], [0.30, 0.20, 0.4, 0.65]),
static_label("detected.arms", 2, ["arms", "elbowed arms", "trailing filaments", "elbowed arms · held out, then trailing meters of thread"], [0.35, 0.5, 0.3, 0.4]),
tracked_label(
"detected.squid", 4,
["squid", "bigfin squid", "Magnapinna", "Magnapinna · elbowed arms trailing meters into the dark"],
0.0, 1.0,
[(0.0, [0.3, 0.36, 0.28, 0.3]), (0.5, [0.42, 0.22, 0.26, 0.28]), (1.0, [0.55, 0.1, 0.26, 0.3])],
),
measure("measure.depth", "2,000 m", [0.06, 0.06, 0.16, 0.08], 2),
],
"abyss_dandelion": [
static_label("detected.siphonophore", 4, ["orb", "siphonophore", "dandelion siphonophore", "Rhodaliidae · a colony of clones tethered to the seabed"], [0.32, 0.28, 0.36, 0.4]),
static_label("detected.tentacle", 2, ["threads", "tentacles", "feeding tentacles", "feeding tentacles · a drifting net for prey"], [0.3, 0.45, 0.4, 0.3]),
tracked_label(
"detected.siphonophore", 4,
["orb", "siphonophore", "dandelion siphonophore", "Rhodaliidae · a colony of clones tethered to the seabed"],
0.0, 1.0,
[(0.0, [0.38, 0.28, 0.26, 0.36]), (0.5, [0.3, 0.2, 0.34, 0.46]), (1.0, [0.18, 0.1, 0.45, 0.7])],
),
measure("measure.depth", "2,500 m", [0.06, 0.06, 0.16, 0.08], 2),
],
"abyss_octopus": [
static_label("detected.octopus", 4, ["octopus", "deep-sea octopus", "Graneledone", "Graneledone boreopacifica · broods its eggs for ~4.5 years"], [0.25, 0.30, 0.5, 0.45]),
static_label("detected.arms", 2, ["arms", "eight arms", "sucker-lined arms", "eight arms · sucker-lined, tasting what they touch"], [0.25, 0.45, 0.5, 0.3]),
tracked_label(
"detected.octopus", 4,
["octopus", "deep-sea octopus", "Graneledone", "Graneledone boreopacifica · broods its eggs for ~4.5 years"],
0.0, 1.0,
[(0.0, [0.5, 0.1, 0.35, 0.55]), (0.5, [0.42, 0.16, 0.36, 0.52]), (1.0, [0.05, 0.02, 0.92, 0.95])],
),
measure("measure.depth", "2,500 m", [0.06, 0.06, 0.16, 0.08], 2),
],
"abyss_seapig": [
static_label("detected.seacucumber", 4, ["blob", "sea cucumber", "Enypniastes eximia", "Enypniastes · a swimming sea cucumber, the “headless chicken”"], [0.30, 0.30, 0.4, 0.4]),
static_label("detected.veil", 2, ["veil", "oral veil", "swimming veil", "oral veil · sweeps sediment, and flaps to swim"], [0.3, 0.3, 0.35, 0.25]),
tracked_label(
"detected.seacucumber", 4,
["blob", "sea cucumber", "Enypniastes eximia", "Enypniastes · a swimming sea cucumber, the “headless chicken”"],
0.0, 1.0,
[(0.0, [0.58, 0.38, 0.18, 0.3]), (0.5, [0.46, 0.32, 0.19, 0.32]), (1.0, [0.38, 0.28, 0.2, 0.36])],
),
measure("measure.depth", "2,700 m", [0.06, 0.06, 0.16, 0.08], 2),
],
}
@@ -540,10 +849,13 @@ LABELS: dict[str, list[dict]] = {
# data above; this maps measurement keys to nothing extra (their value IS the string).
def _affect_for_clip(scale: str) -> tuple[list, dict]:
"""Build the affect list + its strings for a scale's pool member."""
def _affect_for_clip(scale: str, clip_id: str) -> tuple[list, dict]:
"""Build the affect list + its strings for a clip. A per-clip override in
AFFECT_CLIP (feelings drawn from THAT footage) wins; otherwise the scale's
shared register applies."""
source = AFFECT_CLIP.get(clip_id, AFFECT[scale])
entries, strings = [], {}
for key, at, min_level, tiers in AFFECT[scale]:
for key, at, min_level, tiers in source:
entries.append({"key": key, "at": at, "min_level": min_level})
strings[key] = tiers
return entries, strings
@@ -562,7 +874,7 @@ def _labels_for_clip(clip_id: str) -> tuple[list, dict]:
def _clip_entry(scale: str, clip_id: str) -> dict:
title, license_, source = META[clip_id]
anns, lab_strings = _labels_for_clip(clip_id)
affect, aff_strings = _affect_for_clip(scale)
affect, aff_strings = _affect_for_clip(scale, clip_id)
return {
"id": clip_id,
"title": title,
+32
View File
@@ -164,6 +164,38 @@ test("a landed clip loops from the morph-tail offset (no jump-back to frame 0)",
expect(v.t).toBeGreaterThanOrEqual(2.5); // looping from ~3s, not 0
});
test("ascending re-lands anchored at the clip head (no reverse-landing jump)", async ({ page }) => {
// D3: a morph spans src@0 (frac 0) -> dst@~3s (frac 1). Descending lands on dst at its
// tail (~3s); ASCENDING lands on src at its HEAD (~0s). The steady loop must continue
// from 0 there — seeking to the tail (~3s) was the ~3s reverse-landing jump.
await boot(page);
await enableVideo(page); // base loop only loads when video is shown
// Descend one altitude (cosmos -> orbit), then ascend back (orbit -> cosmos): the
// ascent is the reverse landing under test.
const start = (await page.locator("#scale-name").textContent())!;
await wheelOnStage(page, 60); // descend
await page.waitForFunction((s) => document.querySelector("#scale-name")?.textContent !== s, start, { timeout: 20000 });
const mid = (await page.locator("#scale-name").textContent())!;
expect(mid).toContain("orbit");
await wheelOnStage(page, -60); // ascend back
await page.waitForFunction((s) => document.querySelector("#scale-name")?.textContent !== s, mid, { timeout: 20000 });
expect(await page.locator("#scale-name").textContent()).toContain("cosmos");
// The loop element is now anchored at the HEAD (loopStart "0"), playing — not jumped to
// the ~3s tail the descending case uses.
await page.waitForFunction(
() => { const v = document.querySelector("#vid-loop") as HTMLVideoElement; return v.dataset.loopTail === "1" && v.dataset.loopStart === "0"; },
null,
{ timeout: 10000 },
);
const v = await page.evaluate(() => {
const el = document.querySelector("#vid-loop") as HTMLVideoElement;
return { loopStart: el.dataset.loopStart, loopTail: el.dataset.loopTail, paused: el.paused };
});
expect(v.loopTail).toBe("1"); // tail-loop machinery armed
expect(v.loopStart).toBe("0"); // anchored at the head, not the morph tail
expect(v.paused).toBe(false); // the loop is actually playing
});
test("zoom in plays the matching morph, lands locked, and stays put while parked", async ({ page }) => {
await boot(page);
const start = (await page.locator("#scale-name").textContent())!;
File diff suppressed because it is too large Load Diff
+116 -22
View File
@@ -238,20 +238,42 @@ function preloadChip() {
// to the loop's first frame, and the morph-covered intro never re-shows at rest.
const LOOP_TAIL_S = 3.0;
// Load a clip's base into the LOOP element (#vid-loop), seeked to the tail and PAUSED
// on that exact frame. Idempotent per clip. Called during a scrub to PRELOAD the clip
// we're about to land on, so settle just plays an already-decoded element at the morph's
// last frame — no reload, no seek, no stall. `playLoop()` starts it at landing.
function loadLoop(clip) {
if (!clip || loopVid.dataset.clip === clip.id) return;
// The frame the current loop both LANDS on and WRAPS back to — stored per load so the
// wrap handlers honor a direction-aware landing (forward=LOOP_TAIL_S, reverse=0).
function loopStartFrame() {
const s = parseFloat(loopVid.dataset.loopStart);
return Number.isFinite(s) ? s : LOOP_TAIL_S;
}
// Load a clip's base into the LOOP element (#vid-loop), seeked to `landFrame` and PAUSED
// on that exact frame. `landFrame` is the frame the morph last showed for THIS landing:
// LOOP_TAIL_S when descending (lands on the morph's dst-tail), 0 when ascending (lands on
// the morph's src-head — fixes the D3 reverse-landing jump). Omitted → LOOP_TAIL_S, the
// forward default for non-scrub paths (initial load, knob changes). Idempotent per clip;
// an explicit `landFrame` re-seeks the already-loaded clip if the travel direction flipped
// the landing phase, but a bare call (ensureClipMedia) never clobbers a phase a scrub set.
// Called during a scrub to PRELOAD the clip we're about to land on, so settle just plays
// an already-decoded element at the right frame — no reload, no seek, no stall.
function loadLoop(clip, landFrame) {
if (!clip) return;
const seekTo = (t) => { try { loopVid.currentTime = t; } catch (e) { /* not seekable yet */ } };
if (loopVid.dataset.clip === clip.id) {
if (landFrame != null && loopVid.dataset.loopStart !== String(landFrame)) {
loopVid.dataset.loopStart = String(landFrame);
if (loopVid.readyState >= 1) seekTo(landFrame);
else loopVid.addEventListener("loadedmetadata", () => seekTo(landFrame), { once: true });
}
return;
}
const lf = landFrame == null ? LOOP_TAIL_S : landFrame;
loopVid.dataset.clip = clip.id;
loopVid.dataset.loopTail = "1"; // arm the [LOOP_TAIL_S, duration] wrap handler
loopVid.dataset.loopTail = "1"; // arm the [loopStart, duration] wrap handler
loopVid.dataset.loopStart = String(lf);
loopVid.src = mediaUrl(clip.base_file);
loopVid.loop = false; // native loop restarts at 0; we loop from the tail instead
loopVid.loop = false; // native loop restarts at 0; we loop from loopStart
loopVid.muted = true;
const seek = () => { try { loopVid.currentTime = LOOP_TAIL_S; } catch (e) { /* not seekable yet */ } };
if (loopVid.readyState >= 1) seek();
else loopVid.addEventListener("loadedmetadata", seek, { once: true });
if (loopVid.readyState >= 1) seekTo(lf);
else loopVid.addEventListener("loadedmetadata", () => seekTo(lf), { once: true });
}
function playLoop() {
@@ -267,21 +289,22 @@ function ensureClipMedia() {
}
// The custom loop on the LOOP element: when a base loop reaches the end, jump back to
// the tail offset (not 0). The morph element (#vid) is driven directly by the scrub.
// its landing offset (LOOP_TAIL_S forward, 0 reverse — see loopStartFrame), not a fixed
// tail. The morph element (#vid) is driven directly by the scrub.
loopVid.addEventListener("timeupdate", () => {
if (loopVid.dataset.loopTail === "1" && loopVid.duration && loopVid.currentTime >= loopVid.duration - 0.08) {
try { loopVid.currentTime = LOOP_TAIL_S; } catch (e) { /* not seekable */ }
try { loopVid.currentTime = loopStartFrame(); } catch (e) { /* not seekable */ }
}
});
// Safety net: the timeupdate wrap above is a narrow window (the last 0.08 s). Under
// real-browser decode/GPU load a `timeupdate` can skip past it, the clip fires
// `ended`, and — with native loop off — freezes on its last frame ("not looping").
// `ended` GUARANTEES recovery: seek back to the tail and resume. Headless rarely
// hits this; loaded real machines do.
// `ended` GUARANTEES recovery: seek back to the landing offset and resume. Headless
// rarely hits this; loaded real machines do.
loopVid.addEventListener("ended", () => {
if (loopVid.dataset.loopTail !== "1") return;
try { loopVid.currentTime = LOOP_TAIL_S; } catch (e) { /* not seekable */ }
try { loopVid.currentTime = loopStartFrame(); } catch (e) { /* not seekable */ }
loopVid.play().catch(() => {});
});
@@ -560,6 +583,49 @@ function renderOverlay(level, intensity) {
// knob no longer gates them). `intensity` is the layer opacity. Words are placed at authored
// scene points (no boxes — feelings are scene-level) and read softer than the
// clinical reticles — feeling, not measurement.
// --- Keep Feel (affect) words clear of Think (label) chips -------------------
// Feelings are scene-level (soft position), so we nudge THEM off the analytical
// chip plates rather than move a chip off its subject. Obstacles are computed
// from the manifest (not the DOM) so the clearance holds for the WHOLE loop: a
// tracked chip's drift is sampled, so the per-frame track re-render never slides
// a chip under an already-placed word. Mirrors chip()'s plate geometry.
const FS_CHIP = 2.4, PAD_CHIP = 0.6;
function chipPlateRect(bx, by, textLen) {
const w = textLen * FS_CHIP * 0.6 + PAD_CHIP * 2;
const h = FS_CHIP + PAD_CHIP * 1.4;
const cy = Math.max(by - h - 0.6, 0.4); // plate sits just above the box top
return { x: bx, y: cy, w, h };
}
function rectsOverlap(a, b, pad) {
pad = pad || 0;
return a.x < b.x + b.w + pad && a.x + a.w + pad > b.x &&
a.y < b.y + b.h + pad && a.y + a.h + pad > b.y;
}
// Plate rects for every left label visible at `level`; a tracked label's path is
// sampled across the loop so its whole drift envelope is treated as occupied.
function leftLabelPlates(clip, level) {
const out = [];
if (!clip || !clip.annotations || level <= 0) return out;
const strings = HEFi18n.resolveStrings(clip.strings, activeLang);
for (const a of clip.annotations) {
if (level < firstLevel(a)) continue;
const measure = a.key.startsWith("measure.");
const raw = strings[a.key];
const tier = measure ? 1 : clamp(level - firstLevel(a) + 1, 1, tierCount(raw));
const label = String(pickTier(raw !== undefined ? raw : a.key, tier) || "");
const len = label.length + (measure ? 0 : 5); // detections append " 0.xx"
const ts = (a.track && a.track.length) ? [0, 0.2, 0.4, 0.6, 0.8, 1] : [0];
for (const tt of ts) {
const b = boxAt(a, tt);
out.push(chipPlateRect(b[0] * 100, b[1] * 100, len));
}
}
// The global "◉ ANALYSIS · L… · … OBJ" status tag (top-right) shows whenever any
// label does — reserve its corner so feelings don't collide with it either.
if (out.length) out.push({ x: 64, y: 2, w: 34, h: 6 });
return out;
}
function renderAffect(strength, intensity, right) {
lastAffect = { strength, intensity, right };
const clip = activeClip();
@@ -568,14 +634,36 @@ function renderAffect(strength, intensity, right) {
if (!clip || !clip.affect || strength <= 0) { affectLayer.style.opacity = "0"; return; }
affectLayer.style.opacity = String(intensity);
const strings = HEFi18n.resolveStrings(clip.strings, activeLang);
// Chip plates to avoid (left labels visible at the current Left level), plus the
// words placed so far so feelings don't stack on each other either.
const obstacles = leftLabelPlates(clip, lastOverlay ? lastOverlay.level : 0);
const placed = [];
for (const f of clip.affect) {
if (f.min_level > strength) continue;
const [x, y] = f.at.map((n) => n * 100);
const t = svg("text", { x, y, "text-anchor": "middle", class: "hud-affect" }, affectLayer);
const [x, y0] = f.at.map((n) => n * 100);
const t = svg("text", { x, y: y0, "text-anchor": "middle", class: "hud-affect" }, affectLayer);
// RIGHT emotion tier: the vocabulary escalates basic -> compound as the Right
// knob rises (appearance is gated by the Right knob via `strength` above).
const raw = strings[f.key];
t.textContent = pickTier(raw !== undefined ? raw : f.key, clamp(right || 0, 1, tierCount(raw)));
// Nudge vertically off any chip plate / already-placed word, staying on-screen;
// keep the least-overlapping spot if nothing is fully clear.
const bb0 = t.getBBox();
// Diagnostic seam (sibling of window.__hefState): disable nudging to A/B the
// overlap-avoidance in tests.
const deconflict = !(typeof window !== "undefined" && window.__hefNoDeconflict);
if (deconflict && bb0.width) {
let bestY = y0, bestHits = Infinity;
for (const dy of [0, 4, -4, 8, -8, 12, -12, 16, -16, 20, -20]) {
const yy = clamp(y0 + dy, 4, 96);
const r = { x: bb0.x, y: bb0.y + (yy - y0), w: bb0.width, h: bb0.height };
const hits = obstacles.concat(placed).filter((o) => rectsOverlap(r, o, 0.4)).length;
if (hits < bestHits) { bestHits = hits; bestY = yy; if (hits === 0) break; }
}
if (bestY !== y0) t.setAttribute("y", bestY);
}
const bb = t.getBBox();
placed.push({ x: bb.x, y: bb.y, w: bb.width, h: bb.height });
}
}
@@ -835,7 +923,12 @@ function setPos(next) {
busy = false;
delete vid.dataset.morph; // clear so re-entering the same segment reloads the morph (not the base)
currentClipId = null; // force ensureClipMedia to reload + loop the locked clip
playLoop(); // the loop element was preloaded to this clip @ tail during the scrub → instant, no reload
// Land the loop on the frame the morph last showed for THIS travel direction:
// descending lands on the morph's dst-tail (LOOP_TAIL_S), ascending on its src-head
// (0). Explicit (not relying on the scrub's preload) so a reverse landing never
// jumps even if the heading clip wasn't warmed in time. (D3 fix.)
loadLoop(activeClip(), HEFScrub.loopLandFrame(scrubDir, LOOP_TAIL_S));
playLoop(); // the loop element was preloaded to this clip during the scrub → instant, no reload
showActiveSource();
setNeedle(ringIndex * dialStep());
renderScaleReadout();
@@ -846,10 +939,11 @@ function setPos(next) {
}
busy = true; // mid-morph: block update() from reloading the base clip under us
if (!activeSeg || activeSeg.lo !== lo) rebuildSegment(lo, ringIndex);
// Preload the clip we're heading toward into the LOOP element (paused @ tail) so the
// landing is a swap, not a reload+seek. dir>0 lands on clipHi, dir<0 on clipLo.
// Preload the clip we're heading toward into the LOOP element (paused on its landing
// frame) so the landing is a swap, not a reload+seek. dir>0 lands on clipHi at the
// morph's dst-tail (LOOP_TAIL_S); dir<0 lands on clipLo at the morph's src-head (0).
const headingId = dir > 0 ? activeSeg.clipHi : activeSeg.clipLo;
loadLoop(clipsById[headingId]);
loadLoop(clipsById[headingId], HEFScrub.loopLandFrame(dir, LOOP_TAIL_S));
showActiveSource(); // morph element is the live source while scrubbing
setNeedle(pos * dialStep());
blendAudio(lo, frac);
+13 -1
View File
@@ -29,6 +29,18 @@
return { from: 1 - f, to: f };
}
// The base-loop frame a landing must continue from so the steady loop picks up
// the EXACT frame the morph last showed — no jump. A morph spans src@0 (frac 0)
// -> dst@loopTailS (frac 1), scrubbed bidirectionally. Descending (dir>0) lands
// on dst at frac 1, whose frame is dst@loopTailS, so the loop resumes at loopTailS
// (and the morph-covered intro never re-shows). Ascending (dir<0) lands on src at
// frac 0, whose frame is src@0, so the loop must resume at 0 — seeking to loopTailS
// there is the ~3s reverse-landing jump (D3). dir 0 defaults to the forward/tail
// case (matches the loadLoop default used by non-scrub paths).
function loopLandFrame(dir, loopTailS) {
return dir < 0 ? 0 : loopTailS;
}
// Integers strictly crossed moving prevPos -> pos, in travel order. Landing
// exactly on an integer counts as crossing it (it commits that altitude).
function integerCrossings(prevPos, pos) {
@@ -41,5 +53,5 @@
return out;
}
return { clamp01, wrapIndex, segmentOf, accumToPos, fracToTime, crossfadeGains, integerCrossings };
return { clamp01, wrapIndex, segmentOf, accumToPos, fracToTime, crossfadeGains, loopLandFrame, integerCrossings };
});
+14
View File
@@ -58,3 +58,17 @@ test("integerCrossings: multiple crossings in travel order", () => {
assert.deepEqual(S.integerCrossings(2.4, 4.1), [{ index: 3, dir: 1 }, { index: 4, dir: 1 }]);
assert.deepEqual(S.integerCrossings(3.2, 1.8), [{ index: 3, dir: -1 }, { index: 2, dir: -1 }]);
});
test("loopLandFrame: descend lands on the morph's tail frame, ascend on its head", () => {
// A morph spans src@0 (frac 0) -> dst@loopTailS (frac 1). Descending (dir>0) lands
// on dst at frac 1 -> the base loop must continue from loopTailS. Ascending (dir<0)
// lands on src at frac 0 -> the loop must continue from 0 (NOT loopTailS — that was
// the D3 reverse-landing jump).
assert.equal(S.loopLandFrame(1, 3), 3); // descend -> tail
assert.equal(S.loopLandFrame(-1, 3), 0); // ascend -> head
// dir 0 (no travel) is treated as a forward/tail landing (the default everywhere else).
assert.equal(S.loopLandFrame(0, 3), 3);
// honors any tail value
assert.equal(S.loopLandFrame(1, 2.5), 2.5);
assert.equal(S.loopLandFrame(-1, 2.5), 0);
});
File diff suppressed because it is too large Load Diff
+658 -15
View File
@@ -36,6 +36,12 @@
"anhelo",
"añoranza",
"una añoranza punzante"
],
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
]
},
"cosmos_galaxies": {
@@ -69,6 +75,13 @@
"anhelo",
"añoranza",
"una añoranza punzante"
],
"measure.count": "~2 billones de galaxias",
"feel.distance": [
"lejos",
"distancia",
"lejanía",
"una lejanía exquisita"
]
},
"cosmos_orion": {
@@ -108,7 +121,19 @@
"estrella del Trapecio",
"Trapecio · cálidas estrellas O que iluminan la nebulosa"
],
"measure.distance": "≈1.344 al"
"measure.distance": "≈1.344 al",
"feel.tenderness": [
"suave",
"ternura",
"calidez",
"la calidez de una cuna"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
]
},
"cosmos_tarantula": {
"feel.wonder": [
@@ -147,7 +172,13 @@
"R136",
"R136 · reúne algunas de las estrellas más masivas conocidas"
],
"measure.distance": "≈160.000 al"
"measure.distance": "≈160.000 al",
"feel.turbulence": [
"agitación",
"turbulencia",
"fermento",
"un fermento violento"
]
},
"cosmos_westerlund": {
"feel.wonder": [
@@ -186,7 +217,19 @@
"estrella O-type",
"O-type · blanca azulada, decenas de masas solares"
],
"measure.distance": "≈20.000 al"
"measure.distance": "≈20.000 al",
"feel.radiance": [
"brillo",
"resplandor",
"fulgor",
"un fulgor de joyas"
],
"feel.exhilaration": [
"yupi",
"euforia",
"júbilo",
"un júbilo que se eleva"
]
},
"cosmos_southernring": {
"feel.wonder": [
@@ -225,7 +268,19 @@
"enana blanca",
"enana blanca · el núcleo estelar caliente que queda"
],
"measure.distance": "≈2.500 al"
"measure.distance": "≈2.500 al",
"feel.mortality": [
"fin",
"mortalidad",
"fugacidad",
"la lenta muerte de una estrella"
],
"feel.poignancy": [
"punzada",
"desgarro",
"melancolía dulce",
"una despedida luminosa"
]
},
"cosmos_carina_eso": {
"feel.wonder": [
@@ -264,7 +319,13 @@
"estrella masiva",
"estrella masiva · la que esculpe los acantilados de Carina"
],
"measure.distance": "≈7.500 al"
"measure.distance": "≈7.500 al",
"feel.abundance": [
"lleno",
"abundancia",
"riqueza",
"una riqueza rebosante"
]
},
"orbit_planetearth": {
"detected.cloud_band": [
@@ -303,6 +364,18 @@
"distancia",
"lejanía",
"una lejanía exquisita"
],
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.radiance": [
"brillo",
"resplandor",
"fulgor",
"un fulgor de joyas"
]
},
"orbit_bluemarble": {
@@ -312,6 +385,18 @@
"planeta terrestre",
"planeta terrestre · 12.742 km de diámetro"
],
"detected.cloud_band": [
"nubes",
"sistemas meteorológicos",
"sistemas nubosos",
"sistemas nubosos globales · las nubes cubren ~67% de la Tierra"
],
"detected.starfield": [
"estrellas",
"campo estelar",
"estrellas de fondo",
"campo estelar de fondo · un telón renderizado, sin escala"
],
"feel.serenity": [
"calma",
"serenidad",
@@ -335,6 +420,12 @@
"distancia",
"lejanía",
"una lejanía exquisita"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"orbit_aurora2025": {
@@ -374,7 +465,25 @@
"limbo atmosférico",
"limbo atmosférico · ~100 km de aire, brillando de canto"
],
"measure.altitude": "~408 km"
"measure.altitude": "~408 km",
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
],
"feel.eeriness": [
"extraño",
"inquietud",
"una carga sobrenatural",
"un susurro eléctrico y sobrenatural"
]
},
"orbit_citylights": {
"feel.serenity": [
@@ -413,7 +522,19 @@
"luminiscencia atmosférica",
"luminiscencia del aire · la débil emisión nocturna de la alta atmósfera"
],
"measure.altitude": "~408 km"
"measure.altitude": "~408 km",
"feel.tenderness": [
"suave",
"ternura",
"calidez",
"la calidez de una cuna"
],
"feel.longing": [
"querer",
"anhelo",
"añoranza",
"una añoranza punzante"
]
},
"orbit_helene": {
"feel.serenity": [
@@ -452,7 +573,25 @@
"limbo atmosférico",
"limbo atmosférico · la fina capa donde vive el clima"
],
"measure.altitude": "~408 km"
"measure.altitude": "~408 km",
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
],
"feel.turbulence": [
"agitación",
"turbulencia",
"fermento",
"un fermento violento"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"orbit_epic": {
"feel.serenity": [
@@ -491,7 +630,13 @@
"sistemas meteorológicos",
"sistemas meteorológicos · arremolinándose sobre un planeta que gira"
],
"measure.distance": "~1,5 M km"
"measure.distance": "~1,5 M km",
"feel.insignificance": [
"pequeño",
"pequeñez",
"insignificancia",
"una insignificancia que humilla"
]
},
"sky_grca_templesa": {
"feel.exhilaration": [
@@ -529,6 +674,30 @@
"estratos de roca",
"lechos sedimentarios",
"estratos · épocas de sedimentación apiladas"
],
"detected.butte": [
"torre",
"cerro testigo",
"templo de roca",
"templo de roca · un cerro testigo de la erosión"
],
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"sky_greenland_landice": {
@@ -567,6 +736,30 @@
"nevero",
"neviza",
"neviza · nieve vieja que se compacta hacia hielo glaciar"
],
"detected.crevasse": [
"grietas",
"grietas",
"grietas glaciares",
"grietas · el hielo fracturándose al fluir"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
],
"feel.purity": [
"puro",
"pureza",
"quietud",
"un silencio glacial"
],
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
]
},
"sky_greenland_suture": {
@@ -605,6 +798,30 @@
"canal",
"canal abierto",
"canal · una fractura de agua abierta entre témpanos"
],
"detected.floe": [
"placas",
"témpanos",
"témpanos de banquisa",
"témpanos · placas de mar congelado a la deriva"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
],
"feel.solitude": [
"solo",
"soledad",
"aislamiento",
"una vasta soledad helada"
]
},
"sky_jungle_amazon": {
@@ -643,6 +860,24 @@
"árbol alto",
"árbol emergente",
"emergente · un gigante que rompe sobre el techo del dosel"
],
"detected.mist": [
"bruma",
"bruma del dosel",
"bruma de transpiración",
"bruma de transpiración · el bosque exhalando vapor"
],
"feel.verdancy": [
"frondoso",
"verdor",
"exuberancia",
"una exuberancia verde y rebosante"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
]
},
"sky_jungle_waterfall": {
@@ -681,6 +916,24 @@
"dosel de la jungla",
"dosel selvático",
"dosel · bosque denso que abarrota la garganta"
],
"detected.spray": [
"rocío",
"aerosol",
"rocío de caída",
"rocío de caída · el río pulverizado al impactar"
],
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.freshness": [
"fresco",
"frescura",
"vitalidad",
"una frescura que cae en cascada"
]
},
"sky_coast_cliffspain": {
@@ -719,6 +972,30 @@
"rompiente",
"oleaje rompiente",
"oleaje rompiente · el océano que encuentra la piedra"
],
"detected.headland": [
"punta",
"promontorio",
"promontorio rocoso",
"promontorio · un brazo de tierra acantilado en el mar"
],
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
],
"feel.turbulence": [
"agitación",
"turbulencia",
"fermento",
"un fermento violento"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"sky_mtn_castlecrags": {
@@ -757,6 +1034,24 @@
"bosque de coníferas",
"bosque montano",
"bosque montano · cubriendo las laderas bajo los riscos"
],
"detected.talus": [
"pedrera",
"talud",
"talud de derrubios",
"talud · roca rota por el hielo amontonada bajo los riscos"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"sky_mtn_rocky": {
@@ -795,6 +1090,24 @@
"tundra alpina",
"tundra alpina",
"tundra alpina · plantas en cojín bajas sobre los árboles"
],
"detected.snowfield": [
"nieve",
"nevero",
"nevero alpino",
"nevero alpino · nieve persistente de gran altitud"
],
"feel.sublime": [
"grandioso",
"lo sublime",
"grandeza",
"una grandeza catedralicia"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"coast_birdrock": {
@@ -833,6 +1146,24 @@
"melancolía",
"anhelo",
"un suave anhelo hacia el mar"
],
"detected.seabirds": [
"aves",
"aves marinas",
"aves marinas anidando",
"colonia de aves marinas · el criadero que da nombre a la roca"
],
"feel.fragility": [
"fino",
"fragilidad",
"vulnerabilidad",
"una fragilidad tierna"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
]
},
"coast_surfgrass": {
@@ -871,6 +1202,30 @@
"melancolía",
"anhelo",
"un suave anhelo hacia el mar"
],
"detected.tidepool": [
"charca",
"poza de marea",
"poza intermareal",
"poza de marea · un mar en miniatura al bajar la marea"
],
"feel.abundance": [
"lleno",
"abundancia",
"riqueza",
"una riqueza rebosante"
],
"feel.immersion": [
"dentro",
"inmersión",
"absorción",
"una absorción contenida y sin aliento"
],
"feel.curiosity": [
"mira",
"curiosidad",
"fascinación",
"una fascinación absorta"
]
},
"coast_kelp": {
@@ -909,6 +1264,36 @@
"lámina",
"fronda de alga",
"fronda · flotadores llenos de gas la mantienen erguida"
],
"detected.pneumatocyst": [
"flotadores",
"vejigas de gas",
"neumatocistos",
"neumatocistos · flotadores de gas que alzan las hojas a la luz"
],
"feel.immersion": [
"dentro",
"inmersión",
"absorción",
"una absorción contenida y sin aliento"
],
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
],
"feel.freedom": [
"libre",
"libertad",
"liberación",
"una liberación sin límites"
]
},
"coast_otters": {
@@ -947,6 +1332,24 @@
"estuario",
"estero de marea",
"estero de marea · aguas resguardadas de crianza"
],
"detected.fur": [
"pelaje",
"pelaje denso",
"el pelaje más denso",
"el pelaje más denso de la Tierra · ~1 M de pelos/in², sin grasa"
],
"feel.tenderness": [
"suave",
"ternura",
"calidez",
"la calidez de una cuna"
],
"feel.delight": [
"divertido",
"deleite",
"alegría",
"una alegría viva y centelleante"
]
},
"coast_kalaloch": {
@@ -985,6 +1388,18 @@
"farallón",
"farallón costero",
"farallón · roca residual tallada por las olas"
],
"detected.sunset": [
"resplandor",
"atardecer",
"hora dorada",
"hora dorada · el sol bajo enrojecido por más atmósfera"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
]
},
"coast_seals": {
@@ -1023,6 +1438,30 @@
"roca de descanso",
"roca intermareal",
"descansadero · una repisa de descanso bañada por la marea"
],
"detected.whiskers": [
"bigotes",
"vibrisas",
"bigotes sensores",
"vibrisas · bigotes que detectan presas en agua turbia"
],
"feel.tenderness": [
"suave",
"ternura",
"calidez",
"la calidez de una cuna"
],
"feel.repose": [
"descanso",
"reposo",
"sopor",
"un sopor entibiado por el sol"
],
"feel.delight": [
"divertido",
"deleite",
"alegría",
"una alegría viva y centelleante"
]
},
"coast_mist": {
@@ -1061,6 +1500,30 @@
"roca de orilla",
"roca costera",
"roca costera · el borde firme de la tierra"
],
"detected.swell": [
"olas",
"mar de fondo",
"oleaje de fondo",
"mar de fondo · olas formadas por tormentas lejanas"
],
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
],
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.hush": [
"silencio",
"quietud",
"sosiego",
"un silencio contenido"
]
},
"reef_lionfish": {
@@ -1100,6 +1563,18 @@
"inmersión",
"absorción",
"una absorción contenida y sin aliento"
],
"feel.poise": [
"quieto",
"aplomo",
"gracia",
"un aplomo suspendido y ornado"
],
"feel.fragility": [
"fino",
"fragilidad",
"vulnerabilidad",
"una fragilidad tierna"
]
},
"reef_spawning": {
@@ -1139,6 +1614,18 @@
"inmersión",
"absorción",
"una absorción contenida y sin aliento"
],
"feel.flow": [
"fluir",
"flujo",
"corriente",
"un flujo de cardumen que barre"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"reef_hawkfish": {
@@ -1172,6 +1659,18 @@
"inmersión",
"absorción",
"una absorción contenida y sin aliento"
],
"detected.coral": [
"coral",
"coral de arrecife",
"coral duro",
"coral duro · el arrecife que este pez muele en arena"
],
"feel.radiance": [
"brillo",
"resplandor",
"fulgor",
"un fulgor de joyas"
]
},
"reef_coralspacific": {
@@ -1210,6 +1709,24 @@
"inmersión",
"absorción",
"una absorción contenida y sin aliento"
],
"detected.polyp": [
"pólipos",
"pólipos de coral",
"pólipos vivos",
"pólipos · cada uno un animal diminuto, constructores de la colonia"
],
"feel.intricacy": [
"fino",
"complejidad",
"detalle",
"una densidad intrincada y tejida"
],
"feel.fragility": [
"fino",
"fragilidad",
"vulnerabilidad",
"una fragilidad tierna"
]
},
"reef_redsea": {
@@ -1249,7 +1766,19 @@
"antias",
"antias · nubes naranjas de comeplancton sobre el arrecife"
],
"measure.depth": "10 m"
"measure.depth": "10 m",
"feel.serenity": [
"calma",
"serenidad",
"paz",
"una paz serena e ingrávida"
],
"feel.radiance": [
"brillo",
"resplandor",
"fulgor",
"un fulgor de joyas"
]
},
"reef_flowergarden": {
"feel.delight": [
@@ -1288,7 +1817,13 @@
"Abudefduf saxatilis",
"Abudefduf · la damisela rayada “sargento mayor”"
],
"measure.depth": "20 m"
"measure.depth": "20 m",
"feel.freedom": [
"libre",
"libertad",
"liberación",
"una liberación sin límites"
]
},
"abyss_wow": {
"detected.jelly": [
@@ -1327,6 +1862,12 @@
"pavor",
"presagio",
"un presagio lento y frío"
],
"feel.vastness": [
"grande",
"vastedad",
"inmensidad",
"una inmensidad vertiginosa"
]
},
"abyss_midwaterexp": {
@@ -1366,6 +1907,18 @@
"pavor",
"presagio",
"un presagio lento y frío"
],
"feel.fragility": [
"fino",
"fragilidad",
"vulnerabilidad",
"una fragilidad tierna"
],
"feel.poignancy": [
"punzada",
"desgarro",
"melancolía dulce",
"una despedida luminosa"
]
},
"abyss_hiding": {
@@ -1399,6 +1952,24 @@
"pavor",
"presagio",
"un presagio lento y frío"
],
"detected.marinesnow": [
"motas",
"nieve marina",
"detrito que cae",
"nieve marina · restos orgánicos que descienden desde arriba"
],
"feel.spectral": [
"fantasma",
"lo espectral",
"lo fantasmal",
"un fantasma en el agua"
],
"feel.fragility": [
"fino",
"fragilidad",
"vulnerabilidad",
"una fragilidad tierna"
]
},
"abyss_bigfin": {
@@ -1432,7 +2003,19 @@
"Magnapinna",
"Magnapinna · brazos acodados que se extienden metros en la oscuridad"
],
"measure.depth": "2.000 m"
"measure.depth": "2.000 m",
"detected.arms": [
"brazos",
"brazos acodados",
"filamentos colgantes",
"brazos acodados · extendidos y luego colgando metros de hilo"
],
"feel.alienness": [
"raro",
"lo alienígena",
"extrañeza",
"una gracia alienígena"
]
},
"abyss_dandelion": {
"feel.unease": [
@@ -1465,7 +2048,31 @@
"sifonóforo diente de león",
"Rhodaliidae · una colonia de clones anclada al lecho marino"
],
"measure.depth": "2.500 m"
"measure.depth": "2.500 m",
"detected.tentacle": [
"hilos",
"tentáculos",
"tentáculos de caza",
"tentáculos de caza · una red a la deriva para atrapar presas"
],
"feel.radiance": [
"brillo",
"resplandor",
"fulgor",
"un fulgor de joyas"
],
"feel.wonder": [
"guau",
"asombro",
"sobrecogimiento",
"sobrecogimiento trascendente"
],
"feel.fragility": [
"fino",
"fragilidad",
"vulnerabilidad",
"una fragilidad tierna"
]
},
"abyss_octopus": {
"feel.unease": [
@@ -1498,7 +2105,25 @@
"Graneledone",
"Graneledone boreopacifica · incuba sus huevos durante ~4,5 años"
],
"measure.depth": "2.500 m"
"measure.depth": "2.500 m",
"detected.arms": [
"brazos",
"ocho brazos",
"brazos con ventosas",
"ocho brazos · con ventosas que saborean lo que tocan"
],
"feel.curiosity": [
"mira",
"curiosidad",
"fascinación",
"una fascinación absorta"
],
"feel.tenderness": [
"suave",
"ternura",
"calidez",
"la calidez de una cuna"
]
},
"abyss_seapig": {
"feel.unease": [
@@ -1531,6 +2156,24 @@
"Enypniastes eximia",
"Enypniastes · un pepino de mar nadador, el “pollo sin cabeza”"
],
"measure.depth": "2.700 m"
"measure.depth": "2.700 m",
"detected.veil": [
"velo",
"velo oral",
"velo natatorio",
"velo oral · barre el sedimento y aletea para nadar"
],
"feel.strangeness": [
"raro",
"rareza",
"lo inquietante",
"una rareza carnosa"
],
"feel.curiosity": [
"mira",
"curiosidad",
"fascinación",
"una fascinación absorta"
]
}
}
+658 -15
View File
@@ -36,6 +36,12 @@
"désir",
"aspiration",
"une aspiration douloureuse"
],
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
]
},
"cosmos_galaxies": {
@@ -69,6 +75,13 @@
"désir",
"aspiration",
"une aspiration douloureuse"
],
"measure.count": "~2 000 milliards de galaxies",
"feel.distance": [
"loin",
"distance",
"éloignement",
"un éloignement exquis"
]
},
"cosmos_orion": {
@@ -108,7 +121,19 @@
"étoile du Trapèze",
"Trapèze · étoiles O chaudes illuminant la nébuleuse"
],
"measure.distance": "≈1 344 al"
"measure.distance": "≈1 344 al",
"feel.tenderness": [
"doux",
"tendresse",
"chaleur",
"la chaleur dun berceau"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
]
},
"cosmos_tarantula": {
"feel.wonder": [
@@ -147,7 +172,13 @@
"R136",
"R136 · concentre certaines des étoiles les plus massives connues"
],
"measure.distance": "≈160 000 al"
"measure.distance": "≈160 000 al",
"feel.turbulence": [
"remous",
"turbulence",
"ferment",
"un ferment violent"
]
},
"cosmos_westerlund": {
"feel.wonder": [
@@ -186,7 +217,19 @@
"étoile de type O",
"O-type · blanc-bleu, des dizaines de masses solaires"
],
"measure.distance": "≈20 000 al"
"measure.distance": "≈20 000 al",
"feel.radiance": [
"éclat",
"rayonnement",
"scintillement",
"un scintillement de joyaux"
],
"feel.exhilaration": [
"youpi",
"exaltation",
"euphorie",
"une euphorie qui s'envole"
]
},
"cosmos_southernring": {
"feel.wonder": [
@@ -225,7 +268,19 @@
"naine blanche",
"naine blanche · le cœur stellaire chaud laissé derrière"
],
"measure.distance": "≈2 500 al"
"measure.distance": "≈2 500 al",
"feel.mortality": [
"fin",
"mortalité",
"impermanence",
"la lente mort dune étoile"
],
"feel.poignancy": [
"pincement",
"émotion poignante",
"douceur amère",
"un adieu lumineux"
]
},
"cosmos_carina_eso": {
"feel.wonder": [
@@ -264,7 +319,13 @@
"étoile massive",
"étoile massive · celles qui sculptent les falaises de la Carène"
],
"measure.distance": "≈7 500 al"
"measure.distance": "≈7 500 al",
"feel.abundance": [
"plein",
"abondance",
"richesse",
"une richesse grouillante"
]
},
"orbit_planetearth": {
"detected.cloud_band": [
@@ -303,6 +364,18 @@
"distance",
"éloignement",
"un éloignement exquis"
],
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.radiance": [
"éclat",
"rayonnement",
"scintillement",
"un scintillement de joyaux"
]
},
"orbit_bluemarble": {
@@ -312,6 +385,18 @@
"planète tellurique",
"planète tellurique · 12 742 km de diamètre"
],
"detected.cloud_band": [
"nuages",
"systèmes météo",
"systèmes nuageux",
"systèmes nuageux planétaires · les nuages couvrent ~67% de la Terre"
],
"detected.starfield": [
"étoiles",
"champ d’étoiles",
"étoiles darrière-plan",
"champ d’étoiles de fond · un décor rendu, hors échelle"
],
"feel.serenity": [
"calme",
"sérénité",
@@ -335,6 +420,12 @@
"distance",
"éloignement",
"un éloignement exquis"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"orbit_aurora2025": {
@@ -374,7 +465,25 @@
"limbe atmosphérique",
"limbe atmosphérique · ~100 km d'air, brillant sur la tranche"
],
"measure.altitude": "~408 km"
"measure.altitude": "~408 km",
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
],
"feel.eeriness": [
"étrange",
"étrangeté",
"une charge surnaturelle",
"un frisson électrique et surnaturel"
]
},
"orbit_citylights": {
"feel.serenity": [
@@ -413,7 +522,19 @@
"luminescence atmosphérique",
"luminescence atmosphérique · la faible émission nocturne de la haute atmosphère"
],
"measure.altitude": "~408 km"
"measure.altitude": "~408 km",
"feel.tenderness": [
"doux",
"tendresse",
"chaleur",
"la chaleur dun berceau"
],
"feel.longing": [
"envie",
"désir",
"aspiration",
"une aspiration douloureuse"
]
},
"orbit_helene": {
"feel.serenity": [
@@ -452,7 +573,25 @@
"limbe atmosphérique",
"limbe atmosphérique · la fine pellicule où vit la météo"
],
"measure.altitude": "~408 km"
"measure.altitude": "~408 km",
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
],
"feel.turbulence": [
"remous",
"turbulence",
"ferment",
"un ferment violent"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"orbit_epic": {
"feel.serenity": [
@@ -491,7 +630,13 @@
"systèmes météorologiques",
"systèmes météorologiques · tourbillonnant sur une planète qui tourne"
],
"measure.distance": "~1,5 M km"
"measure.distance": "~1,5 M km",
"feel.insignificance": [
"petit",
"petitesse",
"insignifiance",
"une insignifiance qui rend humble"
]
},
"sky_grca_templesa": {
"feel.exhilaration": [
@@ -529,6 +674,30 @@
"strates rocheuses",
"bancs sédimentaires",
"strates · des époques de dépôt empilées"
],
"detected.butte": [
"tour",
"butte-témoin",
"temple rocheux",
"temple rocheux · une butte-témoin laissée par l’érosion"
],
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"sky_greenland_landice": {
@@ -567,6 +736,30 @@
"champ de neige",
"névé",
"névé · vieille neige se compactant en glace glaciaire"
],
"detected.crevasse": [
"fissures",
"crevasses",
"crevasses glaciaires",
"crevasses · la glace se fracture en s’écoulant"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
],
"feel.purity": [
"pur",
"pureté",
"quiétude",
"un silence glaciaire"
],
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
]
},
"sky_greenland_suture": {
@@ -605,6 +798,30 @@
"chenal",
"chenal libre",
"chenal · une fracture d'eau libre entre les floes"
],
"detected.floe": [
"plaques",
"floes",
"floes de banquise",
"floes · plaques de mer gelée à la dérive"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
],
"feel.solitude": [
"seul",
"solitude",
"isolement",
"une vaste solitude gelée"
]
},
"sky_jungle_amazon": {
@@ -643,6 +860,24 @@
"grand arbre",
"arbre émergent",
"émergent · un géant perçant au-dessus de la canopée"
],
"detected.mist": [
"brume",
"brume de canopée",
"brume de transpiration",
"brume de transpiration · la forêt exhalant sa vapeur"
],
"feel.verdancy": [
"luxuriant",
"verdure",
"abondance",
"une abondance verte et grouillante"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
]
},
"sky_jungle_waterfall": {
@@ -681,6 +916,24 @@
"canopée de jungle",
"canopée de forêt tropicale",
"canopée · forêt dense pressée autour de la gorge"
],
"detected.spray": [
"embruns",
"embruns",
"embruns de chute",
"embruns de chute · la rivière pulvérisée à limpact"
],
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.freshness": [
"frais",
"fraîcheur",
"vitalité",
"une fraîcheur en cascade"
]
},
"sky_coast_cliffspain": {
@@ -719,6 +972,30 @@
"ressac",
"houle déferlante",
"houle déferlante · l'océan à la rencontre de la pierre"
],
"detected.headland": [
"pointe",
"promontoire",
"promontoire rocheux",
"promontoire · un bras de terre escarpé dans la mer"
],
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
],
"feel.turbulence": [
"remous",
"turbulence",
"ferment",
"un ferment violent"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"sky_mtn_castlecrags": {
@@ -757,6 +1034,24 @@
"forêt de conifères",
"forêt montagnarde",
"forêt montagnarde · habillant les pentes sous les pics"
],
"detected.talus": [
"éboulis",
"talus",
"talus d’éboulis",
"éboulis · roches gélifractées entassées sous les aiguilles"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"sky_mtn_rocky": {
@@ -795,6 +1090,24 @@
"toundra alpine",
"toundra alpine",
"toundra alpine · plantes en coussin basses au-dessus des arbres"
],
"detected.snowfield": [
"neige",
"névé",
"névé alpin",
"névé alpin · neige persistante de haute altitude"
],
"feel.sublime": [
"grandiose",
"le sublime",
"grandeur",
"une grandeur de cathédrale"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"coast_birdrock": {
@@ -833,6 +1146,24 @@
"mélancolie",
"langueur",
"une douce langueur tournée vers le large"
],
"detected.seabirds": [
"oiseaux",
"oiseaux marins",
"oiseaux marins nicheurs",
"colonie doiseaux marins · la rookerie qui nomme le rocher"
],
"feel.fragility": [
"mince",
"fragilité",
"vulnérabilité",
"une tendre fragilité"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
]
},
"coast_surfgrass": {
@@ -871,6 +1202,30 @@
"mélancolie",
"langueur",
"une douce langueur tournée vers le large"
],
"detected.tidepool": [
"mare",
"cuvette de marée",
"mare intertidale",
"cuvette de marée · une mer de poche découverte à marée basse"
],
"feel.abundance": [
"plein",
"abondance",
"richesse",
"une richesse grouillante"
],
"feel.immersion": [
"dedans",
"immersion",
"absorption",
"une absorption retenue, à couper le souffle"
],
"feel.curiosity": [
"regarde",
"curiosité",
"fascination",
"une fascination absorbée"
]
},
"coast_kelp": {
@@ -909,6 +1264,36 @@
"lame",
"fronde de varech",
"fronde · des flotteurs remplis de gaz la maintiennent droite"
],
"detected.pneumatocyst": [
"flotteurs",
"vésicules à gaz",
"pneumatocystes",
"pneumatocystes · des flotteurs à gaz qui hissent les lames vers la lumière"
],
"feel.immersion": [
"dedans",
"immersion",
"absorption",
"une absorption retenue, à couper le souffle"
],
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
],
"feel.freedom": [
"libre",
"liberté",
"délivrance",
"une délivrance sans limites"
]
},
"coast_otters": {
@@ -947,6 +1332,24 @@
"estuaire",
"chenal de marée",
"chenal de marée · eaux de nourricerie abritées"
],
"detected.fur": [
"fourrure",
"fourrure dense",
"la fourrure la plus dense",
"la fourrure la plus dense au monde · ~1 M de poils/in², sans graisse"
],
"feel.tenderness": [
"doux",
"tendresse",
"chaleur",
"la chaleur dun berceau"
],
"feel.delight": [
"amusant",
"ravissement",
"joie",
"une joie vive et frétillante"
]
},
"coast_kalaloch": {
@@ -985,6 +1388,18 @@
"aiguille marine",
"aiguille côtière",
"aiguille marine · roche résiduelle taillée par les vagues"
],
"detected.sunset": [
"lueur",
"coucher de soleil",
"heure dorée",
"heure dorée · le soleil bas rougi par plus datmosphère"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
]
},
"coast_seals": {
@@ -1023,6 +1438,30 @@
"rocher reposoir",
"rocher intertidal",
"reposoir · une corniche de repos balayée par la marée"
],
"detected.whiskers": [
"moustaches",
"vibrisses",
"moustaches sensorielles",
"vibrisses · des moustaches qui sentent les proies en eau trouble"
],
"feel.tenderness": [
"doux",
"tendresse",
"chaleur",
"la chaleur dun berceau"
],
"feel.repose": [
"repos",
"quiétude",
"somnolence",
"une somnolence réchauffée de soleil"
],
"feel.delight": [
"amusant",
"ravissement",
"joie",
"une joie vive et frétillante"
]
},
"coast_mist": {
@@ -1061,6 +1500,30 @@
"rocher de rivage",
"rocher côtier",
"rocher côtier · le bord dressé de la terre"
],
"detected.swell": [
"vagues",
"houle",
"houle océanique",
"houle · des vagues nées de tempêtes lointaines"
],
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
],
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.hush": [
"silence",
"calme",
"quiétude",
"un silence retenu"
]
},
"reef_lionfish": {
@@ -1100,6 +1563,18 @@
"immersion",
"absorption",
"une absorption retenue, à couper le souffle"
],
"feel.poise": [
"immobile",
"aplomb",
"grâce",
"un aplomb suspendu et ouvragé"
],
"feel.fragility": [
"mince",
"fragilité",
"vulnérabilité",
"une tendre fragilité"
]
},
"reef_spawning": {
@@ -1139,6 +1614,18 @@
"immersion",
"absorption",
"une absorption retenue, à couper le souffle"
],
"feel.flow": [
"aller",
"flux",
"courant",
"un flux de banc qui ondule"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"reef_hawkfish": {
@@ -1172,6 +1659,18 @@
"immersion",
"absorption",
"une absorption retenue, à couper le souffle"
],
"detected.coral": [
"corail",
"corail récifal",
"corail dur",
"corail dur · le récif que ce poisson broie en sable"
],
"feel.radiance": [
"éclat",
"rayonnement",
"scintillement",
"un scintillement de joyaux"
]
},
"reef_coralspacific": {
@@ -1210,6 +1709,24 @@
"immersion",
"absorption",
"une absorption retenue, à couper le souffle"
],
"detected.polyp": [
"polypes",
"polypes coralliens",
"polypes vivants",
"polypes · chacun un animal minuscule, bâtisseurs de la colonie"
],
"feel.intricacy": [
"fin",
"complexité",
"détail",
"une densité tissée et complexe"
],
"feel.fragility": [
"mince",
"fragilité",
"vulnérabilité",
"une tendre fragilité"
]
},
"reef_redsea": {
@@ -1249,7 +1766,19 @@
"anthias",
"anthias · nuages orange de mangeurs de plancton au-dessus du récif"
],
"measure.depth": "10 m"
"measure.depth": "10 m",
"feel.serenity": [
"calme",
"sérénité",
"paix",
"une paix tranquille et sans pesanteur"
],
"feel.radiance": [
"éclat",
"rayonnement",
"scintillement",
"un scintillement de joyaux"
]
},
"reef_flowergarden": {
"feel.delight": [
@@ -1288,7 +1817,13 @@
"Abudefduf saxatilis",
"Abudefduf · la demoiselle rayée “sergent-major”"
],
"measure.depth": "20 m"
"measure.depth": "20 m",
"feel.freedom": [
"libre",
"liberté",
"délivrance",
"une délivrance sans limites"
]
},
"abyss_wow": {
"detected.jelly": [
@@ -1327,6 +1862,12 @@
"effroi",
"pressentiment",
"un pressentiment lent et glacé"
],
"feel.vastness": [
"grand",
"vastitude",
"immensité",
"une immensité vertigineuse"
]
},
"abyss_midwaterexp": {
@@ -1366,6 +1907,18 @@
"effroi",
"pressentiment",
"un pressentiment lent et glacé"
],
"feel.fragility": [
"mince",
"fragilité",
"vulnérabilité",
"une tendre fragilité"
],
"feel.poignancy": [
"pincement",
"émotion poignante",
"douceur amère",
"un adieu lumineux"
]
},
"abyss_hiding": {
@@ -1399,6 +1952,24 @@
"effroi",
"pressentiment",
"un pressentiment lent et glacé"
],
"detected.marinesnow": [
"points",
"neige marine",
"détritus tombants",
"neige marine · débris organiques descendant des couches supérieures"
],
"feel.spectral": [
"fantôme",
"le spectral",
"hantise",
"un fantôme dans leau"
],
"feel.fragility": [
"mince",
"fragilité",
"vulnérabilité",
"une tendre fragilité"
]
},
"abyss_bigfin": {
@@ -1432,7 +2003,19 @@
"Magnapinna",
"Magnapinna · des bras coudés traînant sur des mètres dans l'obscurité"
],
"measure.depth": "2 000 m"
"measure.depth": "2 000 m",
"detected.arms": [
"bras",
"bras coudés",
"filaments traînants",
"bras coudés · tendus puis traînant des mètres de fil"
],
"feel.alienness": [
"bizarre",
"l’étrangeté",
"altérité",
"une grâce extraterrestre"
]
},
"abyss_dandelion": {
"feel.unease": [
@@ -1465,7 +2048,31 @@
"siphonophore pissenlit",
"Rhodaliidae · une colonie de clones amarrée au fond marin"
],
"measure.depth": "2 500 m"
"measure.depth": "2 500 m",
"detected.tentacle": [
"fils",
"tentacules",
"tentacules de capture",
"tentacules de capture · un filet dérivant pour les proies"
],
"feel.radiance": [
"éclat",
"rayonnement",
"scintillement",
"un scintillement de joyaux"
],
"feel.wonder": [
"ouah",
"émerveillement",
"saisissement",
"un saisissement transcendant"
],
"feel.fragility": [
"mince",
"fragilité",
"vulnérabilité",
"une tendre fragilité"
]
},
"abyss_octopus": {
"feel.unease": [
@@ -1498,7 +2105,25 @@
"Graneledone",
"Graneledone boreopacifica · couve ses œufs pendant ~4,5 ans"
],
"measure.depth": "2 500 m"
"measure.depth": "2 500 m",
"detected.arms": [
"bras",
"huit bras",
"bras à ventouses",
"huit bras · garnis de ventouses qui goûtent ce quelles touchent"
],
"feel.curiosity": [
"regarde",
"curiosité",
"fascination",
"une fascination absorbée"
],
"feel.tenderness": [
"doux",
"tendresse",
"chaleur",
"la chaleur dun berceau"
]
},
"abyss_seapig": {
"feel.unease": [
@@ -1531,6 +2156,24 @@
"Enypniastes eximia",
"Enypniastes · un concombre de mer nageur, le “poulet sans tête”"
],
"measure.depth": "2 700 m"
"measure.depth": "2 700 m",
"detected.veil": [
"voile",
"voile oral",
"voile natatoire",
"voile oral · balaie le sédiment et bat pour nager"
],
"feel.strangeness": [
"étrange",
"bizarrerie",
"linquiétant",
"une étrangeté charnue"
],
"feel.curiosity": [
"regarde",
"curiosité",
"fascination",
"une fascination absorbée"
]
}
}
+658 -15
View File
@@ -36,6 +36,12 @@
"憧れ",
"切望",
"胸を締めつける切望"
],
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
]
},
"cosmos_galaxies": {
@@ -69,6 +75,13 @@
"憧れ",
"切望",
"胸を締めつける切望"
],
"measure.count": "約2兆個の銀河",
"feel.distance": [
"遠い",
"隔たり",
"遥かさ",
"この上なく美しい遥かさ"
]
},
"cosmos_orion": {
@@ -108,7 +121,19 @@
"Trapezium星",
"Trapezium · 星雲を照らす高温のO-type星"
],
"measure.distance": "≈1,344 光年"
"measure.distance": "≈1,344 光年",
"feel.tenderness": [
"やさしい",
"優しさ",
"ぬくもり",
"ゆりかごのようなぬくもり"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
]
},
"cosmos_tarantula": {
"feel.wonder": [
@@ -147,7 +172,13 @@
"R136",
"R136 · 知られている中で最も重い部類の星々を抱える"
],
"measure.distance": "≈160,000 光年"
"measure.distance": "≈160,000 光年",
"feel.turbulence": [
"渦巻き",
"乱流",
"胎動",
"激しい胎動"
]
},
"cosmos_westerlund": {
"feel.wonder": [
@@ -186,7 +217,19 @@
"O-type星",
"O-type · 青白い、太陽の数十倍の質量"
],
"measure.distance": "≈20,000 光年"
"measure.distance": "≈20,000 光年",
"feel.radiance": [
"輝き",
"光輝",
"煌めき",
"宝石のような煌めき"
],
"feel.exhilaration": [
"わーい",
"高揚",
"歓喜",
"舞い上がる歓喜"
]
},
"cosmos_southernring": {
"feel.wonder": [
@@ -225,7 +268,19 @@
"白色矮星",
"白色矮星 · 残された高温の星の中心核"
],
"measure.distance": "≈2,500 光年"
"measure.distance": "≈2,500 光年",
"feel.mortality": [
"終わり",
"死",
"無常",
"星のゆるやかな死"
],
"feel.poignancy": [
"切なさ",
"哀切",
"ほろ苦さ",
"光に満ちた別れ"
]
},
"cosmos_carina_eso": {
"feel.wonder": [
@@ -264,7 +319,13 @@
"重い星",
"重い星 · カリーナの崖を彫り出すたぐいの星"
],
"measure.distance": "≈7,500 光年"
"measure.distance": "≈7,500 光年",
"feel.abundance": [
"いっぱい",
"豊かさ",
"充溢",
"あふれんばかりの充溢"
]
},
"orbit_planetearth": {
"detected.cloud_band": [
@@ -303,6 +364,18 @@
"隔たり",
"遥かさ",
"この上なく美しい遥かさ"
],
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.radiance": [
"輝き",
"光輝",
"煌めき",
"宝石のような煌めき"
]
},
"orbit_bluemarble": {
@@ -312,6 +385,18 @@
"岩石惑星",
"岩石惑星 · 直径12,742 km"
],
"detected.cloud_band": [
"雲",
"気象システム",
"雲システム",
"地球規模の雲システム · 雲は地表の約67%を覆う"
],
"detected.starfield": [
"星",
"星野",
"背景の星",
"背景の星野 · 描画された背景で実寸ではない"
],
"feel.serenity": [
"穏やか",
"静けさ",
@@ -335,6 +420,12 @@
"隔たり",
"遥かさ",
"この上なく美しい遥かさ"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"orbit_aurora2025": {
@@ -374,7 +465,25 @@
"大気の縁",
"大気の縁 · 約100 kmの大気層が縁で光る"
],
"measure.altitude": "約408 km"
"measure.altitude": "約408 km",
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
],
"feel.eeriness": [
"異様",
"不気味さ",
"異世界の気配",
"電気を帯びた異世界の静けさ"
]
},
"orbit_citylights": {
"feel.serenity": [
@@ -413,7 +522,19 @@
"超高層の大気光",
"大気光 · 高層大気がかすかに放つ夜間の光"
],
"measure.altitude": "約408 km"
"measure.altitude": "約408 km",
"feel.tenderness": [
"やさしい",
"優しさ",
"ぬくもり",
"ゆりかごのようなぬくもり"
],
"feel.longing": [
"欲しい",
"憧れ",
"切望",
"胸を締めつける切望"
]
},
"orbit_helene": {
"feel.serenity": [
@@ -452,7 +573,25 @@
"大気の縁",
"大気の縁 · 気象が宿る薄い層"
],
"measure.altitude": "約408 km"
"measure.altitude": "約408 km",
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
],
"feel.turbulence": [
"渦巻き",
"乱流",
"胎動",
"激しい胎動"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"orbit_epic": {
"feel.serenity": [
@@ -491,7 +630,13 @@
"気象系",
"気象系 · 回転する惑星の上を渦巻く"
],
"measure.distance": "約150万 km"
"measure.distance": "約150万 km",
"feel.insignificance": [
"小さい",
"小ささ",
"ちっぽけさ",
"謙虚にさせるちっぽけさ"
]
},
"sky_grca_templesa": {
"feel.exhilaration": [
@@ -529,6 +674,30 @@
"岩層",
"堆積層",
"地層 · 積み重なった堆積の時代"
],
"detected.butte": [
"塔",
"残丘",
"岩の神殿",
"岩の‘神殿’ · 浸食に取り残された残丘"
],
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"sky_greenland_landice": {
@@ -567,6 +736,30 @@
"雪原",
"フィルン",
"フィルン · 氷河の氷へと締まっていく古い雪"
],
"detected.crevasse": [
"亀裂",
"クレバス",
"氷河の裂け目",
"クレバス · 流れる氷が割れてできた裂け目"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
],
"feel.purity": [
"清らか",
"純粋さ",
"静寂",
"氷河のような静寂"
],
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
]
},
"sky_greenland_suture": {
@@ -605,6 +798,30 @@
"水路",
"リード",
"リード · 氷盤の間に開いた水の裂け目"
],
"detected.floe": [
"板状氷",
"氷盤",
"流氷の氷盤",
"氷盤 · 漂う凍った海の板"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
],
"feel.solitude": [
"ひとり",
"孤独",
"隔絶",
"凍てつく広大な孤独"
]
},
"sky_jungle_amazon": {
@@ -643,6 +860,24 @@
"高い木",
"突出木",
"突出木 · 樹冠の天井を突き抜ける巨木"
],
"detected.mist": [
"もや",
"林冠のもや",
"蒸散のもや",
"蒸散のもや · 森が吐き出す水蒸気"
],
"feel.verdancy": [
"緑豊か",
"青々しさ",
"繁茂",
"生い茂る緑の豊かさ"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
]
},
"sky_jungle_waterfall": {
@@ -681,6 +916,24 @@
"ジャングルの樹冠",
"熱帯雨林の樹冠",
"樹冠 · 峡谷に密集する濃い森"
],
"detected.spray": [
"水しぶき",
"しぶき",
"落下のしぶき",
"落下のしぶき · 衝突で霧化した川"
],
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.freshness": [
"涼しい",
"清々しさ",
"生命力",
"滝のように降りそそぐ清涼"
]
},
"sky_coast_cliffspain": {
@@ -719,6 +972,30 @@
"打ち寄せる波",
"砕けるうねり",
"砕けるうねり · 海が岩に出会う"
],
"detected.headland": [
"岬",
"岬",
"岩の岬",
"岬 · 海へ突き出す断崖の地"
],
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
],
"feel.turbulence": [
"渦巻き",
"乱流",
"胎動",
"激しい胎動"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"sky_mtn_castlecrags": {
@@ -757,6 +1034,24 @@
"針葉樹林",
"山地林",
"山地林 · 岩峰の下の斜面を覆う"
],
"detected.talus": [
"岩屑",
"タルス",
"岩屑の斜面",
"タルス · 凍結破砕した岩が岩峰の下に堆積"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"sky_mtn_rocky": {
@@ -795,6 +1090,24 @@
"ツンドラ",
"高山ツンドラ",
"高山ツンドラ · 樹林の上に育つ低いクッション状の植物"
],
"detected.snowfield": [
"雪",
"雪原",
"高山の雪原",
"高山の雪原 · 標高の高い場所に残る雪"
],
"feel.sublime": [
"雄大",
"崇高",
"荘厳",
"大聖堂のような荘厳さ"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"coast_birdrock": {
@@ -833,6 +1146,24 @@
"憂い",
"恋しさ",
"海へと向かう柔らかな恋しさ"
],
"detected.seabirds": [
"鳥",
"海鳥",
"営巣する海鳥",
"海鳥のコロニー · 岩の名の由来となる集団繁殖地"
],
"feel.fragility": [
"薄い",
"儚さ",
"脆さ",
"いたわりたくなる儚さ"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
]
},
"coast_surfgrass": {
@@ -871,6 +1202,30 @@
"憂い",
"恋しさ",
"海へと向かう柔らかな恋しさ"
],
"detected.tidepool": [
"潮だまり",
"タイドプール",
"潮間帯の池",
"潮だまり · 干潮で現れる小さな海"
],
"feel.abundance": [
"いっぱい",
"豊かさ",
"充溢",
"あふれんばかりの充溢"
],
"feel.immersion": [
"中へ",
"没入",
"没頭",
"息を呑む没頭"
],
"feel.curiosity": [
"見て",
"好奇心",
"魅了",
"のめり込む魅了"
]
},
"coast_kelp": {
@@ -909,6 +1264,36 @@
"葉状体",
"ケルプの葉",
"葉状部 · ガスを含んだ浮き袋が直立させる"
],
"detected.pneumatocyst": [
"浮き",
"気胞",
"気胞(ニューマトシスト)",
"気胞 · 葉を光へ持ち上げるガスの浮き"
],
"feel.immersion": [
"中へ",
"没入",
"没頭",
"息を呑む没頭"
],
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
],
"feel.freedom": [
"自由",
"自由さ",
"解放",
"果てしない解放"
]
},
"coast_otters": {
@@ -947,6 +1332,24 @@
"河口",
"潮汐の入り江",
"潮の入り江 · 守られた育成の水域"
],
"detected.fur": [
"毛皮",
"密な毛皮",
"世界一密な毛皮",
"世界一密な毛皮 · 約100万本/in²、脂肪層なし"
],
"feel.tenderness": [
"やさしい",
"優しさ",
"ぬくもり",
"ゆりかごのようなぬくもり"
],
"feel.delight": [
"楽しい",
"喜び",
"歓び",
"ちらちらと輝く歓び"
]
},
"coast_kalaloch": {
@@ -985,6 +1388,18 @@
"海食柱",
"海岸の海食柱",
"海食柱 · 波に削り残された岩"
],
"detected.sunset": [
"輝き",
"夕焼け",
"ゴールデンアワー",
"ゴールデンアワー · 大気を長く通り赤らむ低い太陽"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
]
},
"coast_seals": {
@@ -1023,6 +1438,30 @@
"上陸岩",
"潮間帯の岩",
"上陸地 · 潮に洗われる休息の岩棚"
],
"detected.whiskers": [
"ひげ",
"触毛",
"感覚のひげ",
"触毛 · 濁った水中で獲物を感じ取るひげ"
],
"feel.tenderness": [
"やさしい",
"優しさ",
"ぬくもり",
"ゆりかごのようなぬくもり"
],
"feel.repose": [
"休息",
"安らぎ",
"まどろみ",
"陽だまりのまどろみ"
],
"feel.delight": [
"楽しい",
"喜び",
"歓び",
"ちらちらと輝く歓び"
]
},
"coast_mist": {
@@ -1061,6 +1500,30 @@
"岸の岩",
"海岸の岩",
"海岸の岩 · 陸の立つ縁"
],
"detected.swell": [
"波",
"うねり",
"海のうねり",
"うねり · 遠くの嵐が生んだ波"
],
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
],
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.hush": [
"静か",
"静けさ",
"沈黙",
"息をひそめた静けさ"
]
},
"reef_lionfish": {
@@ -1100,6 +1563,18 @@
"没入",
"没頭",
"息を呑む没頭"
],
"feel.poise": [
"静止",
"気品",
"優雅",
"宙に浮く華麗な気品"
],
"feel.fragility": [
"薄い",
"儚さ",
"脆さ",
"いたわりたくなる儚さ"
]
},
"reef_spawning": {
@@ -1139,6 +1614,18 @@
"没入",
"没頭",
"息を呑む没頭"
],
"feel.flow": [
"流れ",
"フロー",
"奔流",
"群れが織りなすうねり"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"reef_hawkfish": {
@@ -1172,6 +1659,18 @@
"没入",
"没頭",
"息を呑む没頭"
],
"detected.coral": [
"サンゴ",
"礁サンゴ",
"造礁サンゴ",
"造礁サンゴ · この魚が削って砂にする礁"
],
"feel.radiance": [
"輝き",
"光輝",
"煌めき",
"宝石のような煌めき"
]
},
"reef_coralspacific": {
@@ -1210,6 +1709,24 @@
"没入",
"没頭",
"息を呑む没頭"
],
"detected.polyp": [
"ポリプ",
"サンゴのポリプ",
"生きたポリプ",
"ポリプ · 一つ一つが小さな動物、群体の建設者"
],
"feel.intricacy": [
"精緻",
"複雑さ",
"細部",
"織り込まれた緻密さ"
],
"feel.fragility": [
"薄い",
"儚さ",
"脆さ",
"いたわりたくなる儚さ"
]
},
"reef_redsea": {
@@ -1249,7 +1766,19 @@
"ハナダイ",
"ハナダイ · 礁の上を漂う、プランクトンを食むオレンジの群れ"
],
"measure.depth": "10 m"
"measure.depth": "10 m",
"feel.serenity": [
"穏やか",
"静けさ",
"安らぎ",
"静かで無重力の安らぎ"
],
"feel.radiance": [
"輝き",
"光輝",
"煌めき",
"宝石のような煌めき"
]
},
"reef_flowergarden": {
"feel.delight": [
@@ -1288,7 +1817,13 @@
"Abudefduf saxatilis",
"Abudefduf · 縞模様の「sergeant major」スズメダイ"
],
"measure.depth": "20 m"
"measure.depth": "20 m",
"feel.freedom": [
"自由",
"自由さ",
"解放",
"果てしない解放"
]
},
"abyss_wow": {
"detected.jelly": [
@@ -1327,6 +1862,12 @@
"恐れ",
"胸騒ぎ",
"ゆっくりと冷たい胸騒ぎ"
],
"feel.vastness": [
"大きい",
"広大さ",
"無限の広がり",
"めまいを誘う無限の広がり"
]
},
"abyss_midwaterexp": {
@@ -1366,6 +1907,18 @@
"恐れ",
"胸騒ぎ",
"ゆっくりと冷たい胸騒ぎ"
],
"feel.fragility": [
"薄い",
"儚さ",
"脆さ",
"いたわりたくなる儚さ"
],
"feel.poignancy": [
"切なさ",
"哀切",
"ほろ苦さ",
"光に満ちた別れ"
]
},
"abyss_hiding": {
@@ -1399,6 +1952,24 @@
"恐れ",
"胸騒ぎ",
"ゆっくりと冷たい胸騒ぎ"
],
"detected.marinesnow": [
"粒子",
"マリンスノー",
"落下する有機物",
"マリンスノー · 上層から沈む有機物の屑"
],
"feel.spectral": [
"亡霊",
"幽玄",
"亡霊めいた気配",
"水中の亡霊"
],
"feel.fragility": [
"薄い",
"儚さ",
"脆さ",
"いたわりたくなる儚さ"
]
},
"abyss_bigfin": {
@@ -1432,7 +2003,19 @@
"Magnapinna",
"Magnapinna · 肘のように曲がる腕を闇へ何メートルも垂らす"
],
"measure.depth": "2,000 m"
"measure.depth": "2,000 m",
"detected.arms": [
"腕",
"肘状の腕",
"垂れ下がる糸",
"肘状の腕 · 横に張り出し、数メートルの糸を引く"
],
"feel.alienness": [
"奇妙",
"異質さ",
"異形",
"異形の優雅さ"
]
},
"abyss_dandelion": {
"feel.unease": [
@@ -1465,7 +2048,31 @@
"タンポポクダクラゲ",
"Rhodaliidae · 海底につながれたクローンの群体"
],
"measure.depth": "2,500 m"
"measure.depth": "2,500 m",
"detected.tentacle": [
"糸",
"触手",
"捕食の触手",
"捕食の触手 · 獲物を捕らえる漂う網"
],
"feel.radiance": [
"輝き",
"光輝",
"煌めき",
"宝石のような煌めき"
],
"feel.wonder": [
"わあ",
"驚き",
"畏敬",
"超越的な畏敬"
],
"feel.fragility": [
"薄い",
"儚さ",
"脆さ",
"いたわりたくなる儚さ"
]
},
"abyss_octopus": {
"feel.unease": [
@@ -1498,7 +2105,25 @@
"Graneledone",
"Graneledone boreopacifica · 卵を約4.5年抱き続ける"
],
"measure.depth": "2,500 m"
"measure.depth": "2,500 m",
"detected.arms": [
"腕",
"八本の腕",
"吸盤の並ぶ腕",
"八本の腕 · 触れたものを味わう吸盤付き"
],
"feel.curiosity": [
"見て",
"好奇心",
"魅了",
"のめり込む魅了"
],
"feel.tenderness": [
"やさしい",
"優しさ",
"ぬくもり",
"ゆりかごのようなぬくもり"
]
},
"abyss_seapig": {
"feel.unease": [
@@ -1531,6 +2156,24 @@
"Enypniastes eximia",
"Enypniastes · 泳ぐナマコ、「頭のない鶏」"
],
"measure.depth": "2,700 m"
"measure.depth": "2,700 m",
"detected.veil": [
"ヴェール",
"口前のヴェール",
"遊泳用のヴェール",
"口前のヴェール · 堆積物を掃き、羽ばたいて泳ぐ"
],
"feel.strangeness": [
"変",
"奇妙さ",
"不気味",
"肉感的な奇妙さ"
],
"feel.curiosity": [
"見て",
"好奇心",
"魅了",
"のめり込む魅了"
]
}
}