Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,14 @@ ReadMes for the upstream catalogs live in `data/raw/` (`J_ApJS_199_26_ReadMe`, `
- **`textureAtlas.ts` + `quadRenderer.ts` + `shaders/quads.wgsl`**: 2048×2048 atlas of 128×128 slots for galaxy thumbnails. LRU eviction.
- **`galaxyImageQueue.ts`**: priority queue + concurrency limiter (max 4) for thumbnail fetches. Idempotent enqueue (don't re-add in-flight keys — see the long comment for the bug history).
- **`galaxyImageFetcher.ts`**: SDSS DR18 ImgCutout (CORS-safe) for SDSS galaxies; CDS hips2fits (CORS-safe DSS proxy) for 2MRS/GLADE.
- **`engine.ts`**: per-frame loop. Per-galaxy `apparentSizePx` gates thumbnail enqueue — but the inner loop hoists `Math.tan` and pre-computes `maxCamDistForVisibility` to avoid 3.5M trig calls per frame.
- **`engine.ts`**: per-frame loop. Per-galaxy `apparentSizePx` gates thumbnail enqueue — but the inner loop hoists `Math.tan` and pre-computes `maxCamDistForVisibility` to avoid 2.5M trig calls per frame.
- **`renderScheduler.ts` + `engine.ts` frame tail**: render-on-demand. `requestRender()` from event handlers wakes the loop; the frame body re-schedules only while `autoRotate || currentTween || hasAnyAxis || queue.inFlightCount > 0 || recent-fade` is true.

## When the user asks you to…

- **"add a feature"** → look in `docs/superpowers/plans/` for an existing plan. If it's substantial, write a new plan via the `writing-plans` skill rather than coding inline.
- **"fix this bug"** → check tests first; the project favours reproducing bugs as failing tests, then fixing.
- **"why is this slow?"** → profile mental model first: per-frame work scales with on-screen galaxies (~3.5M total). Inner-loop trig and `Math.sqrt` are real costs. Hoist constants, gate with squared distances, avoid per-galaxy `Math.tan`.
- **"why is this slow?"** → profile mental model first: per-frame work scales with on-screen galaxies (~2.5M total). Inner-loop trig and `Math.sqrt` are real costs. Hoist constants, gate with squared distances, avoid per-galaxy `Math.tan`.
- **"refactor X"** → keep the services/ layout. Cross-cutting helpers go in `utils/`; rendering subsystems in `services/gpu/`. Tests mirror the src tree.
- **"why does the renderer use index Y?"** → check `pointRenderer.ts` SLOTS_PER_POINT and the matching attribute layout in the shader. They must agree byte-for-byte.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ _The full overlay surface — left stack (Navigation cheatsheet, Settings panel,

![Data-tier selector](docs/screenshots/tier-selector.png)

_Segmented control at the top of the Settings panel hot-swaps the loaded dataset between three sizes: **Small** (~300k galaxies — mobile-friendly), **Medium** (~600k — default for laptops), and **Large** (~3.5M — full catalog). Tier choice on first paint is driven by viewport width; clicking a button re-fetches the relevant `.bin` files and re-uploads the GPU vertex buffers in place — no page reload._
_Segmented control at the top of the Settings panel hot-swaps the loaded dataset between three sizes: **Small** (~300k galaxies — mobile-friendly), **Medium** (~600k — default for laptops), and **Large** (~2.5M — full catalog). Tier choice on first paint is driven by viewport width; clicking a button re-fetches the relevant `.bin` files and re-uploads the GPU vertex buffers in place — no page reload._

![Pinned InfoCard for a 2MRS galaxy](docs/screenshots/infocard-detail.png)

Expand Down
2 changes: 1 addition & 1 deletion src/@types/data/Tier.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* to its brightest ~256k, 2MRS + Famous kept whole (small).
* - `medium` — default for desktops, ~600k total. Brightest ~156k SDSS +
* brightest ~400k GLADE + full 2MRS + full Famous.
* - `large` — opt-in full catalog (~3.5M). The pre-tier behaviour.
* - `large` — opt-in full catalog (~2.5M). The pre-tier behaviour.
*
* The values are persisted in URL query strings and the runtime API only
* (never on disk: the binary format is tier-agnostic). String-union — not a
Expand Down
2 changes: 1 addition & 1 deletion src/data/galaxyCatalogTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* ### Why this module exists
*
* Sending a GalaxyCatalog across a Worker boundary with structured-clone
* cost would be prohibitive at ~3.5M galaxies. The cheap alternative is
* cost would be prohibitive at ~2.5M galaxies. The cheap alternative is
* `postMessage(payload, transfer)` with a list of `ArrayBuffer`s to
* transfer ownership of — but we can't transfer the engine's
* authoritative buffers (the picker and InfoCard read them after the
Expand Down
6 changes: 3 additions & 3 deletions src/utils/cluster/clusterMembership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* ### Why a pure function (no caching here)?
*
* The expensive bit is the cone search itself (one vec3 subtract + one
* dot product per galaxy, ~3.5M ops for the full loaded catalog).
* dot product per galaxy, ~2.5M ops for the full loaded catalog).
* Memoising against `(poiId, dataRev)` belongs to the subsystem that
* owns the focus state — see spec §4.3 — because cache invalidation
* needs to know when a tier swap has bumped `dataRev`, which this pure
Expand Down Expand Up @@ -66,7 +66,7 @@ export type ClusterMembershipResult = {
* `radiusMpc` of `centerMpc` across the supplied catalogs.
*
* Time complexity: O(total galaxy count). For the typical loaded
* footprint (~3.5M galaxies across SDSS + 2MRS + GLADE), one call
* footprint (~2.5M galaxies across SDSS + 2MRS + GLADE), one call
* runs in single-digit milliseconds on the target hardware — see
* spec §4.2 for the rationale on runtime-vs-build-time computation.
*
Expand All @@ -83,7 +83,7 @@ export function clusterMembership(
const cx = centerMpc[0];
const cy = centerMpc[1];
const cz = centerMpc[2];
// Compare against squared distance to avoid 3.5M Math.sqrt calls.
// Compare against squared distance to avoid 2.5M Math.sqrt calls.
const r2 = radiusMpc * radiusMpc;

const packedIds: number[] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/utils/initialTierFromViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* enough that users intuitively expect "tablet and up" to behave like a
* desktop.
*
* 'large' is intentionally never auto-selected: the full 3.5M-point catalog
* 'large' is intentionally never auto-selected: the full 2.5M-point catalog
* stresses integrated GPUs and the user should opt-in.
*
* ### Defensive edge cases
Expand Down
2 changes: 1 addition & 1 deletion tools/site/makeOgImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const overlay = Buffer.from(
font-size="34" font-weight="400" fill="#a8d0ff" opacity="0.95">Interactive WebGPU 3D galaxy explorer</text>
<text x="60" y="${H - 12}"
font-family="-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Helvetica, sans-serif"
font-size="22" font-weight="400" fill="#ffffff" opacity="0.65">SDSS · GLADE · 2MRS · ~3.5M galaxies · skymap.rulkens.com</text>
font-size="22" font-weight="400" fill="#ffffff" opacity="0.65">SDSS · GLADE · 2MRS · ~2.5M galaxies · skymap.rulkens.com</text>
</svg>`,
);

Expand Down
Loading