███████ ██    ██ ███████ ███    ██  ███████  ██████  ██    ██ ███    ██ ██████  ██████  ██    ██ ██    ██ ████████ ████████
  ██      ██    ██ ██      ████   ██  ██      ██    ██ ██    ██ ████   ██ ██   ██ ██   ██  ██  ██  ██    ██ ╚══██╔══╝╚══██╔══╝
  █████   ██    ██ █████   ██ ██  ██  █████   ██    ██ ██    ██ ██ ██  ██ ██   ██ ██████    ████   ██    ██    ██       ██
  ██       ██  ██  ██      ██  ██ ██  ██      ██    ██ ██    ██ ██  ██ ██ ██   ██ ██   ██    ██    ╚██  ██╔╝   ██       ██
  ███████   ████   ███████ ██   ████  ██       ██████   ██████  ██   ████ ██████  ██   ██    ██     ╚████╔╝    ██       ██

Project a glanceable HUD of your FoundryVTT D&D session onto Even Realities G2 AR glasses (576 × 288, 4-bit greyscale phosphor green), driven by tap / swipe / double-tap gestures from the Even R1 smart ring. Your own Foundry tab shows a QR, you scan it with the FoundryVTT G2 HUD app, and your character streams to the glasses through an end-to-end-sealed relay — no GM step, no server to run. Eyes on the table, not on a laptop.

576×288px / eye
4-bitgreyscale
5zones · 2×2 grid of 288×144 tiles · 1 input panel
0servers you run · hosted relay
1QR scan to pair
v0.13.0relay pairing · ADR-0019 · Even Hub SDK 0.0.16

✨ Four ideas, no magic.

Deterministic by design — every action is an explicit gesture → tool → Foundry. Nothing to host: your own Foundry tab is the projector.

▤

Glanceable HUD

Reads like the paper 5e sheet: portrait · AC shield and HP box · square map · ability and save pages · one context panel. Zone boundaries never move, so you learn where to look once.

⌬

Gesture-driven

R1 ring (ADR-0012): tap at the base view opens the actions menu, swipe moves the cursor, double-tap goes back (exit at root). Long-press opens a shortcuts menu as an extra — never the only path.

✦

Foundry as truth

Every action goes through Activity#use() with all rules, hooks, MidiQOL, advantage / disadvantage. The DM keeps full veto power.

≣

One QR, no GM

The player’s own Foundry tab shows the QR and becomes the projector; the phone never logs into Foundry. Phone and tab meet on an opaque relay that only sees AES-256-GCM sealed frames. No bridge, no Docker, no extra Foundry user.

🛡️ Four rules. Non-negotiable.

Every PR, every audit, every release must satisfy them. They're constraints, not guidelines — ratified in §0.1 of the spec as the contract between today's design and tomorrow's implementation.

⌬

INV-1 · Layout integrity

Formatting and layout are dynamic and always perfect. Frame corners, dividers and columns align to the character in every state — HP 7 / 70 / 700, names from 4ch to 16ch, Italian to English — never misaligned for any reason. Snapshot tests + Box/TextRun render contract.

⌥

INV-2 · Online cross-validation

Every technical claim cites a canonical upstream source — Even Hub docs and changelog, foundryvtt.com, dnd5e, the npm registry. Re-verified before each version bump and every GO/NO-GO gate. Drift is classified, fixed and logged.

▤

INV-3 · Documentation coherence

Spec / README / showcase are always coherent and updated in the same commit for any cross-cutting change — version, fps target, phase count, hardware spec, locale set. No half-updated states. Cross-reference integrity is a hard gate.

≣

INV-4 · Code quality

Code is clean, optimized, documented — and zero dead or unreachable code tolerated. Biome + TS strict + Vitest coverage gate enforce in CI. // TODO without an issue/ADR link fails the build. JSDoc/TSDoc on every public API. Hot-path benchmarks gate regressions.

🥽 Validated against upstream docs.

