Skip to content
Yoann Frayce
All systems
July – August 2026

Omnivision

An assistant for blind wearers, built so it never guesses

The evidence layer of an audio-first assistant for blind and low-vision wearers of smart glasses: the system may only assert what it has direct evidence for.

One of six — owned the extraction and evidence layerView source ↗View architecture
Test suite
365 tests, 0 failures
whole package
Fixture evaluation
215 examples
37/37 commands · 90/90 names · 63 correctly rejected
False triggers
0
0 false command triggers · 0 false name extractions · 0 safety violations
Denylist
69 + 23
hard rejections and ambiguous entries

Figures with a dotted underline carry their source. Hover one to see whether it was verified in code or reported in project documentation.

Context

The situation

Blind and low-vision people navigate social situations without the cues everyone else uses. Existing assistive vision tools describe what is in front of the camera, then forget it. They also present a guess and a certainty in exactly the same tone of voice.

The problem

The failure modes are specific and they are all worse than saying nothing: a face matched to a name and stated as fact; a misread package label becoming a confident “yes, that’s your usual”; the assistant talking over the person the wearer is meeting; narration repeating on a timer until it is tuned out; and a silent fallback to the phone microphone that looks identical to success.

What I built

Make evidence a first-class type rather than a confidence score. Every candidate identity carries an evidence level derived from what was actually observed — a name spoken by the wearer, a corroborating repetition, a human confirmation — and the wording the assistant uses is derived from that level. Assertions and hedges are different sentences, not the same sentence with a probability attached.

For a blind wearer, a confident wrong answer is worse than silence — it gets acted on. Omnivision is an audio-first social-memory assistant for wearers of Ray-Ban Meta glasses, built around a single rule: the system may only assert what it has direct evidence for. A name is asserted only from an exact name token spoken aloud; a face match with no spoken attestation resolves to “likely”, never “known”; conflicting evidence returns “ambiguous” and the resolver refuses to pick. I owned the layer that decides whether the system has earned the right to say something: the command grammar, the name extractor, the evidence ladder and the denylist.

Architecture

How it fits together

  1. Glasses audioinput

    wearer speech and conversation

  2. Tokenisationdeterministic

    ASR-robust across speakers and rates

  3. Command grammardeterministic

    wake word + 9 commands

  4. Name extractiondeterministic

    templates, slots, validation

  5. Denylistguard

    hard and ambiguous rejections

  6. Evidence ladderguard

    may we assert, hedge, or stay silent

  7. Narrationoutput

    assertion or hedge — different wording

The extraction layer is Foundation-only Swift with no Apple framework dependency, so it builds, runs and is tested on Linux in CI without glasses, a microphone or a phone.

Key decisions

The choices that shaped it

Each of these was a fork in the road where the obvious option would have produced something that looked the same and behaved worse.
  1. 01

    Identity is bound from speech, never from a face

    Faces cluster for continuity only — they let the system know it has seen someone before. They never name anyone. A name enters the system only when the wearer naturally echoes it aloud, because that is the only signal that carries the person’s own attestation.

  2. 02

    Evidence levels, not confidence scores

    A score invites a threshold, and a threshold turns a weak signal into a strong claim at some arbitrary cut-off. Discrete evidence levels instead determine which sentence the assistant is permitted to say: an unattested face match resolves to “likely”, never “known”, and only a human confirmation promotes it.

  3. 03

    Conflicting evidence returns ambiguous

    When signals disagree the resolver refuses to choose. Picking the higher-scoring candidate would be the single most damaging behaviour in the product, because the wearer cannot see that it was a coin flip.

  4. 04

    Foundation-only, so it is testable without hardware

    The whole extraction layer avoids Apple frameworks and runs in a plain Swift container on Linux. On a two-day build with one pair of glasses shared across the team, waiting for the device to test string handling would have been fatal. Framework-dependent validation sits behind a protocol seam with a portable implementation.

  5. 05

    The fixture corpus is a gate, not a report

    The evaluation harness exits non-zero on any unmet expectation, so it can gate CI directly. Its hard requirement is zero false command triggers and zero false name extractions — in this product a false positive is the failure that matters, not a miss.

  6. 06

    Document what has not been measured

    The track’s own documentation carries an explicit table of what was validated on hardware and what was not — the wake-word false-trigger test, the battery and thermal profile, the end-to-end latency budget. Stating the gap is what makes the measured numbers worth anything.

Evidence

What it actually looked like

Presenting Omnivision in a lecture theatre at the CREATE Accessibility Hackathon. The screen behind reads “Someone greets you by name. You have no idea who they are.” An ASL interpreter signs beside the stage, and the audience includes a wheelchair user and a guide dog.

The room the product was built for. The problem statement on screen is the one the evidence layer exists to answer.

Presentation

Measured

Every figure, with its source

Test suite
365 tests, 0 failures
whole package
Fixture evaluation
215 examples
37/37 commands · 90/90 names · 63 correctly rejected
False triggers
0
0 false command triggers · 0 false name extractions · 0 safety violations
Denylist
69 + 23
hard rejections and ambiguous entries
Audio path
16 kHz
wideband, against 8 kHz in the vendor documentation — the native rate ASR models are trained on

Figures with a dotted underline carry their source. Hover one to see whether it was verified in code or reported in project documentation.

My contribution

One of six. Owned the speech-understanding and evidence layer.

What I owned

  • The wake-word command grammar and its nine-command parser
  • Name extraction from wearer-echoed speech: templates, slot resolution and validation
  • The evidence ladder — the levels that decide whether the system may assert, hedge or stay silent
  • A curated name denylist separating hard rejections from ambiguous cases
  • Speech tokenisation and ASR-robustness handling across speakers, rates and accents
  • The fixture evaluation harness and its corpus, which exits non-zero on any unmet expectation so it can gate CI

What I did not

  • Face clustering, the person store and the identity resolver’s upper levels
  • Bluetooth routing, camera capture and the glasses integration
  • Narration, earcons, OCR and the shop-assist service

Stack and limits

Built with

  • Swift 6
  • SwiftUI
  • iOS
  • Meta Wearables DAT SDK
  • Core ML
  • Vision
  • Swift Package Manager
  • XCTest
  • Python
  • FastAPI

Techniques

  • Wake-word command grammar
  • Template-based name extraction with slot resolution
  • Evidence-level identity resolution
  • ASR robustness fixtures
  • On-device inference (Core ML, Vision)

What it is not

  • I did not write the majority of the repository. Face clustering, narration, OCR and the shop-assist service are teammates’ work.
  • The extraction layer is deliberately rule-based rather than learned, which is the right trade for a zero-false-positive requirement but is not a modelling contribution.
  • Parts of the hardware validation were still outstanding when my track was documented, and are listed as such in it.

What carried forward

This is the same boundary as the decision work, in a different language and with a human cost attached. There, an unsupported number misleads an operator. Here, an unsupported name misleads someone who cannot check it. Both are solved the same way: make the right to assert something the system has to earn, and encode what earns it.