An AR toothbrushing coach for Snap Spectacles (2024) that talks to real Philips Sonicare toothbrushes over Bluetooth — and puts the brushing HUD on your reflection in the bathroom mirror.
BrushAR.Short.Video.mov
Connect a Sonicare, walk up to your mirror, and press the brush's own button: a face-anchored HUD appears at your reflection's chin with the session timer, a progress board whose boxes fill the way your teeth face (uppers pour down, lowers rise up), sparkle bursts at your mouth, a celebration finale, and an AM/PM streak system with a global leaderboard. On a DiamondClean Smart 9300, advanced mode goes full pro: the board becomes the manual's true 6-zone mouth map (mirrored for the mirror — your upper-right is the top-right box) paced by each mode's real schedule (Clean, White+ with its polish steps, Gum Health with its gum-care steps, DeepClean+, Tongue Care), with the current mode name on display, a live pressure gauge (green → amber → red from the brush's real sensor and over-pressure alarm), the handle's 3-light intensity setting as dots, and frame-accurate session states from the brush's own session engine — including treating the 9300's auto-power-off at routine end as the completion signal it is.
- Mirror AR that actually works. Face-tracking your reflection on
Spectacles (2024) breaks nearly every assumption the Head Binding API
invites you to make.
docs/MIRROR-AR.mddocuments the mechanism that survived twenty rounds of on-device elimination — including the depth and lateral corrections a stereo display demands — and everything that doesn't work, so you don't have to rediscover it. - A reusable Sonicare BLE client for Lens Studio.
docs/SONICARE-BLE.mdcovers the GATT protocol, the Spectacles BLE quirks (indications silently die; subscriptions keep the link alive), and how to dropSonicareClientinto your own project. - The whole experience runs in the editor with zero hardware. No
Spectacles, no toothbrush required:
simulateInEditorfakes the brush, the face-lock, a 12×-speed brushing session, a mid-session disconnect drill, and the 9300's pressure stream — the full loop, every run.
| Piece | Notes |
|---|---|
| Snap Spectacles (2024) | Experimental API enabled (BLE requires it) |
| Philips Sonicare For Kids HX6321 | The kids flow (timer-guided quadrants) |
| Philips DiamondClean Smart HX992x | Advanced mode: pressure gauge + true session states |
- Lens Studio 5.15.4 — exactly. That's the Spectacles (2024) release; opening the project in 5.22+ performs a one-way migration aimed at the newer hardware generation.
- Open
BrushAR.esproj. - Press play. The editor simulation runs the entire experience:
detection modal → mirror face-lock → brushing with quadrant fills,
chimes, and mouth sparkles → celebration → streak + leaderboard, plus a
brush-disconnect drill. Flip
SIM_PROFILEat the top ofBrushARController.tsbetween'kids'and'advanced'to preview either tier. - On device: Project Settings → enable Experimental API → push to Spectacles. Take the brush off its charger (Sonicares never advertise while charging) and press its button once to wake it. Close the Philips phone app — the brush accepts a single BLE connection. Stand at a bathroom mirror.
MENU (auto-scan) ── brush found ──► BRUSH DETECTED (name · battery · mode)
▲ │ OK tap · face fast-path (2 s) · or motor-on shortcut
│ ▼
CONGRATS ◄── 2 s sparkle MIRROR (4 s face-lock on your reflection)
streaks · celebration │
leaderboard ▲ ▼
▲ │ READY "Start brushing!"
└─────────────┴── BRUSHING ◄─────┘
face-anchored HUD · anatomical progress board
(kids 2x2 · 9300 6-zone mouth map + mode name,
pressure gauge, intensity dots) · chimes ·
zone flashes · mouth sparkles
Niceties along the way: pressing the brush's physical button anywhere in the flow jumps straight to brushing (the HUD syncs to the brush's own session clock); a mid-session pause re-prompts and resumes; losing the brush entirely fails into a rescan flow; sessions record into an AM/PM streak model with comeback messaging.
| Piece | Role |
|---|---|
Assets/Scripts/BrushARController.ts |
The state machine, panel layout (all code-side), the face-anchored HUD twin + mirror composition, celebrations, editor sim |
Assets/Scripts/Sonicare/SonicareGatt.ts |
Protocol tables: UUID scheme, services, characteristic definitions, decoders |
Assets/Scripts/Sonicare/SonicareClient.ts |
Reusable BLE client: scan → connect → subscribe → typed live state, poll/notify hybrid, sensor stream control |
Assets/Scripts/Sonicare/BrushProfile.ts |
Family + capability detection (from the discovered GATT topology, not the model name) |
Assets/Scripts/Sonicare/SonicareProbe.ts |
Disabled-by-default GATT exploration tool |
Assets/Scripts/Screens/ |
ForeheadHud (kids quad board + 6-zone segment board, pressure gauge, intensity dots, mode caption), MirrorSeekScreen (face-lock ring), LeaderboardPanel, BatteryBadge, shared palette |
Assets/Scripts/StreakStore.ts |
Persistent AM/PM streak model |
- Sonicare GATT knowledge builds on mtheli/philips_sonicare_ble — an excellent protocol reference.
- Built with Snap's Spectacles Interaction Kit + UI Kit.
- Sounds and the sparkle VFX are project-local assets.
MIT — take the client, the mirror math, whatever helps. Built by Ralph Barbagallo (@flarb).