Every spec parameter below is cited from Even Realities and FoundryVTT documentation, last re-verified for v0.13.0 on 2026-09-25 against hub.evenrealities.com/docs (networking, FAQ, testing and submission pages) and the npm registry (SDK 0.0.16).

Even Realities G2

Smart Glasses · Display

576 × 288 · 4-bit greyscale · 16 levels green 4 image + 8 other containers · isEventCapture
Resolution576 × 288 px / eye
Color4-bit greyscale (16 levels green)
Containersmax 4 image + 8 text/list per page
Image size20–288 px W × 20–144 px H · ≥ 100 ms between updates
Text container1 000 char (2 000 with upgrade)
List container20 items × 64 char, no in-place updates
Networkingwhitelisted origins only (no wildcards) · our relay wss://evf-relay.evf-relay.workers.dev
Layoutabsolute pixel from top-left, no DOM/CSS
Microphone4-mic array · PCM 16 kHz via SDK (unused since v0.12.0)
Audio out❌ no speaker · feedback is visual only
Camera❌ none on the glasses · the phone camera scans the pairing QR (camera permission)
Plugin runs onpaired phone WebView (not on G2 firmware)
✓ verified · hub.evenrealities.com/docs/build/{display, device-apis, input}

Even Realities R1

Smart Ring · Controller

tap swipe ↑↓ double
ConnectionBLE → Even App → G2
Gesturespress · double-press · swipe up/down · long-press extra (SDK ≥ 0.0.14, Even App ≥ 2.2.9)
BiometricsHR · HRV · SpO₂ · skin temp · steps · sleep
Materialzirconia ceramic + medical-grade stainless steel
WaterIP68 · 50 m / 30 min
Battery~4 days · ~90 min full charge
Operating temp-10 °C to 45 °C
✓ verified · evenrealities.com/smart-ring

📦 One QR from your own Foundry — no GM, no login.

Install the Foundry module (manifest URL) and the FoundryVTT G2 HUD app from Even Hub (beta group until the store listing is approved). Then open Connect G2 glasses (Collega occhiali G2) in your own Foundry — right-click your name in the Players list, press Alt+G, or use Configure Settings › EvenFoundryVTT — and tap Scan QR in the app. Opening the window is pairing: your character is preselected, the relay is checked, the QR and a 16-character code appear at once. The tab that showed the QR is the projector; the phone never logs into Foundry, so there is no «(G2)» user, no password and no HTTPS to set up for the phone (self-hosted or The Forge, v13 or v14). A GM can connect glasses for a player without a device of their own — the GM’s tab then projects. Decision record: ADR-0019. Guides: the project wiki (Italian) and the setup guide.

▶ pairing · once per device, < 30 s ① Player’s Foundry tab Connect G2 glasses · Alt+G Relay ✓ · QR + 16-char code 5 min · single use ② FoundryVTT G2 HUD Scan QR · or Enter code room + key from the QR no Foundry credential ③ First welcome room + key rotate window: Glasses connected glasses: Connecting → sheet ▶ at play time · the tab that showed the QR projects G2 glasses · phone app sealed frames · to: projector relay room tab open Player’s Foundry tab actions run as the player tab closed «Player’s Foundry closed» reconnects when the tab reopens One projector per device executes actions (dispatchTool · ADR-0011); the relay only forwards ciphertext.

The Connect G2 glasses window: QR and code at once, a relay check, a 5-minute expiry, a live status under the QR (relay, glasses in the room, why a message was refused), a Restore default notice when the app page or relay is not the default, and the glasses connected to this browser with Disconnect.

