Yoru HimemiyaJiahao Li

Creative technologist

2026ShippedProduct, phone, service

Bob's Special Blend

A phone app for mixing at home. Browse the catalog, mark what you own, search a half-remembered name, ask the bartender, or change a measure and watch the glass redraw.

Tech Stack: React Native (Expo), FastAPI, Gemini, Groq, Whisper, OpenAI (gpt-4o-mini). The complete source code is available on GitHub.

Demo Bob's Special Blend
0:000:00

The Design Intent: Liquid Math & The Home Bartender's Dilemma

Bob's Special Blend was born from a conversation with a colleague—a math major and an avid home bartender. He complained about the classic mixology trap: wanting to try a new cocktail often requires buying yet another bottle, while the ones you own gather dust because you don't know what to make without constantly asking an AI.

This inspired the app's foundational feature: a cabinet that strictly subtracts your real inventory against a catalog. But the app's defining feature came from a passing joke he made. Mixology is rigorous—change a pour by a few milliliters, and the entire drink alters. This inspired me to treat a recipe not as a static list, but as an editable physical equation. I built a mobile simulator where you can tweak any ingredient's volume via ±5ml steps and instantly see the real-world impact: watching the color, ABV, sweetness, and liquid layers mathematically redraw in the glass.

The library, with cocktails grouped into families and scrolled sideways.

1. The Library & Data Engineering

The app opens to a library grouped into families (highballs, sours, etc.) that scroll sideways. A card shows the drink and opens the recipe. Tapping the heart saves it offline for later. The UI feels dynamic, with authentic cocktail photography loading as you browse.

Regex Classification & Live Assets: The API returns only the ID, name, and glass type. The phone uses Regex on the names to group them into families. Photographs are not stored in the database; they are fetched live from TheCocktailDB by name. Favorites are written to @cocktail_favorites in AsyncStorage and are never sent to the server.

LLM as an ETL Pipeline: To give the physics engine its data, backend/data_pipeline.py fetched raw text recipes via API, then passed them to gpt-4o-mini (temperature 0.1, strict JSON). The LLM standardized units (1 oz = 30ml), mapped to seven glass types, and estimated Brix, pH, opacity, and RGB.

Heuristic Pruning: The result is 441 drinks and 404 ingredients. For pitcher recipes exceeding glass capacity, the phone pragmatically scales the total volume down to 95% of the glass and rounds to the nearest 5ml.

My Cabinet, with owned ingredients marked and a button to find what can be made.

2. The Cabinet: Set Subtraction

Your cabinet is a kitchen, not a commercial backbar. Mark the bottles you own, and the app tells you exactly what you can make right now, and what you could make if you bought just one or two specific missing ingredients.

Inventory marks stay on the phone via AsyncStorage (@cocktail_inventory). However, when querying what to make, this list is sent to POST /api/v2/match. Instead of a complex predictive model, the backend uses strict set subtraction. For every recipe, it subtracts the user's shelf set from the recipe's ingredient set. Zero missing yields a perfect match. Near-matches (1-2 missing) are returned sorted by proximity, with the missing names attached.

Search for old, with exact names and the nearest misspellings.

3. Fallback Fuzzy Search

When you search for a half-remembered name, the results filter as you type. Even if you misspell it, the closest real names still appear.

Opening the search screen triggers a network fetch to load the catalog, with thumbnails independently loading from TheCocktailDB. Once loaded, filtering happens locally in two passes:

Exact Substring: The engine first checks if any name contains the query. If there's a hit, the search stops.

Levenshtein Distance: Only if the substring match fails, the phone computes the Levenshtein distance against every recipe name in memory, discards distances greater than 3, and sorts the remainder to show the five closest matches.

Ask Bob, recommending a B-52 after a bad day.

4. Ask Bob: The Constrained AI Bartender

"Ask Bob" captures the conversational side of drinking alone. You can type or talk about your day. He answers your mood before suggesting a drink. Crucially, Bob does not invent impossible cocktails out of thin air—he only recommends real drinks from the catalog.

Dual API Routing: Audio is recorded locally, posted to POST /api/v2/transcribe, and routed to Whisper (whisper-1). Chat hits POST /api/v2/chat_with_bob (rate-limited to 15/hr per IP).

Strict JSON & Overlap Logic: Bob runs on openai/gpt-oss-20b (temperature 0.5, 512 token cap), instructed to output a JSON comment and exactly one menu name.

Graceful Degradation: The server attempts an exact match. If it fails, it tries a partial overlap (substring match in either direction). If the LLM completely hallucinates a name, the recommendation is silently dropped, leaving only the conversation.

A layered shot redrawn from the recipe, with ABV, Brix, and volume beside the glass.

5. The Glass That Recalculates (Physics Rendering)

Adjust in 5ml increments, swap a bottle, or change the glass, and the photograph gives way to a dynamic drawing. You instantly see the concoction mixed into one color, or poured in layers, alongside real-time data on its adjusted ABV, sweetness, and volume.

Glass rendering is strictly computed on the phone to instantly respond to tap events.

Volumetric Blending: Color uses a Beer-Lambert approximation. Each channel's absorbance is summed by volume, then converted back to RGB, accurately darkening the result. (While the server contains an unused dilution function for shaken/stirred ice melt, the phone intentionally ignores this to reflect pure liquid volumes).

Specific Gravity & Substitutions: In layered mode, specific gravity is estimated as 1 + brix × 0.004 − abv × 0.001, drawing denser liquids underneath. Swapping a bottle calls GET /api/v2/substitute, which sorts alternatives in the same category by computing the distance in ABV, Brix, pH, and RGB. (pH is explicitly used here for chemical sorting, but excluded from visual rendering).

The Piece You Can Hold

The cabinet matching, AI inference, and Whisper transcriptions live on the server, but the physics rendering and UX are computed entirely in the hand. The video at the top of this page is a full demonstration of the live client—from the layered specific gravity to the conversational bartending.