splat-streetview deploy brief (READ FIRST) (D071011e)

**Note** from Bead: D071011e · [canonical source](https://redfish.acequia.io/guerin/.agents/d071011e-d121-4b25-87f2-8f5ac1ee2b30/2026-06-07/notes/00-splat-streetview-deploy-brief.md) · session 2026-06-07 · discussion: Talk: D071011e

> This is the load-bearing handoff artifact for bead > `d071011e-d121-4b25-87f2-8f5ac1ee2b30`. It captures the vantage of the chat > that reviewed the repo, so a denovo agent can execute the deploy without > conversation carryover. Read [`about.md`](https://redfish.acequia.io/guerin/.agents/d071011e-d121-4b25-87f2-8f5ac1ee2b30/about.md) > first for posture, then this.

## The task in one sentence Get [`github.com/Ibrahimshoer93/cesium-splat-streetview`](https://github.com/Ibrahimshoer93/cesium-splat-streetview) building and deployed at **`redfish.acequia.io/guerin/apps/splat-streetview/`**, with the Lublin Old Town Gaussian-splat dataset rendering in it. The hard part is **not** the web build — it's **getting and installing the Lublin splat dataset** (a large external asset that must be requested and run through a conversion pipeline). Do that first; everything else is routine.

## What the repo is - **Stack:** TypeScript + **Vite 5** + `cesium ^1.141.0` + `vite-plugin-cesium ^1.2.22`. Dev dep: `typescript ^5.4.5`. MIT licensed. Package name in `package.json` is `cesium-gaussian-splatting` (the GitHub repo name differs). - **Scripts:** `dev` → `vite --host` (dev server on **port 3001**); `build` → `vite build` (emits `dist/`). No test script. - **`vite.config.ts`** sets `base: "/cesium-gaussian-splatting/"` and `server.port: 3001`, and loads `vite-plugin-cesium()`. **The `base` is hardcoded and must be changed** (see step 3 below) or all bundled asset URLs (JS/CSS/Cesium workers/assets) will 404 under the deploy path. - **`src/`:** `main.ts` (entry point), `viewer.ts` (Cesium viewer + tileset loading), `flyover-controls.ts` (street-view-style navigation), `demo-flow.ts` (scripted demo flythrough), `datasets/` (dataset configs — where the tileset URL/path is declared). - **Rendering:** city-scale 3D Gaussian splats via Cesium's **native** `KHR_gaussian_splatting` glTF extension. This is why Cesium **≥1.141** is required — earlier versions don't have the native splat renderer. - **`tools/`:** Python scripts for dataset prep — `ply_split_spatial.py` (spatial splitting) plus KML generation. Requires Python 3.10+ with NumPy.

## The data — no splats ship in the repo The viewer renders **nothing** without a 3D-Tiles splat dataset. The repo expects it at: ``` public/data/lublin-3dtiles/tileset.json ``` - 3D Tiles **1.1**, SPZ-compressed GLB tiles. - The Lublin scan (~259M splats, ~62% in the dense Old Town SW quadrant) is intentionally **split into ~10 density-aware spatial cells** — small cells in the dense core, large cells in the sparse outskirts. This split is **load- bearing**, not cosmetic: it prevents a JS-heap overflow during per-tileset texture aggregation, and it dodges a **Cesium 1.141 splat-orientation bug** that triggers on large monolithic tilesets. Do not collapse it back into one tileset. ### Dataset acquisition + conversion pipeline (do this first) 1. **Obtain the Lublin PLY.** Source: Andrii Shramko / Teleportour (2025), `andrii@teleportour.com` (or LinkedIn). Confirm with Stephen whether he already has the PLY locally or whether the request still needs to be made — this is the critical-path dependency and may gate everything else. 2. **Install the converter:** `npm install -g 3dgs-ply-3dtiles-converter`. 3. **Split the PLY spatially:** run `tools/ply_split_spatial.py` (Python 3.10+, NumPy). Read the script for its exact CLI/args; it produces the ~10 cells. 4. **Convert each cell** with `3dgs-ply-3dtiles-converter` → SPZ-compressed GLB 3D-Tiles, assembled under `public/data/lublin-3dtiles/` with a top `tileset.json`. 5. **Attribution is mandatory** and must appear in the deployed UI/credits: *"3D scanning data created and provided by Andrii Shramko, Teleportour."* Usage restriction to honor: no facial recognition, license-plate identification, or individual/vehicle re-identification. > The dataset is large. Keep the raw PLY and intermediate cells in the > non-deploying `dev/` workshop (see below). Only the final converted > `public/data/lublin-3dtiles/` (as emitted into `dist/`) gets deployed.

## Deploy idiom for `guerin/apps/` `guerin/apps/` is served as **plain static files over WebDAV** — there is **no build step on the server** (see siblings `apps/viewer-3d`, `apps/sphere`, `apps/virus`). So: **build `dist/` locally, deploy the built output.** A **redfish Cesium-ion token already exists** at [`apps/viewer-3d/js/config.js`](https://redfish.acequia.io/guerin/apps/viewer-3d/js/config.js) (`CESIUM_ION_TOKEN`) if you want a base imagery/terrain layer under the splats. Reuse it rather than minting a new one.

## Execution sequence 1. **Clone into a non-deploying workshop.** Put the git checkout + node_modules + raw dataset somewhere that does NOT get served — e.g. `redfish.acequia.io/dev/splat-streetview/` or a `dev/` subdir you keep out of the deploy sync. **Never** run `npm install` / vendor `node_modules` inside the served `apps/splat-streetview/`. 2. **Dataset first** — run the acquisition + conversion pipeline above so `public/data/lublin-3dtiles/` exists before you build. 3. **Patch `vite.config.ts`:** change `base` from `/cesium-gaussian-splatting/` to **`/guerin/apps/splat-streetview/`**. Double-check `src/datasets/` for any hardcoded `/cesium-gaussian-splatting/...` tileset path and update it to the new base (or make it base-relative). 4. **Build:** `npm install && npm run build` → `dist/` (Vite copies `public/` — including the tileset — into `dist/`, so the converted tiles ship in the build output). 5. **Deploy** the contents of `dist/` to `sites/redfish.acequia.io/guerin/apps/splat-streetview/`, then sync to the canonical URI via WebDAV (`/bead-sync` resolution order / `webdav-sync.js`; credential `.credentials/redfish-acequia-jwt.txt`). The tileset is large — mind the size pre-flight; large-file handling may need attention. 6. **Verify in a browser** at `https://redfish.acequia.io/guerin/apps/splat-streetview/`: base globe loads, the Lublin tileset streams in, flyover controls work, and the attribution credit is visible. Confirm no `/cesium-gaussian-splatting/` 404s in the network tab (catches a missed `base`).

## Caveats - **Cesium 1.141 is bleeding-edge** for native splatting. Needs a WebGL2 (or WebGPU) capable browser. If splats render mis-oriented, that's the known monolithic-tileset bug — confirm the 10-cell split actually took effect rather than fighting it in code. - **Base-path correctness is the #1 deploy failure mode.** A wrong `base` produces a blank page with 404s on the bundled JS — check it explicitly. - **Size:** the deployed tileset may be multiple GB. The bead does NOT vendor it; it lives next to the app. Plan the WebDAV push accordingly.

## Working conventions for this bead - New decisions/progress notes → `2026-06-07/notes/`. - Verbatim chat log → `2026-06-07/chats/`, written incrementally (chat-log discipline; verbatim user prompts). - Cross-references → `https://` absolute URIs. - The only external write target is the built app at `guerin/apps/splat-streetview/`. Do not write into other `guerin/` global spaces.