╭────────────────────────────────────────────────────────────────────────╮
│ EvenFoundryVTT · Connect G2 glasses                                    │
├────────────────────────────────────────────────────────────────────────┤
│                                                                        │
│  Character  [ Thorin ▾ ]                          Relay ✓              │
│                                                                        │
│  ┌──────────────────────────┐                                          │
│  │   ▄▄▄▄▄▄▄ ▄ ▄▄ ▄▄▄▄▄▄▄   │   1. On the phone open FoundryVTT G2 HUD │
│  │   █ ▄▄▄ █ ▀█▄▀ █ ▄▄▄ █   │      and tap «Scan QR»                   │
│  │   █ ███ █ ▄▀▀█ █ ███ █   │   2. The sheet appears on the glasses.   │
│  │   █▄▄▄▄▄█ █▀▄▀ █▄▄▄▄▄█   │      Keep this Foundry tab open          │
│  │   ▄▄ ▄  ▄▄▀█▄▀▄ ▄▄▄ ▄    │                                          │
│  │   █▄▀██▄▄▀▄ ▀▄██▀▄▀▄█▀   │   Expires in  04:52                      │
│  │   ▄▄▄▄▄▄▄ ▀▄█▀ ▄ ▀█▄▀▄   │   Single use: it stops working once      │
│  │   █ ▄▄▄ █ █▀▄▄▀▀▄█▀▄▄█   │   the glasses connect.                   │
│  │   █ ███ █ ▀▄▀██▄▀▄ ▀▀    │   ● Relay connected · ○ waiting…         │
│  │   █▄▄▄▄▄█ █▄ ▀▄█▀▄█▄▄█   │   No camera? Type this code:             │
│  └──────────────────────────┘   7QK3-MX9P-2HRA-C4TE   [ Copy code ]    │
│                                                                        │
│  Glasses connected to this browser                                     │
│   ● Thorin · online · now                         [ Disconnect ]       │
│   ○ Mira   · waiting for the glasses              [ Disconnect ]       │
│                                                                        │
╰────────────────────────────────────────────────────────────────────────╯
⌬

Single-use QR

The QR opens the app page with a fragment carrying only the 16-character code, which never reaches a server; room and key derive from it. On first connect the room and key rotate, so a scanned QR never works twice, and an unused one expires after 5 minutes — even if you close the window, which reopens on the same QR; «Cancel QR» kills it at once. A phone that reopens a QR it already used ignores it, says so, and keeps its working pairing.

▤

Your tab is the projector

No second Foundry user, no password: the tab that showed the QR reads your character and runs every action through dispatchTool as you (ADR-0011). The pairing is stored in this browser and reconnects by itself whenever it has Foundry open.

⊟

Opaque relay

A Cloudflare Worker with one Durable Object per room forwards AES-256-GCM sealed frames between the two roles, caps frame size and rate, and stores nothing. It learns room ids, timing and sizes — never content. Privacy policy.

👓 The HUD, in your face.

Interactive preview of the v0.9 layered engine (map base + status corner card + overlay panels), kept for reference. v0.12.0 replaced it with the D&D-sheet layout shown in the next section. Its gesture hints (long-press = quick action) are the historical v0.9 model; the live gestures follow ADR-0012 (tap at the base view opens the menu). Mockups from the spec § 7.


      
R1: scroll = pan · tap = ping · long-press = quick action mode: ▶ RASTER (toggle GLYPH)

👓 Your character sheet, on the glasses.

The HUD reads like the paper 5e sheet and D&D Beyond. Five fixed zones on the 576 × 288 canvas, laid on the hardware-proven 2 × 2 grid of 288 × 144 image tiles (the real G2 host rejects image containers at off-grid offsets): A portrait 144², B header 288 × 144 (AC shield, HP box + temp, INIT · SPD · PROF, ● Action ▲ Bonus ◆ Reaction, conditions, ▲ YOUR TURN), C square map 144² centred on your token — the scene’s original art, pixelated (see below), ≤ 1 fps, D sheet 288 × 144 (Abilities · Saves & Skills, death saves at 0 HP) and E the context panel — the only zone that reacts to input. Zones A–D are drawn by our own 4-bit pixel renderer and bitmap fonts (the firmware font has no D&D glyphs): the top band A + B + C (576 × 144) is rendered once and split at x = 288 into two tiles, D is the tile at (0, 144), and E is native firmware text in the bottom-right quarter. Budget 3/4 images + 4/8 text, no page rebuild during play; the full-screen states (unpaired, connecting) use all four tiles. Real simulator screenshots below (Italian UI; the map shows the pixelated scene art of the demo crypt); full design in docs/design/g2-sheet-ux.html.

