A friendly pain in your assumptions.Curiosity, with a raised eyebrow.
An independent product, from concept to working application.Product · Interaction · Engineering
The starting point
Why another perspective?
Algorithms are good at giving us more of what we already like. That bothers me when it means hearing the same opinions again and again. I wanted a companion that would push back, with enough wit to make me want to hear him out.
I had the idea about a year before I built it. When the tools made it practical, I picked it up again. Mr. Fluff is what came out of that curiosity.
An ideaThe assumptionThe exceptionAnother angle
Make room for another perspective.
01 / The experience
A conversation with a raised eyebrow.
Hold the face, say what you think, then release to hear his reply. Push back if you disagree. The conversation works in English and Spanish.
Ready
Your turn.
Waiting for you. Opening the app does not start recording.
Listening
You have his attention.
Hold to speak; release to stop recording. The expression follows the microphone’s actual state.
Thinking
Considering the other side.
Recording has stopped. He is preparing a reply, and the face makes that pause visible.
Speaking
A point. With a punchline.
The mouth follows the audio you hear. Stop interrupts the reply; End closes the session. This illustration is silent.
Interaction illustration · no recording, audio or model connection.
Illustrative exchange
“Automation always saves time.”
It can—once the task repeats enough. Otherwise you’ve built a tiny factory to fold one napkin.
He needs a reason to disagree. If your argument holds up, another angle or a useful caveat is enough. Contradiction for its own sake gets boring quickly.
02 / Product judgement
The decisions that shaped it.
01
Make speaking a deliberate act.
Holding the face gives recording a clear start and finish, without another button competing for attention. It also makes touch handling critical: the face must stay under your finger, and cancellation must stop capture.
Hold → speak → release
02
Give the humour a job.
I wanted a reason with a comic payoff, not a stream of jokes. That means judging humour separately from accuracy and relevance. A funny answer that misses your point still fails.
“!”Reason + comic payoff
03
Keep the system small and controllable.
One backend handles access, conversations and spending. That keeps deployment manageable, but puts audio traffic through the server. Adding replicas would need more coordination; it is not just a hosting switch.
Access · capacity · budget
03 / Technical architecture
What sits behind the face.
React in the browser, one FastAPI backend, PostgreSQL for persistent records. Audio travels through the backend so access and spending checks happen before a paid model call.
The system at a glanceOne application. Clear responsibilities.
01
Visitor browser
React · TypeScript · Web Audio
Capture, playback and expression→
02
Cloudflare
HTTPS · Tunnel
Public entry to the application→
03
FastAPI application
HTTP + WebSocket · internal modules
Identity / conversation / spending↔
04
PostgreSQL
SQLAlchemy · transactions
Sessions, reservations and usage
Application ↔ provider
OpenAI
Voice generation and transcription
Application ↔ identity service
Google identity
Owner sign-in
Identity, conversation and spending are modules inside the FastAPI application. External services connect through that backend.
Control paid work at the server.
Provider credentials and spending limits stay on the backend. The browser sends application commands, not arbitrary provider requests.
Keep spending durable.
Reserve allowance in a database transaction before dispatch. Settle against provider receipts; keep missing usage evidence unresolved.
Let presentation evolve independently.
The SVG renderer receives state and audio level. Changing an expression cannot open the microphone or authorise a charge.
One voice turn
01
Capture
AudioWorklet → PCM chunks.
02
Admission
Release → validate and reserve.
03
Response
OpenAI → backend → browser.
04
Playback
Web Audio → sound and mouth.
05
Settlement
Usage receipts → PostgreSQL.
Audio streams while the reply is generated. Playback and usage settlement finish independently.
The stack and deployment choices
Audio and rendering
An AudioWorklet converts microphone input to 24 kHz mono PCM in 100 ms chunks. Web Audio schedules playback; the SVG mouth follows playback energy without a React render on every frame.
Transport and contracts
The React/TypeScript frontend, HTTP API and application WebSocket share one origin under /MrFluff. FastAPI and Pydantic validate commands. The backend owns the separate model connection; generated TypeScript types keep HTTP contracts aligned.
Persistence and deployment
SQLAlchemy and PostgreSQL hold sessions, reservations and usage; Alembic manages schema changes. Docker packages the compiled frontend and Python backend together. The demo runs on a laptop through Cloudflare Tunnel, so availability still depends on that machine and its connection.
04 / Working through the details
Where the build became interesting.
Challenge 01
A reply finished before its sound did.
What happened
Generation could finish while audio was still buffered in the browser.
What changed
I separated generation, playback and accounting, and tied the face to playback.
Why it mattered
He should stop moving his mouth when you stop hearing him.
Generation
✓
Playback
✓
Settlement
✓
Three clocks. Three meanings of finished.Conceptual illustration
Challenge 02
A touch target moved beneath the finger.
What happened
Press scaling and nearby controls shifted the held face. Small screens also hid the entry action.
What changed
I fixed the hold geometry and shortened the welcome layout across languages and orientations.
Why it mattered
A passing click test means little if you cannot find or hold the control.
PressHoldRelease
A stable target, from press to release.Conceptual illustration
Challenge 03
The reasoning worked. The wit needed work.
What happened
Replies could be insightful yet miss the character’s comic payoff.
What changed
I revised the character guidance and assessed humour separately from truth, relevance and continuity.
Why it mattered
Correct answers were only half the brief. The revised character still needs human assessment.
Insight, relevance and wit need separate attention.Conceptual illustration
Challenge 04
More conversations changed the problem.
What happened
Eight-user stress scenarios exposed storage contention and timing failures.
What changed
I refined transaction admission and reran isolated scenarios with the packaged app.
Why it mattered
Limits need evidence. Those scenarios informed the fix; final capacity verification is still outstanding.
↓Check & reserve↓Bounded paid work
Admission before dispatch. Evidence before expansion.Conceptual illustration
05 / Where it stands
A working product. A clear next step.
Built
Bilingual voice conversations, an expressive face, owner controls and persistent spending records.
Evaluated
Logic, browser journeys and real database transactions have separate checks. Provider samples confirm compatibility, not conversational quality.
Next
Character assessment, real-device responsiveness, final capacity verification and independent recovery.
I came for the character. The audio timing, awkward touch behaviour and spending controls gave me plenty of reasons to stay curious. There is more to do, and I want to hear what he makes of your ideas.