Case study

An ambient AI assistant for the home

A wall-mounted display and Persian-speaking voice assistant that lives in the room instead of on a screen: weather as art, music as color, and a voice that knows my day.

Product designer and owner · Sep–Oct 2026 · Product brief, interaction, motion and voice UX · Raspberry Pi 5, React, realtime voice

Hero photo: the wall display in its frame, weather state

At a glance

Role

Product designer and owner: product brief, decisions, interaction and motion design, voice UX. Designed and built with AI coding tools, with me directing and reviewing every pull request.

Team

Me and AI coding agents.

Timeline

First commit on 22 September 2026, active through October 2026. Hardware bring-up is in progress.

Status

Software works end to end in a desktop preview and an ARM64 virtual machine. Validation on the Raspberry Pi is underway.

Platform

Raspberry Pi 5 kiosk, React and TypeScript UI, a Node backend over WebSocket, a WebGL weather engine, an on-device wake word (openWakeWord), a full-duplex realtime voice model, and a self-hosted agent server (OpenClaw) for memory and actions.

One assistant, many surfaces

Phones demand attention, smart speakers are blind, and “smart displays” are dashboards: ten tiles, all equally loud. I wanted something on the wall that is beautiful when you ignore it, useful at a glance, and conversational when you talk to it, in Persian as people actually speak it.

It had a brain to plug into: a personal assistant I’d built on a self-hosted agent server, with memory, reminders, a morning brief, home control and Telegram. So the real problem was never the screen.

“How does one assistant show up on many surfaces (chat, voice, a wall) and stay the same assistant?”

On the wall · one mode at a time

9 modes

57 decisions

916 tests

19 days

The numbers, written the way the display would show them: one quiet line. Sources in By the numbers.

PRD snapshot

What V1 had to be

Goal

An ambient home computer that never looks like a dashboard, with a voice that shares one memory with the text assistant.

Users

The owner, hands busy, across the room. Guests, who should see nothing personal.

Success criteria

Glanceable in under a second from across the room: one state on screen at a time.

Voice answers to “what’s my day” or “what’s playing” are immediate, with no backend round trip.

The voice and the chat assistant never disagree about what’s true today.

Nothing personal is shown or said when guests are over.

Runs on a 2 GB Raspberry Pi, a budget kept even after a 4 GB board arrived.

Non-goals for V1

Camera, presence and ambient-light sensing. Touch UI on the wall. A chat transcript on screen. Multi-user profiles.

Constraints

Low-power ARM hardware. A model quota shared with my own daily AI use. Free or non-commercial APIs. No public ports, private network only. Persian and English mixed in one sentence.

Challenge 1 of 5

Do not look like a dashboard

Situation

Weather, music, lyrics, voice, morning brief, reminders, trip nudges, cards and night mode all want permanent real estate. That is how smart displays become tile grids.

Options

Tiles. A carousel. Or one exclusive full-screen state with a priority order.

Decision

One state at a time (D4), resolved by a pure state machine with a fixed priority. Interrupts win; everything falls back to weather.

I wrote the principles down: ambient first, utility second; voice is a mode, not a chat window.

Priority order, highest first · D4

01 Brief

02 Voice

03 Card

04 Night

05 Camera

06 Lyrics

07 Music

08 Weather

On screen now

Nothing else wants the wall, so it shows the sky.

1 of 9 full-screen states · everything falls back here

A stack, not a grid. Anything higher in the deck interrupts anything lower, and only the front screen is ever drawn.

Outcome

Nine full-screen states, and the state logic is a pure, unit-tested function.

State-machine diagram: the 9 states, interrupts and the priority order

Every state, and what is allowed to interrupt it.

Challenge 2 of 5

Voice that is fast, cheap and actually remembers

Situation

A full-duplex realtime voice model handles the conversation, but memory, reminders and tools live in the agent server. Its model quota is shared with my own use. On 24 September it hit its limit, and replies failed.

Why it was hard

Routing everything to the agent is slow, and silence feels broken in voice. It also burns quota. Routing nothing makes the voice a stranger.

Function tools were not an option: the voice model’s delegation channel carries no task text.

Decision