Exploration: portrait, AC 18, HP 27/38, square map, abilities page, log in the context panel
S1 · Exploration — abilities page, log
Combat, your turn: YOUR TURN chip, action economy, initiative list in the context panel
S2 · Combat, your turn — action economy, initiative
Target picker: crosshair on the square map, target list in the context panel
S4 · Target picker — crosshair drawn on the map
At 0 HP: dimmed portrait, death saving throws replace the sheet
S9 · 0 HP — death saves replace the sheet

🏗️ Your tab projects. The relay only sees ciphertext.

The phone never logs into Foundry. The glasses app (FoundryVTT G2 HUD — one bundle: Even Hub package, GitHub Pages /app/, Vite dev) and the player’s own Foundry tab meet in a random room on an opaque relay (packages/relay, Cloudflare Worker + Durable Object per room). The projector — the tab that showed the QR — computes dnd5e derived data, answers snapshots, pushes deltas, sends the scene art and is the only client that executes that device’s actions. Why: Foundry ≥ 14.361 serves module HTML as text/plain, players cannot create users, Forge private games gate every path, and a store app reaches only fixed whitelisted origins. Decision records: ADR-0019 (supersedes the Foundry-served page of ADR-0016 and all of ADR-0017).

▶ v0.13.0 · relay pairing Even G2 Glasses 576 × 288 · 4-bit D&D-sheet HUD Even R1 Ring tap · swipe · double long-press extra Even Realities App · FoundryVTT G2 HUD .ehpk · GitHub Pages /app/ · Vite dev pixel renderer · sheet · map 144² · context sealed session (AES-256-GCM) BLE BLE Relay · wss://evf-relay.evf-relay.workers.dev Cloudflare Worker · one Durable Object per room projector ⇄ glasses · 1 MiB · 60 frames/s opaque sealed frames · stores nothing wss Pairing (ADR-0019) Connect G2 glasses · Alt+G one QR · 16-char code · no GM room + key rotate on welcome scan Player’s Foundry tab = projector dnd5e readers · dispatchTool activity.use · MidiQOL (opt) scene art sent once · v13 / v14 · Forge wss Removed in v0.13.0 Foundry-served page · «(G2)» users GM enablement · ECDH custody · election (v0.12.0: bridge · Docker · foundry-mcp)

⚡ Your battle map, pixelated for 16 greens.

Zone C shows the scene’s original art — background, tiles and token art — not a schematic. The projector’s scene reader sends only compact scene data (MapSnapshot: grid, walls, tiles, visible tokens, asset references); the projector loads the pictures in its own tab (Forge CDN art included), downsizes them and sends each one once as an asset; the phone renders the square in plain, deterministic TypeScript (ADR-0018). Darkness outside your token’s sight means the map never reveals rooms your character can’t see. Inspired by the Doom-on-exotic-devices pattern and ditherpunk.

01
Scene data
MapSnapshot from the projector
cells · walls · tokens
02
Art assets
background ≤ 768 px · tiles/tokens ≤ 128 px
sent once by the projector
03
Crop
window on your token
follow or free
04
Pixelate
block average
pixel 1 / 2 / 3 (default 2)
05
Tone + dither
G2 tone curve
Floyd–Steinberg · 16 levels
06
Sight
black outside sight radius
12 cells if unknown · walls block
07
Markers
self · ally · enemy · reticle
reach · north · scale
08
Pace
per-zone hash · ≤ 1 fps
≥ 100 ms between images
09
Apply
4-bit PNG · updateImageRawData
144 × 144

Blocks are anchored to scene pixels so they don’t crawl while the map scrolls; the art stays below mid-tone so HP, AC and turn remain the brightest marks. When a scene has no art (or it fails to load) the map falls back to a schematic walls-and-grid view.

