Initial Comming

This commit is contained in:
2026-06-26 13:58:39 +10:00
parent 8b064bbd39
commit 0d1d66a85d
28 changed files with 4328 additions and 2 deletions
+109 -2
View File
@@ -1,3 +1,110 @@
# newbury-exhibit
# Newbury Exhibit
Shared multi-device AR exhibit viewer for a physical Newbury LEGO build. View-only, fiducial-anchored, WebSocket-synchronized ghosts. Fan tribute to LEGO Hidden Side.
Shared, view-only AR experience for a **physical Newbury LEGO display** (max 700 × 1500 mm).
Multiple phones/tablets see the *same* ghosts, in the *same* real-world spots on the build,
each from their own viewpoint — the "Hidden Side" made visible to a room full of people at once.
This is a sibling project to **newbury-nights** (the single-player hunt). The architecture is
deliberately different: instead of ghosts spawning around one player, ghosts live at fixed
**world anchors** on the build, and every device localises itself against printed/LEGO markers
so they all agree on where those anchors are.
---
## How it fits together
```
physical marker ──► world origin ──► anchors ──► ghosts ──► broadcast ──► every phone
(Newbury Crest) (one corner) (scene.json) (motion) (WebSocket) (renders identically)
```
1. **Localisation.** Each device's camera detects an ArUco 4×4 marker of known size and known
world position. From the marker's four corners it solves where the device is in the build's
coordinate frame. (Proof of concept: `public/ar-test.html`.)
2. **Authoritative state.** `server/sync-server.js` runs one motion loop and broadcasts ghost
poses at 20 Hz over WebSocket (`/sync`). The server is the single source of truth, so all
viewers see synchronised motion.
3. **Rendering.** Each device renders its own scene locally. `public/js/viewer.js` is the desktop
"god-view" for authoring/testing; the phone AR client (next milestone) will render ghosts at
anchor world-positions seen through the camera.
All coordinates are in **millimetres**, world origin at the **front-left corner** of the baseplate.
This maps 1:1 to the physical build, so tape-measure values drop straight into `data/scene.json`.
---
## Markers — "Newbury Crests"
The markers are **ArUco 4×4** (dictionary `ARUCO_4X4_1000`), chosen because a 4×4 data grid maps
cleanly onto LEGO tiles. Physically each marker is a **6×6 grid**: a 1-cell black quiet border
around the 4×4 code.
**Design decision — colour wheel as frame, not code.** ArUco detection is brightness-based
(each cell reads black or white), which is exactly what makes it robust under bad lighting and
shallow angles. Encoding the Red/Yellow/Blue Hidden Side colours *into* the code cells would make
it pretty but fragile. So:
- **Detector core (6×6):** black & white flat tiles — this is what the camera reads.
- **Colour-wheel ring:** a decorative Red/Yellow/Blue border *around* the core, in the zone the
detector ignores. On-theme for humans, invisible to the maths.
See `markers/` for printable SVGs, per-anchor LEGO build maps, and `crest-01_buildplan.svg`
(a visual of core + colour ring). Print a marker now to test robustness before committing to a build.
### Marker → anchor mapping
`markers/manifest.json` maps each physical ArUco id to a scene anchor. The `fiducialId` in
`data/scene.json` must match the `arucoId` of the marker you place there.
| Crest | ArUco id | Ward (placeholder) |
|-------|----------|--------------------|
| crest-01 | 0 | North-West |
| crest-02 | 1 | North-East |
| crest-03 | 2 | Town Square |
| crest-04 | 3 | South-West |
| crest-05 | 4 | South-East |
---
## Proof of concept: `ar-test.html`
Camera → marker detection → a ghost sprite locked to the marker, bobbing above it. No server
needed for this page. Use it to test detection robustness on your iPhone 15 Pro.
**Must be served over HTTPS** for the camera to work on iOS. Behind your existing nginx that's
already handled; for the exhibit it'll sit at the same kind of URL as newbury-nights.
Drop an animated WebP at `public/assets/ghost_blue.webp` to use a real sprite; otherwise it draws
a procedural blue "Sad" wisp.
---
## Run
```bash
npm install
npm start # PORT defaults to 34034
# open http://localhost:34034/ar-test.html (camera needs HTTPS on phones)
# open http://localhost:34034/ god-view viewer (viewer.js)
```
Env: `PORT` (default 34034), `TICK_HZ` (default 20).
---
## Status / roadmap
- [x] Authoritative sync server (hover + patrol motion solvers, 20 Hz broadcast)
- [x] God-view desktop viewer
- [x] Scene + anchor + spawn data model (mm, corner origin)
- [x] ArUco 4×4 marker system + LEGO build maps ("Newbury Crests")
- [x] AR PoC: camera + detection + ghost locked to marker
- [ ] Phone AR client: render anchored ghosts through the camera using marker pose
- [ ] Calibration page: scan each crest, record its measured world position
- [ ] Per-ghost colour/sprite wiring from the enriched roster
- [ ] Multi-device soak test on the real build under exhibition lighting
---
## Licence
MIT (project). Vendored `js-aruco2` is MIT (Damiano Falcioni / Juan Mellado); ArUco dictionary
data is 3-clause BSD (OpenCV). See headers in `public/vendor/`.