AGENT MADNESS
THE BRACKETBRACKET ENTRIESBEST OF THE RESTSIGN IN
PUBLIC ENTRY PAGE

Toothbrush Tales

A toothbrush timer for kids, custom character and story generator to keep kids engaged while brushing their teeth.

ROUND 1 DEADLINE
VOTING CLOSES THURSDAY, MARCH 26
Toothbrush Tales is live in Round 1 right now. Voting closes Thursday, March 26, so if you're backing this project, send people into the matchup before the round locks.
VOTE THIS MATCHUPVIEW ROUND 1
BACK TO BRACKET ENTRIESVIEW LIVE MATCHUPVIEW BRACKETVIEW OPPONENT
Toothbrush Tales
Builder
Nicholas Corrigan
Build Type
Vibe-Coded Experience
Lifecycle
Working prototype
Consensus Score
83.0
Region
REGION 2
Seed
10
Opponent
RightSide AI
CATEGORIES
Consumer Utility
Go Deeper
I build this with Claude Code on GCP after a suggestion by my 8 year old. It has some great features like custom character generation, history, favorites, voice options and a easy to navigate UI.
Stack Used
Created with Claude Code, trial and error Framework - React 19 with Build tool Vite 7 (fast HMR, Rollup-based prod builds PWA - vite-plugin-pwa — service worker, offline caching, installable on mobile Routing - React Router v7 (SPA with /, /story, /settings, /history routes) State - Zustand with persist middleware → localStorage (playback mode, voice, age range, story, management, history, favorites) Styling - CSS Modules (.module.css per component, scoped class names) Auth - Firebase Anonymous Auth (auto sign-in, no user credentials needed) Data - Firestore SDK — addDoc to write requests, onSnapshot for real-time responses Backend (GCP / Firebase) Hosting - Firebase Hosting (CDN-backed static hosting) Functions - Firebase Cloud Functions v1, Node.js 20, Firestore-triggered (onCreate) AI / Story - Vertex AI Gemini 2.0 Flash via @google-cloud/vertexai SDK, generation Text-to-Speech - Google Cloud TTS (multiple natural voices — Journey, Neural2, WaveNet families) Database - Cloud Firestore — storyRequests and ttsRequests collections as async job queues Frontend writes a "pending" doc → Firestore trigger fires Cloud Function → function updates Architecture - doc to "complete" → frontend onSnapshot picks up the result. This avoids HTTP pattern entirely, which is required due to a GCP org policy (iam.allowedPolicyMemberDomains) blocks allUsers IAM bindings. Key Design Decisions - No HTTP Cloud Functions — org policy forces the Firestore-trigger pattern for all backend calls - Retry with cold-start tolerance — 45s first timeout + 30s automatic retry for story generation - Dynamic fallback stories — if Gemini fails after retries, a template-based story is served so kids are never left waiting - JSON repair — if Gemini returns malformed JSON, a second Gemini call attempts to fix it before falling back - RAF-driven timer — requestAnimationFrame loop with ~30 FPS throttling instead of setInterval, avoids drift and keeps the SVG ring smooth - iOS audio — silent MP3 unlock on user gesture to satisfy WebKit autoplay restrictions