01: Context
The phone was always in the loop.
I bought the G1 for one job: to know when Claude Code needs me without looking at a screen. Out of the box that is not what the glasses do. Even Realities ships them with a phone app that owns everything. Notifications pass through it. Weather passes through it. The AI assistant passes through it. The glasses are a peripheral of the phone, and the phone is always in the loop.
So the first decision was to take the phone out. Replace the companion app with a desktop-hosted runtime that owns the G1 over BLE, and let the glasses become a direct surface for whatever is running on the machine.
That gave the build a specific north star. The glasses answer one question, when can I give feedback or guidance? Live tool progress, stop notifications, and session state from Claude Code, with no phone in hand and no window switch.
02: The Constraint
Forty characters. Five lines. Green.
The G1 display is monochromatic green, roughly 488 pixels wide. That gives you about 40 characters per line and 5 lines per screen. No scrolling, no font weights, no color. The window sits in peripheral vision at a 20° field of view, so whatever appears has to be readable in under 300 milliseconds. Anything slower is a distraction.
Input is voice and a small touch surface on the temple arm. There is no tap target, no scroll gesture, no pointer. Most UI patterns do not transfer. The design question is what does, and what should never appear in the window at all.
The hardware splits too. Left and right arms are separate BLE connections, both paired, both synchronized: send to the left, wait for the ACK, then send to the right. Done naively that takes 1.6 seconds per frame, which for a glance is the same as never arriving.
Line width
Maximum characters per line on the monochromatic green display.
Screen depth
Total visible lines. No scrolling, no paging.
Field of view
Peripheral vision. Has to land in a glance.
Dual BLE
Left and right are separate connections; both must sync.
03: The D0 Probe
Co-opt. Don’t suppress.
The first assumption did not survive the first device probe. The stock firmware HUD (time, weather, date) cannot be fully hidden. The hide_dashboard command only repositions it. I had planned the whole display around a blank canvas, and there was no blank canvas.
So instead of fighting the firmware layer, I co-opted it. The 0x06 BLE command lets you feed the stock HUD your own data: your own time format, your own weather string. Set it to minimal mode so it takes as little room as possible, then take over the “Even AI” canvas, the second display layer, for everything that matters.
The result is a two-layer model. The firmware HUD carries ambient context at the bottom of the display. The custom canvas carries active content: tool progress, stop notifications, session state. The layers share the window, and that one discovery set the display strategy for everything after it.
Three-ring takeover
Ring 1: Own the companion connection (BLE pairing, bypass phone). Ring 2: Own the display (firmware co-option + custom canvas). Ring 3: Own the experience (attention tiers, context routing, voice).
Dual-layer display
Firmware HUD: ambient (clock, weather, battery) via 0x06. Custom canvas: active content (tool progress, notifications, state) via send_text/send_result. Two layers, one window.
04: The Build
Glasses as a coding surface.
The runtime is a Python FastAPI daemon on macOS. It holds persistent BLE connections to both arms, exposes an HTTP API for integrations, and runs display state through a mode machine: idle home, tool progress, stop notification, tilt dashboard.
Claude Code hooks are the primary integration. An on-tool hook fires on every tool use, file reads, edits, shell commands, and pushes live progress to the glasses. An on-stop hook fires when the agent stops and needs input, and the glasses show a summary frame: task count, tool count, and a “your turn” indicator. I stopped switching windows to check on it.
Transport was the hardest infrastructure problem. Sequential writes took 1.6 seconds per frame, and tool progress fires several times a second. Parallel dual-arm writes push the same content to both arms at once and bring that down to about 150ms. A SHA-256 content hash skips the write entirely when the display already shows the right frame.
Claude fires event
on-tool or on-stop posts JSON to the local daemon.
40-char layout
Word-wrap, truncate, pad to the active display anchor position.
Dedup check
SHA-256 content hash; skip the BLE write if the display is current.
Parallel BLE
Dual-arm write in ~150ms (vs 1.6s sequential).
Glance frame
Tool name + status on the 40×5 canvas. Readable at a glance.
Dashboard on gesture
Head-up triggers clock + weather + session state overlay.
05: Display Modes
Three stances for attention.
Once frames arrived fast enough, the harder question surfaced: what should the glasses say when nothing is happening? The idle state is most of the day. The state machine has three layout modes, selectable in configuration:
Collaboration
Readiness-first. Shows “G1 OS” header, last action with time-ago label, session stats (tasks/tools). The idle state answers: is anything happening, and do I need to look?
Zen
Single word when idle. Nothing else. For deep work where the glasses should disappear until an interrupt arrives.
Dev
Inline tool progress. Every file read, every edit, every shell command streams to the display as it happens. For pairing sessions where you want full visibility.
Active frames override all modes: tool progress shows a spinner animation with the current tool name (>> Edit, ✓ Read file.py, ✗ Edit). Stop notifications show a summary frame with an ASCII progress bar, task count, and “your turn” indicator. Head-tilt triggers a dashboard overlay regardless of mode.
06: Mac Companion
The control surface the phone app isn’t.
Display configuration needed a home once the phone app was gone, so a native macOS app took its place. The Even app uses a vertical “ladder picker” (positions 0–8) for display height; the Mac app mirrors that mental model exactly, then adds the controls the phone app does not expose: head-up angle (0–60°), display depth (1–9), text anchor position, and alignment.
Geometry changes use the Even app’s two-step preview and apply flow, because the glasses physically move the display and you need to confirm it feels right before committing. Brightness is a direct slider (0–41) with an auto-brightness toggle.
The app also shows daemon health: per-arm BLE state, Claude session activity, current display mode, and the last push timestamp. A live activity card mirrors what the glasses are showing, which makes layout debugging possible without wearing the device.
07: What’s Still Open
The hard problems are not display.
Display is solved enough to be useful. The problems I have not solved sit higher up the stack:
Attention tiers
Not everything deserves a glance. The system needs to tell interrupt (look now) from ambient (there if you look) from quiet (suppressed until asked). Today everything is “always notify,” which is the wrong default.
Voice input
The G1 mic sends LC3-encoded audio from the right arm only, never raw PCM, so decoding through liblc3 has to happen before Whisper can transcribe. Voice would close the loop: the glasses show state, and I answer without my hands.
Context at glance-scale
40 characters is enough for “Edit: server.py” and nowhere near enough for why. The system shows what is happening. Showing whether you should care needs inference the display layer does not have yet.
Failure at speed
Wrong at glance-scale is worse than no output. If the model produces something incorrect and I trust it because I only glanced, that is a harder failure than a phone screen, where there is time to verify. This is the problem the whole project is really about.