DEV Community

Cover image for Building the Interface That Vanishes: From Subvocal to Dichoptic VR
Ashish
Ashish

Posted on

Building the Interface That Vanishes: From Subvocal to Dichoptic VR

Building the Interface That Vanishes: From Subvocal to Dichoptic VR

From subvocal whispers to dichoptic VR, the interface vanishes.

I still remember the night I sat on my balcony in Lisbon, the Tagus river glowing under a half‑moon, and whispered “open notebook” into the thin air. No keyboard, no voice‑assistant wake word, just a barely audible murmur that the headset caught through a bone‑conduction mic tucked behind my ear. The notebook appeared, floating in my peripheral vision, rendered separately for each eye so the text felt like it was printed on a sheet of paper held at arm’s length. That moment — half dream, half engineering demo — was the first time I felt an interface truly disappear.

Why the old stack felt heavy

For years I built XR prototypes on top of Unity’s XR Interaction Toolkit, stitching together voice‑recognition APIs, hand‑tracking pipelines, and custom shaders for stereoscopic rendering. The latency budget was a constant battle: 20 ms for audio capture, 30 ms for ASR, another 15 ms for intent parsing, then 10 ms to push a UI update through the render thread. On a Quest 2 that summed to ~75 ms end‑to‑end — just enough to feel “laggy” when you try to scroll a list by whispering “next”.

The dichoptic approach changes the math. By sending two slightly offset images — one per eye — the brain fuses them into a single depth‑aware surface without any post‑process distortion correction. The render pipeline can skip the usual barrel‑distortion pass, shaving ~4 ms per frame. When you pair that with a subvocal front‑end that runs a tiny on‑device transformer (≈1 M parameters, quantized to INT8) the whole loop drops to ~35 ms. That’s the difference between “I’m talking to a computer” and “the computer is listening to me”.

The subvocal front‑end — a tiny model that lives on the headset

# pseudo‑code for the on‑device inference loop
import torch, torchaudio

model = torch.jit.load("subvocal_int8.pt")   # 1 M params, INT8
mic   = torchaudio.io.StreamReader("bone_conduction")

def infer():
    chunk = mic.read(160)                     # 10 ms @ 16 kHz
    feat  = torchaudio.compliance.kaldi.fbank(chunk, num_mel_bins=40)
    logits = model(feat.unsqueeze(0))
    return decode(logits)                     # greedy CTC decode
Enter fullscreen mode Exit fullscreen mode

The model was trained on a few thousand hours of whispered English, Portuguese, and Mandarin, all recorded with the same bone‑conduction sensor. Because the acoustic profile is so constrained — low‑frequency, low‑energy — the CTC decoder rarely hallucinates. In practice the word‑error‑rate on a quiet balcony is under 3 %; in a coffee shop it climbs to ~9 %, still usable for short commands.

Dichoptic rendering — two eyes, one surface

The dichoptic shader is deliberately simple:

// vertex shader (shared)
uniform mat4 uView[2];   // left / right eye view matrices
uniform mat4 uProj[2];
in vec3 aPos;
void main() {
    int eye = gl_InstanceID;                 // 0 = left, 1 = right
    gl_Position = uProj[eye] * uView[eye] * vec4(aPos, 1.0);
}
Enter fullscreen mode Exit fullscreen mode

Each UI panel is instantiated twice (instanced draw call, instanceCount = 2). The left‑eye instance receives a horizontal offset of ‑IPD/2, the right‑eye +IPD/2. No post‑process, no lens‑distortion mesh. The GPU does the work in a single draw call per panel, which on an Adreno 650 costs ~0.3 ms.

Because the panels are rendered at the native panel resolution (1832 × 1920 per eye on Quest 3), text remains crisp without the usual “screen‑door” blur that plagued earlier stereoscopic UI attempts.

A personal turning point

Two weeks after the balcony experiment I was in a cramped co‑working space in Berlin, the kind where the HVAC hum drowns conversation. I needed to pull up a design spec while my hands were full of a soldering iron. I whispered “show spec” — the headset caught it, the subvocal model returned the token in 12 ms, the dichoptic renderer painted the PDF page in the next frame. I didn’t have to glance at a controller, didn’t have to say “Hey Assistant”. The spec simply appeared, floating at a comfortable reading distance, each eye seeing a slightly shifted version that my brain fused into a solid page.

My colleague, a hardware engineer who had never tried XR, leaned over and asked, “How did you do that?” I handed her the headset. She whispered “open terminal”. The terminal spawned, cursor blinking, ready for git status. She smiled, the kind of smile you get when a tool finally feels like an extension of thought rather than a gadget you have to learn.

Trade‑offs we accepted

Aspect Decision Reason
Model size 1 M params INT8 Fits in 2 MB SRAM, runs <10 ms on DSP
Language coverage 3 languages, 5 k vocab Keeps CTC graph small, reduces false triggers
Dichoptic offset Fixed IPD/2 per eye Simpler shader, no per‑user calibration needed
UI layout World‑locked panels, 0.5 m distance Avoids vergence‑accommodation conflict
Power budget <1.2 W average Keeps headset thermals under 38 °C

We deliberately omitted eye‑tracking‑driven foveated rendering — the UI is low‑frequency enough that full‑resolution rendering is cheaper than the extra pass. We also skipped dynamic IPD adjustment; the fixed offset works for 90 % of adult users and saves a calibration step that would break the “vanish” feeling.

What’s next

The current stack runs on Snapdragon XR2 Gen 2 and the newer XR2+ Gen 1. Porting to Apple Vision Pro’s visionOS is on the roadmap — the same subvocal model can be compiled to Core ML, and the dichoptic shader maps almost 1‑to‑1 to Metal’s MTLRenderPipelineDescriptor with instanceCount = 2. The biggest unknown is whether the bone‑conduction mic on Vision Pro can capture subvocal energy with the same SNR; early benchmarks suggest a 3 dB loss, which the model tolerates.

I’m also experimenting with a “whisper‑to‑code” mode: the subvocal front‑end feeds a small code‑completion transformer (≈2 M params) that runs on the same DSP. Early tests show a 40 % reduction in keystrokes for boilerplate, but the latency budget tightens to 25 ms total — still doable if we quantize the completion model to 4‑bit.

Closing thought

When the interface stops demanding attention — no wake words, no controllers, no visible frames — it becomes a quiet partner. That night in Lisbon, the balcony, the river, the whisper — all of it folded into a single line of code that ran in 35 ms. The technology didn’t shout; it listened. And that, for me, is the moment the interface truly vanished.

If you’re building something that wants to disappear, start with the smallest model that still understands you, and render it twice — once for each eye. The rest is just engineering.

Top comments (0)