Four layers (D48), ordered by how close the answer already is. Weather and music come from the display’s own state (D43, D44), and filler lines cover the waits.

The question

Spoken to the wall.

L1 Identity and tone

No model call

A file in the agent’s workspace, re-read only when its timestamp changes.

L2 Today

No model call

The “today” snapshot the chat assistant writes every 5 minutes.

L3 Memory search

Search only

Only for a strictly defined pure recall question.

L4 Agent

Agent call

Actions, and everything else.

The line gets longer the further an answer has to travel. Three of four layers answer without the agent.

Outcome

Day, weather and music answers are instant; the agent runs only when something has to happen. The bilingual recall classifier is tested on edge cases: “who directed…” must not hit personal memory.

Challenge 3 of 5

Persian voice: wake word, turn-taking and colloquial speech

Situation

Off-the-shelf wake-word engines had no Persian, and Persian voice etiquette doesn’t map onto English phrase lists.

Decision

I trained a custom “Hey Ari” wake word (D27) on synthetic clips in 13 voices, with near-miss negatives such as “hey Siri” and “hey Harry”.

Turn-taking

A conversation ends on 15 seconds of silence, a goodbye in either language, or 5 minutes (D30).

A goodbye said while the agent is working is held, and «صبر کن، یه چیز دیگه» (“wait, one more thing”) takes it back (D37). An evening “good night” also puts the display to sleep (D41).

One conversation, start to end

Illustration, not a recording

Hey Ari

15 s of silence

Ends on

15 seconds of silence, a goodbye in either language, or 5 minutes (D30).

Held

A goodbye said while the agent is still working waits for the answer.

Taken back

Saying «صبر کن، یه چیز دیگه» (“wait, one more thing”) cancels the goodbye (D37).

Wake word (D27)

Trained on synthetic clips in 13 voices. Ignores near misses like “hey Siri” and “hey Harry”.

Outcome

An on-device wake word, and endings that behave the way people talk. These rules came from living with it, not from a spec.

Wake-word training pipeline: synthetic voices, near-miss negatives, on-device model

Trained on synthetic speech, tested against the names it must ignore.

The voice screen

While the assistant listens, thinks and speaks, the screen shows one orb and no transcript. The orb changes color and motion with each phase, so you can tell what it is doing from across the room.

Smart display in voice mode while listening: a single teal and blue orb on a dark screen, no text. Demo data.

Listening: the orb swells with the level of your voice. Demo data.

Smart display in voice mode while speaking: a single orange and pink orb on a dark screen, no text. Demo data.

Speaking: the same orb, with a different color and motion. Demo data.

Challenge 4 of 5

Privacy in a shared room

Situation

The display lives in a shared space, and it knows a lot.

Guest mode

Saying «مهمون داریم» (“we have guests”) or flipping a switch on the phone hides everything personal and stops memory search (D40). It turns itself off at 03:00.

Guardrails

The mic sleeps from 01:00 to 08:00 (D36). The display’s agent credential is never admin (D29). Phone control is private-network only, with no open ports (D17). Personal context is held in memory and never logged.

Twenty-four hours in the room

01:00 → 08:00 mic asleep (D36)

03:00 guest mode switches itself off (D40)

00:00

06:00

12:00

18:00

Privacy runs on the room’s clock, not on a settings page.

Smart display with guest mode off: the everyday weather screen, Lisbon, 16 degrees, hourly and weekly forecast. Demo data.

Guest mode off: the everyday screen. Demo data.

Smart display with guest mode on: the same weather screen with a welcome line and a Wi-Fi QR code for the guest network, nothing personal shown. Demo data.

Guest mode on: the same moment, nothing personal. Demo data.

Outcome

Privacy is a mode you switch on by voice in one sentence, not a settings page.

Challenge 5 of 5

Motion and color that read from across the room

Situation

Music and weather had to feel alive without becoming noise.

Music

Music is only a circular cover, rotating every 30 seconds with a 24 px, 440 ms blur crossfade (D20), over a wash of the album’s main colors, darkened to keep 7:1 text contrast (D42).

Lyrics

One line at a time, crossfading 300 ms before the cue (D21). The music API is polled every 15 seconds, so playback is projected locally for up to 20 seconds to stay in sync (D22).

