# BLOBBO Workshop — custom assets + in-game editor (plan, 2026-07-18) **Goal:** John designs/builds models (Blender, or farm-generated GLB) and drops them into the live game without touching code — plus an in-browser editor to preview, fit, and export the mapping. ## Why this is easy here Physics and visuals are fully decoupled: every collider is a primitive; every mesh is cosmetic. Swapping a mesh can never break gameplay. The ONE special case is the blobbo body — the paint system stamps into a canvas texture through the mesh's UVs — so a custom blob needs clean, non-overlapping UVs and its material's `map` slot handed to `PaintSkin`. ## Reskinnable slots (the inventory) | Slot id | Today | Notes | |---|---|---| | `blob.body` | UV sphere | PAINTABLE — needs clean UVs; SkinnedMesh OK (idle/wobble clip plays via mixer; squash still applies at group level) | | `blob.face` | procedural googly eyes | separate from paintable body, by design | | `ghost.body` | translucent blob copy | auto-derived from blob.body (desaturated) | | `cannon.base` / `cannon.barrel` | cylinders | per-colour tint applied on top | | `machine.plate` | box + base | pressure plate | | `machine.boot` | boot+spring primitive | farm GLB already exists (`assets/meshes/prop-spring-boot.glb`) | | `machine.bucket` | cylinder shell + fill | farm GLB exists (`prop-paint-bucket.glb`) | | `machine.arch` | frame + bubbles | bubble cleanser | | `machine.belt` | striped box | conveyor | | `course.scenery.*` | giant boxes (cereal box etc.) | free-place decorative slots; toaster GLB exists | | `course.tramp` | orange pad | gap launcher | | `course.tunnel` | slab + posts | MINI tunnel | | `course.finish` | pink podium | finish pad | | `fx.puddle` | flat glossy slab | material/decal upgrade slot | | UI: title wordmark, HUD | CSS/DOM | swap via CSS/vars, out of scope for v1 | ## Architecture (two lanes + integration) ### Lane I — Asset runtime (`src/assets/`) `assets/manifest.json` maps slot → `{url, offset, rotation, scale, options}`. `AssetRegistry` (GLTFLoader + cache) resolves slots; **every construction site falls back to today's procedural primitive when a slot is empty or fails to load** — an empty manifest must produce today's game, byte-for-byte behavior. Construction sites (createBlob, cannon barrel, machine parts, greybox scenery) ask the registry instead of building directly, via a tiny `slotMesh(world, 'machine.boot', buildProcedural)` hook. Blob slot additionally: UV sanity check (warn if overlapping/absent), PaintSkin adoption of the delivered material, and optional AnimationMixer for a named idle clip. Local override layer: manifest + GLB blobs can live in IndexedDB (set by the editor) and take precedence — so custom assets are playtestable on the LIVE site with zero deploys. ### Lane J — Workshop editor (`/editor.html`, `src/editor/`) A separate page sharing the game's modules. Three panels: 1. **Slots** — every slot with live thumbnail; click to select. 2. **Stage** — the actual game world (greybox + machines idling) rendering the current assets; orbit camera; selected slot highlighted. 3. **Fit** — drag-drop a `.glb` from disk onto a slot → instant swap in the stage; sliders/inputs for offset/rotation/scale; paintability report for blob.body (UV coverage, tri count, material count); "Test drive" button boots the REAL game in-page with overrides active. Export: ⬇ `manifest.json` download + "Save locally" (IndexedDB override, with a persistent "custom assets active — clear?" pill in the game). No server writes: committing an asset pack = John drops the GLB in `assets/live/` + manifest in the repo (documented in the panel's "ship it" help text). ### Integration (main session) Wire registry into game.ts construction, ship `/blobbo/editor.html`, verify (empty-manifest parity, toaster GLB in a scenery slot, blob swap with the farm mesh incl. paint), deploy. Ship the 4 existing farm GLBs as the first pack. ## Blender/authoring conventions (put in the editor's help panel too) - Y-up, -Z forward, meters; blob body ≈ 1.0u diameter, origin at CENTER; props origin at ground-contact center. Single material preferred. - Paintable blob: one UV island set, no overlaps, no mirrored UVs. - Rigged: single armature, clips named `idle` (loop) and optionally `roll`; keep ≤ ~40 bones (mobile later). GLB embedded textures ≤2048². - Licensing per asset-pipeline skill: 🟢 web-ok only (farm-generated = ok). ## Out of scope v1 (candidate wave 4) Course layout editing (move/add machines, puddles, boxes → course JSON), animation retarget beyond idle clips, multi-pack management, shared packs.