# Room Map

A live event tool: everyone in a room scans a QR code, types one phrase ("what are you obsessed with right
now?"), and the phrases appear on a big-screen map where similar ones sit together, with the groups named
and every point's nearest neighbours a tap away. An embedding map of the room, meant to teach what an
embedding is by letting people find themselves and each other in one.

Nothing runs on a server. Each phone turns its own phrase into a vector (the sentence model
`Xenova/all-MiniLM-L6-v2` through transformers.js, or a built-in fallback when the model can't load), a
Supabase table passes the vectors on, and the big screen does PCA, t-SNE, k-means and naming in a Web Worker.
The Python package `roommap/` is the readable reference for the maths; `web/*.js` is its port.

## Files

    index.html          the tool: Host (QR + map), Join (phone) and Local (no database) views
    setup.sql           Supabase tables, policies, functions and realtime (run once; safe to re-run)
    web/embed.js        MiniLM in the browser + the hash/trigram/topic fallback (same as roommap/embed.py)
    web/vocab.js        word list and 32-topic table read by both languages
    web/mapmath.js      PCA, t-SNE, k-means, silhouette, names, trustworthiness (port of roommap/)
    web/worker.js       runs the layout off the main thread, warm-starting when phrases arrive
    web/map.js          the canvas map; web/qr.js the QR encoder; web/page.js the views
    web/sample.js       60 sample phrases (Local mode, tests, figures)
    roommap/            Python reference: embed, pca, tsne, cluster, metrics, pipeline, figures
    tests/              pytest, parity (Python vs JS), QR truth + test, SQL rules in PGlite
    out/                figures and results.json for the How it works page

## Run the Python

    pip install -r requirements.txt
    python -m pytest -q                 # 18 tests, ~3 s
    python -m roommap.pipeline          # the sample room as JSON (or: python -m roommap.pipeline phrases.txt)
    python -m roommap.figures           # out/*.png, out/results.json
    python tests/parity.py              # Python against web/*.js (needs node)
    pip install qrcode && python tests/make_qr_truth.py && node tests/qr.test.js
    npm i --no-save @electric-sql/pglite && node tests/sql.test.mjs     # from the repo root

## Set up the rooms (one time)

The Local room works with no setup. Hosting and joining need the database (the same Supabase project as
the Run List; `ROOMMAP_CONFIG` at the top of `index.html` already has its URL and publishable key):

1. Supabase > SQL Editor > New query, paste all of `setup.sql`, Run. Safe to run again after updates.
2. Check realtime: Database > Publications > `supabase_realtime` should list `room_entries` (the SQL adds
   it; if the publication is managed from the dashboard, tick the table there). Without it the map still
   refreshes every 30 s.
3. No authentication, so no redirect URLs. Push, open `/projects/room-map/`, click *Create room*.

How a room works: the host's browser keeps a secret per room (`localStorage`), participants' phones keep a
random device token; both are checked by the SQL functions, never sent anywhere else. Anyone with a
four-letter code can read a room's phrases (they are anonymous). A device may hold 3 phrases per room;
a room holds 500. The host can close, clear or re-embed a room, and remove any phrase.

## Numbers

From `python -m roommap.figures` on the sample room with the fallback embedder: t-SNE trustworthiness 0.86
(PCA alone 0.78), 66% of each point's 5 real neighbours kept (PCA 46%), 8 groups with silhouette 0.33.
Everything else, with the figures, is on the How it works page.