Weather

WebGL with the real sun, moon phase and stars for the city, with a CSS fallback (D38). Reduced motion is respected throughout.

Now playing · lyrics

One line at a time.

The next line is already fading in.

30 s

The round cover turns (D20)

24 px · 440 ms

Blur crossfade between covers (D20)

300 ms

Lyric fades in before its cue (D21)

15 s → 20 s

API poll, then local projection to stay in sync (D22)

7:1

Text contrast floor on the color wash (D42)

The music screen, drawn as its own spec sheet.

Music and lyrics on the wall

While a song plays, the screen shows only the cover. If synced lyrics exist, the current line takes over and sits centered over colors taken from the album.

Smart display showing the now-playing screen: only the album cover, a neutral demo cover. Demo data.

Now playing: only the cover, nothing else on screen. Demo data.

Smart display showing one line of Persian lyrics, centered over a soft wash of the album colors. The line is a public-domain verse. Demo data.

Lyrics: the current line, a public-domain verse. Demo data.

Recording: weather, day to night

Recording: cover rotation and color wash

Recording: lyric crossfade, non-copyrighted text

Recording: voice orb, listening, thinking, speaking

Screen recordings with placeholder data.

Design decisions and system

A decision log as the system’s spine

Decision log

57 numbered decisions, each Locked, Provisional or Rejected, with a date and a rationale, and a master brief that went through 36 versions. Each change to a locked decision names what it amends.

Six of 57, pinned

D4

One state at a time

A pure state machine with a fixed priority decides what is on the wall.

D20

The cover turns

Every 30 seconds, with a 24 px, 440 ms blur crossfade.

D27

«Hey Ari»

A custom wake word, trained on synthetic clips in 13 voices.

D29

Never admin

The display’s agent credential is never an admin one.

D40

Guest mode

One sentence hides everything personal and stops memory search.

D48

Four voice layers

Files first, the agent last.

Each entry in the log carries an ID, a status (locked, provisional or rejected), a date and the reason. A change to a locked decision names what it amends.

Information architecture

The wall is ambient output. Voice is the primary input. A phone control page is the settings: city, quiet hours, the guest switch, music pairing. No touch on the wall.

Typography

Vazirmatn for Persian, Inter for English, chosen per line so mixed-language lyrics and cards render correctly right to left and left to right.

Voice screen

No transcript. Just an orb with listening, thinking and speaking looks.

One memory, many surfaces

One source of truth for reminders and automations (D5). The voice reads the same “today” snapshot the chat assistant writes every 5 minutes, and finished voice conversations are written back as text, so the chat side knows what was said on the wall.

Edge cases

A 5-second pause grace, lyric intros and outros, failed reads that keep the last good copy, a brief that interrupts a conversation (D26), and “couldn’t check” only when every layer fails. Never guess.

Evidence

By the numbers

smart-display · main · counted 11 Oct 2026

$ git log --oneline | wc -l

420

commits on main

$ gh pr list --state all

51

pull requests, 50 merged

$ git log --format=%ad

19

days, 22 Sep → 10 Oct 2026, with commits on 12

→ docs/decisions.md

57

numbered product decisions, D1–D57

→ docs/

36

versions of the brief, v0.3 → v0.38 (2,093 lines)

→ app/src/states/

9

display states in stateMachine.ts

$ grep test( / it( / def test_

916

test cases: 907 JavaScript, 9 Python

$ wc -l app/ server/ wakeword/

~16.9k

lines of source, excluding tests

→ D42

7:1

contrast floor for lyrics and music text

Counted from the project’s repository on 11 October 2026. Still to measure on the Pi: wake-word false accepts and rejects, time to first word per layer, the share of answers with no agent call, and WebGL frame rate.

What I learned

A decision log beats a spec for a living product. Fifty-seven dated decisions let me and the AI agents change things without losing the why.

Latency is the voice UX. The biggest win was moving answers closer: files, local state, search, then the agent.

Privacy has to be a gesture, not a setting.

Next: finish validation on the Pi, measure the numbers above, keep or drop WebGL weather by its real frame rate, then presence sensing in V2.

Ambient first, utility second. Voice is a mode, not a chat window.