⚡ Six layers, 5 to 15 fps.

BLE bandwidth is the bottleneck on a 4-bit greyscale phosphor display. This optimisation stack was designed for the v0.9 full-screen raster (5 fps committed, 15 fps stretch). The v0.12 sheet-layout map needs far less: one 144 × 144 image, ≤ 1 fps, changed tiles only, paced to the SDK's 100 ms image limit.

L0
naïve baseline4 tiles full PNG, no delta
~ 1.5 fps
L1
Per-tile delta hashxxHash · push only changed tiles
~ 3–6 fps
L3
Static layer cachingwalls/floors recomputed only on scene change
~ 5–8 fps
L4
Custom RLE 4-bituniform regions 1.5–25× compression
~ 5–10 fps
L2
Sub-tile delta 20×20partial updates · token move = 4–9 sub-tiles
~ 8–12 fps
L5
BLE 4.2+ DLEMTU 244 byte · 700–1000 kbps real-world
15 fps ✓
L6 — adaptive frame rate (idle 0.3 / slow 3–5 / active 8–15 / storm 0.5–2 fps) reduces battery drain ~ 50 % across a session.

⚙️ Locale follows Foundry. Override on the glasses.

Internationalization is wired in from the MVP — the locale auto-detects from game.i18n.lang in Foundry and broadcasts at handshake; the player can override it directly from the glasses without touching the world setting. Catalogs live in Foundry (dnd5e + module languages), the G2 carries no strings of its own. See §7.16 of the spec.

╔═══════════════════════════════════════════════════════════════════════════════╗
║                              ◈  LANGUAGE                                      ║
║                                                                               ║
║                       ▶ [ • ]  Auto (Foundry: it)                             ║
║                         [ ◦ ]  English        (en)                            ║
║                         [ ◦ ]  Italiano       (it)                            ║
║                         [ ◦ ]  Deutsch        (de)                            ║
║                         [ ◦ ]  Español        (es)                            ║
║                         [ ◦ ]  Français       (fr)                            ║
║                         [ X ]  Cancel                                         ║
║                                                                               ║
║                       R1 scroll=select  tap=apply  long=cancel                ║
╚═══════════════════════════════════════════════════════════════════════════════╝
▶

Source of truth: Foundry

Locale is read from game.i18n.lang at handshake. Catalogs come from dnd5e (en/de/es/fr/it/ja/ko/pl/pt-BR/ru/zh-CN) and the evenfoundryvtt module. The G2 ships no strings.

⌬

Override on glasses

Change it from the phone page (Language) or the glasses shortcuts menu (Lingua). Selection is device-local: it never modifies the world setting. Auto mode follows Foundry; an explicit override sticks per device.

▤

Width-budget by key

Every i18n key carries a _max width. The build verifies all locales fit, otherwise falls back to English. Layout integrity (INV-1) holds even when Italian strings run 30% longer than English.

🗺️ Phase 0–13, then direct streaming and relay pairing.

