Case Studies

SafeChoice: A Food Journal You Talk To, Rendered With Six Custom Shaders

Aariz RasheedCo-Founder & Chief Product Officer4 min read

The short answer

SafeChoice treats each day as a conversation thread with an agent that can read your history, look food up and log what you tell it. The interesting engineering is in two places: an agent runtime that carries real tools rather than a chat window bolted onto a database, and six custom GPU shaders that let a nutrition app look like a magazine instead of a spreadsheet.

The short version

Each day is a thread. You talk to it. The agent on the other side can read your history, look a food up, and log what you tell it, so the conversation is the interface rather than a support channel attached to one.

Scan a product label and the analysis lands in that thread as a plate: studio-lit, scored and narrated. Pinch out and the whole journal blooms into a magazine catalogue of everything you have eaten.

An agent runtime, not a chat box

There is a meaningful difference between an app with a chat screen and an app built around an agent, and it shows up in the architecture.

A chat screen sends text somewhere and prints the reply. An agent runtime gives the model tools it can actually call: read the journal, search the nutrition table, write an entry, generate the plate image. The conversation changes the state of the app rather than describing it.

LayerResponsibility
designTokens, type, motion, glass and the shader library
dataJournal store, nutrition table, agent runtime, plate imagery
featuresCanvas, catalogue, detail, scan, menu and shared widgets
screensOnboarding funnel, personalise, paywall, splash, guides
backendSupabase, billing, live activity, come-back bar

Four edge functions sit behind it for product analysis, chat, the agent and food imagery. Notably, the app is complete and usable before two of them are deployed, which is a deliberate property rather than an accident of sequencing.

The hard parts

Six shaders, because the category looks the same everywhere

Every nutrition app looks like every other nutrition app: a ring, a bar chart, a list of grams. That similarity is a commercial problem, not an aesthetic one, because a product that looks generic gets compared on price.

SafeChoice ships six custom fragment shaders compiled to the platform graphics layer, and a design system built around glass, motion and type. The result is a nutrition app that reads as a designed object.

Shaders are a real cost. They have to run at frame rate on old devices, they are hard to debug, and they are easy to get subtly wrong in ways that only appear on one GPU family. They were worth it here because differentiation was the point.

Testing an interface you cannot click

A pinch-to-zoom canvas with long-press actions and paging between days is not something a unit test can meaningfully cover. The suite boots the real canvas on a real simulator and walks the flows a finger would: opening a day, logging plates, the detail breakdown, the catalogue, paging, long-press, sending a message.

There is also a development affordance that saves a great deal of time. Any screen can be launched directly, and the entitlement gate can be bypassed while developing, so nobody has to click through an onboarding funnel to check a paywall change.

Two altitudes, one gesture

The zoom between the daily thread and the full catalogue is the core interaction, and it is the kind of thing that either feels magical or feels broken. There is very little middle ground, which means it absorbs a disproportionate share of the polish budget.

What we would tell you if you were building this

  1. If your category all looks the same, visual differentiation is a business decision and deserves a real budget. Shaders and motion are expensive, and being indistinguishable is more expensive.
  2. Decide early whether your agent has tools. A chat window is a week. An agent runtime that can read and write your app state is a different project, and retrofitting it is worse than starting with it.
  3. Make every screen directly launchable in development. Onboarding funnels are long, and a team that has to click through one to test the paywall will test the paywall less.
  4. Design so the app is complete before every backend function is deployed. It de-risks launch and it makes your degraded state a designed experience rather than an error screen.

Frequently asked questions

What makes an agent different from a chatbot in an app?
Tools. A chatbot sends text and prints a reply. An agent can call functions that change the app: read the journal, search the nutrition table, write an entry, generate an image. The conversation alters state rather than commenting on it, and that difference runs through the whole architecture.
Why use custom shaders in a mobile app?
Differentiation. Nutrition apps converge on the same rings and bar charts, and a product that looks generic ends up competing on price. Six custom fragment shaders and a glass and motion design system make the app read as a designed object. The cost is real: shaders must hold frame rate on older hardware and are awkward to debug.
How do you test a gesture-driven interface?
On a real simulator, driving the real canvas. The suite opens a day, logs plates, walks the detail breakdown and catalogue, pages between days, exercises long-press actions and sends a message. Unit tests cannot meaningfully cover a pinch-to-zoom canvas.
Can the app work before the backend is fully deployed?
Yes. It is complete and usable before two of the four edge functions are live. That was a deliberate design property, because it de-risks launch and turns the degraded state into something designed rather than an error.
How long does a build like this take?
Longer than a standard scanning app, because the shader work, the agent runtime and the zoom interaction are each substantial on their own. A conventional label scanner with a list view is a much shorter build, and a much easier product to copy.

Talking about ai products & ai agents?

Production AI features rather than demonstrations — model integration, retrieval over your own data, and agents that complete real tasks.