111 lines
4.8 KiB
Markdown
111 lines
4.8 KiB
Markdown
# Newbury Exhibit
|
||
|
||
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/`.
|