P0Week 0
Hardware / SDK validation
R1 events · updateImageRawData format · BLE bandwidth · partial-update API · DLE 4.2+ · GO/NO-GO decision tree (Branch A / B / C)
P1Week 1–2
Foundation
monorepo skeleton (pnpm + TypeScript) · shared protocol · CI pipeline · ADR-0001 to ADR-0004
P2Week 2–3
Foundry module core
readers (character / combat / scene / log) · WS server · capability handshake · contract fixtures
P3Week 3–4
Bridge skeleton
Fastify + ws · auth bearer 24 h · tool registry · Docker — removed in v0.12.0 (ADR-0016)
P4Week 4–7
G2 layered engine + map modes
layer manager · raster pipeline (Layers 1+3+4+6 base, 2+5 conditional) · glyph fallback · boot splash · render primitives
P5Week 6–8
Panel plugin system
Sheet 6-tab · Combat · Log · Spellbook · Inventory · auto-discovered panels · snapshot fixtures
P6Week 7–8
R1 + event plumbing
ring-r1 provider · Quick Action menu · gesture latency telemetry
P7Week 8–9
Foundry write path
Activity#use wrapper · v13 targeting · AbilityTemplate AoE · socketlib GM forward · MidiQOL workflow
P8Week 9–10
Manual action UX
tap-to-cast · tap-to-use · target picker · toast banner · action economy widget
P9Week 10–11
Action economy + edge cases
precondition checks · concentration drop · multi-attack · slot consumption · reaction prompts
P10Week 11–13
Polish + 4 h field test
offline mode · latency profiling · real D&D session · runbook + video demo
— end MVP — V2 OPTIONAL below —
P11Week 14–15V2
foundry-mcp server
MCP TS SDK · tools mirror Tool Registry — removed in v0.12.0; may return as a client of the direct channel (new ADR)
P12Week 15–16V2
Voice UX tuning
IT↔EN spell name lookup · Deepgram keyterms — removed in v0.12.0
P13post-V2V2
Stretch goals
reaction bot · biometric narrative cues · multi-player · dnd5e 6.x adapter · PF2e adapter · portrait images · Dice So Nice raster · headless Foundry session
— bridge era (superseded) —
v0.9.14–0.112026-06/07
Raster substrates on the bridge
27 px status sheet · canvas-compositor raster (v0.10.0) · showcase whole-HUD raster (v0.11.0) · headless player-view map capture · bearer pairing — released up to foundry-module v0.1.55; superseded in v0.12.0 (ADR-0016/0017/0018). Kept: the hardware lessons (grid-anchored tiles, image-over-text z-order, capture ' ', real-G2 BLE pacing) and the Foundry fixes (Activity#use dialog arg, null temp HP, spell method/prepared, skill checks).
— v0.12.0 · direct streaming —
v0.122026-09
Direct Foundry → G2 streaming
bridge + Docker + foundry-mcp removed · g2-app served by Foundry + QR sideload · hybrid projector (player or GM, ADR-0017) · AES-GCM sealed relay · D&D-sheet HUD · pixelated original map (ADR-0018) · Even Hub SDK 0.0.15 — Foundry-served page and «(G2)» users superseded in v0.13.0
— v0.13.0 · relay pairing —
v0.132026-09
Relay pairing · your tab projects
phone never logs into Foundry · player’s Foundry tab = projector · one QR, no GM · opaque sealed relay (Cloudflare Worker + Durable Object per room) · FoundryVTT G2 HUD on Even Hub + GitHub Pages + pnpm dev:glasses · Even Hub SDK 0.0.16 (ADR-0019) · next: Even Hub beta → store, hardware UAT (validate:relay)

🎙️ Voice: removed in v0.12.0, may return.

The bridge-hosted Deepgram proxy and the foundry-mcp server were removed together with the bridge (ADR-0016). The product is fully usable with gestures alone. Voice or MCP control may come back later as a client of the same direct channel — that needs a new ADR.

⌬

Mic capture: hardware-feasible

The G2 4-mic array streams PCM 16 kHz to plugins through the Even Hub SDK. Not used since v0.12.0. §3.5

⊟

AI on-glasses: not possible

EvenAI is proprietary, cloud-backed, and exposes no API to developer apps. Any future voice path must be external. §3.6

▤

No speaker → visual feedback only

The G2 has no audio output. Every result lands as a toast or a HUD update — never a beep, never a TTS line.

🧰 Open-source, mostly TypeScript.

Library choices are research-driven and documented as ADRs. Every external dependency was evaluated against alternatives in § 11.5.7 of the spec.

TypeScript pnpm workspaces Vite 8 Vitest Biome Changesets GitFlow GitHub Actions FoundryVTT v13.347+ / v14 dnd5e 5.x MidiQOL (opt) Even Hub SDK 0.0.16 @evenrealities/pretext Cloudflare Workers + Durable Objects WebCrypto AES-GCM @noble/ciphers + hashes (http fallback) Zod upng-js v2 jsQR qrcode