BMO
A tiny desk companion with a face, a voice, a memory core, and five little buttons that make the hardware feel alive.
Spin it in 3D & print it yourself on a Bambu Lab A1See it in action
BMO, listening and talking back.
A quick clip of the push-to-talk loop: hold the touch sensor, speak, release — and BMO answers out loud with a live lip-sync face.

Playable information
Every button teaches one part of the build.
Touch the controls, watch the face answer, then scroll into the little body to see which organ does the work.
Feature map
What the tiny toy actually does.
The landing is cute, but the build is practical: each interaction is a visible state backed by a real hardware or software role.
Expression engine
The firmware owns the face library: idle, touch, listen, thinking, talking, laughing, bashful, and the wider mood set.
- User sees
- BMO blinks, glances, smiles, talks, reacts to touch, and never feels like a static image.
- How it works
- A 160x128 RGB565 back-buffer is composed around 30 fps and flushed over hardware SPI.
Touch language
One capacitive pad becomes several gestures instead of a single boring button press.
- User sees
- Tap gives a small surprise, hold opens listening, long-hold gets shy, and rapid taps can become a laugh moment.
- How it works
- The firmware debounces the touch line, classifies press timing, and maps it to mood plus audio behavior.
Voice loop
BMO moves from listening to thinking to talking, so voice input has a visible status at every step — and the talking mouth lip-syncs to the reply.
- User sees
- The face shows a glitchy listening state while recording, a pulsing thinking orb during the round-trip, and a lip-synced mouth while the reply plays.
- How it works
- Held-touch captures auto-gained low-rate mono audio (decimated so a fixed buffer holds ~6s) sent to the brain route; streamed PCM16 feeds the I2S speaker while a live envelope drives the mouth.
Tiny voice pack
Short local clips keep instant reactions fast, while streamed replies cover open conversation.
- User sees
- BMO greets you instantly (and lip-syncs) on a normal touch, and still speaks longer generated replies when the brain route answers.
- How it works
- Greetings are generated once in BMO’s real voice, downsampled, and baked as 4-bit ADPCM into firmware; generated replies stream to avoid storing large audio.
Memory core
A gbrain-inspired layer gives BMO short-term conversation continuity plus a durable, self-updating profile of the child.
- User sees
- Follow-ups make sense (apple → “red” stays on topic), and BMO remembers the child’s name — learned, never hardcoded, newest value wins.
- How it works
- Recent turns are replayed as chat history; stable facts are upserted by key and recalled by vector similarity before each reply. Fully degradable.
Live web search
For questions that depend on current facts, BMO can look things up on the web instead of guessing from stale training data.
- User sees
- Ask "who is the president now" or "what is the weather today" and BMO answers from fresh results, not an out-of-date memory.
- How it works
- When the web_search skill is on, the brain call enables the OpenRouter web plugin and nudges the model to trust live results; citations and URLs are stripped before the reply is spoken.
Editable personality
BMO’s persona, tone, and reply language all come from one editable soul prompt — the single source of truth.
- User sees
- Change the soul on the dashboard to make BMO talk differently or reply in another language; it takes effect on the next request.
- How it works
- The brain route sends the soul verbatim as the system prompt (plus only the live clock). No hardcoded language or style clamp overrides it.
Secure cloud bridge
The device can talk to the cloud brain without making the private operator controls public.
- User sees
- A paired BMO can call the brain route; an unpaired request gets rejected.
- How it works
- Firmware sends an X-BMO-Fingerprint header while the server stores only the hashed value.
Private operator console
The public homepage explains the build, while the admin controls stay intentionally unlisted.
- User sees
- Visitors see the landing and wiki; the operator still has a private route for tuning the device.
- How it works
- The operator route remains behind Supabase login and is excluded from public navigation and sitemap.
Open build kit
The project is meant to be hackable: firmware, voice tools, wiring notes, and the web brain live in the repo.
- User sees
- A builder can inspect the code, flash the device, replace clips, and iterate on the shell.
- How it works
- PlatformIO handles firmware flashing; Next.js, Supabase, and small scripts handle the cloud side.
Inside BMO
Real hardware, cast as tiny organs.
The screen is the feeling window. The ESP32-C3 is the pocket brain. The mic, speaker, touch pad, and memory core each get a visible role, so the device reads as a character instead of a box of parts.
Read the build wiki
Feeling WindowRounded TFT faceeyes, glances, and mouth shapesPocket BrainESP32-C3Wi-Fi, timing, mood stateListening SproutI2S microphonevoice awarenessVoice StarTiny speakersmall voice outputKind ButtonTouch sensortap, hold, long-holdWonder HeartMemory coreGBrain-inspired recall3D preview · Bambu Lab A1
See it in 3D. Then print your own.
This is the real, printable BMO — straight from the build files. Switch between the assembled body and the two A1 print plates, then orbit with the Front / Back / Left / Right / Top / Bottom buttons. Everything is sized for a Bambu Lab A1 (256 × 256 mm bed).
- ExploreSpin the model above. Tap a part to learn its job. Pick the Outside or Inside kit to see how it all packs in.
- DownloadGrab the A1-ready plates below (.3mf), already laid out flat with zero overlap. STL is in the repo if you prefer.
- Print on your A1Open the .3mf in Bambu Studio, pick PLA, slice, and send to your Bambu Lab A1. ~2 plates, no supports for the body.
Components needed
The build checklist.
ESP32-C3
Runs timing, Wi-Fi, mood state, button handling, and the route to the brain service.
- Need
- ESP32-C3 Super Mini board
- Wire / route
- Owns TFT SPI, I2S audio, touch input, Wi-Fi, and firmware secrets.
- Note
- Target PlatformIO env: esp32c3_supermini.
ST7735 TFT display
Shows eyes, mouth shapes, glances, listening, thinking, talking, bashful, and the wider mood set.
- Need
- 1.8 inch 160x128 RGB565 ST7735 display
- Wire / route
- CS GP7, RESET GP10, DC GP3, MOSI GP6, SCK GP4; VCC and LED to 3V3.
- Note
- The face is rendered in a small double-buffered framebuffer.
I2S microphone
Makes the listening state real and gives the firmware a voice-input path.
- Need
- INMP441 I2S microphone
- Wire / route
- Shares the ESP32-C3 I2S clock (GP0/GP1); data (SD) on GP5.
- Note
- SD must avoid strapping pins GP8/GP9, or capture reads as silence.
I2S amp + speaker
Plays compact voice output and lets talk animation follow the reply rhythm.
- Need
- MAX98357A I2S amp plus 8 ohm 1 W speaker
- Wire / route
- BCLK GP0, LRC GP1, DOUT GP2; speaker connects to amp output pads.
- Note
- Short clips are instant; streamed PCM16 replies come from the brain route.
Touch sensor
Turns tap, hold, and long-hold into distinct personality inputs.
- Need
- TTP223 capacitive touch sensor
- Wire / route
- Touch output to GP20, plus VCC and GND.
- Note
- The top touch pad is the main physical interaction.
Memory service
Stores preferences, recent moments, and context for more coherent behavior.
- Need
- Next.js brain routes with Supabase-backed memory
- Wire / route
- ESP32 posts to the deployed origin with its fingerprint header.
- Note
- Inspired by Garry Tan GBrain-style recall and enrichment.
Power and wiring
Keeps the tiny cast stable enough that animation and audio do not brown out.
- Need
- USB-C data cable, 3V3/GND bundles, jumpers, and a reliable 1 A supply
- Wire / route
- Display, mic, touch, amp, and board share planned power and ground rails.
- Note
- Build one subsystem at a time so wiring bugs are easy to isolate.
Core brain
Small enough for the desk, thoughtful enough to remember.
The project keeps the public magic playful while the memory idea nods to Garry Tan's GBrain: moments become preferences, synthesis, and useful gaps.
Persistent memory
Every exchange is embedded and written down, then recalled by meaning before BMO answers — so follow-ups stay on topic and the brain grows the more BMO is used.
lib/brain.ts · match_brain_memoryReasoned recall
Beyond fetching memories, BMO can compose a single cited answer and honestly flag what it does not know yet.
lib/brain/synthesize.tsConnected memories
People, places, and topics become entities with typed links, so BMO can reach facts that plain similarity search misses.
lib/brain/graph.ts · entities.tsChild profile
Durable facts about the child (name, favorites, fears) are distilled from conversations and updated in place — learned, never hardcoded, newest value wins.
lib/brain/profile.tsImportance & tidy-up
Memories are scored for importance and near-duplicates are found, so what matters is kept and clutter is pruned.
lib/brain/salience.tsDream cycle
A scheduled offline pass consolidates, de-duplicates, and re-scores memory so recall quality improves over time with no human in the loop.
lib/brain/consolidate.ts · /api/brain/dreamTimeline
A temporal view of memory — what happened, in what order, and how a topic evolved across time.
lib/brain/timeline.tsHybrid search
Vector similarity and keyword search are fused with reciprocal-rank fusion for results that beat either signal alone.
lib/brain/search.tsBrain health
Built-in checks (table reachable, memories present, embeddings present, recall working) roll up into a single health score.
lib/brain/doctor.tsRandom thoughts
When played with, BMO thinks out loud on its own: it recalls what it knows, muses one short line in its own voice, speaks it, and remembers the thought — a self-feeding inner life. gbrain has no named skill for this; it is their dream-cycle idea made spontaneous.
lib/thoughts.ts · /api/brain/idle-thought