Lab

I built 9 apps.

Ate day view: calories eaten against a 1,400 kcal target, with meals listed below Ate weekly stats: average calories per day and a bar chart against target Ate monthly stats: calories across the month against target

Ate

A calorie tracker built around the week, not the day. It budgets in room for social days, projects where the week will land as you log, and eases the days left if you run over, with protein held fixed. Nutrition comes from verified databases: USDA FoodData Central and Singapore's Health Promotion Board, hawker dishes included.

Billette invoice with line items, discount and due date, ready to print to PDF

Billette

A quoting and invoicing tool for freelance work, replacing a spreadsheet that broke one too many times. Build a quote, convert it to an invoice, print to PDF. Everything lives in localStorage: no backend, no build step.

Cart shared grocery list grouped by aisle, showing who added each item and its price per 100g Cart item page for rolled oats comparing brands by price per 100g, with sale badges

Cart

A collaborative grocery app for two. Shared lists plus a price book that normalises everything to $/100g — so sale prices and different pack sizes are comparable at a glance. Built with Expo so it runs natively on both our phones.

Cookbook sourdough bread recipe page with ingredient scaling buttons, method and nutrition facts

Cookbook

A recipe site with ingredient scaling, nutrition tracking, and a cooking mode that keeps the screen on. Recipe apps have too many ads and none of them scale fractions correctly. Recipes are markdown files; Keystatic is the editor.

GymCat home screen with gym and equipment totals and a list of gyms GymCat equipment list for the Suntec City gym, grouped into cardio, cables and calisthenics GymCat copy-equipment dialog with duration and focus options and a generated workout prompt

GymCat

A reference catalogue of equipment across Anytime Fitness gym locations in Singapore. Built from personal frustration with arriving at a gym that didn't have the rack I needed. Equipment is hand-curated as JSON and grouped by function.

Ledger dashboard in demo mode showing monthly expenses by category

Ledger

A personal bookkeeping app that replaces spreadsheets. Imports bank statements, parses them into a SQLite ledger, and produces categorised reports. Locally it's a Python server against real data; the live site serves fictional demo data.

Move dashboard with total visits, visits per month and an activity heatmap

Move

A personal activity analytics dashboard built on my gym visit data. This parses and visualises the data: monthly trends, time-of-day heatmaps, membership pauses.

Trex trip screen showing remaining budget, cash on hand in yen, daily allowance and spend by category Trex cash ledger showing yen on hand and the history of cash movements

Trex

A phone-first travel spending tracker that works offline. It answers “how much can I spend per day for the rest of the trip?” and treats the cash in my pocket as a number I can reconcile against the notes in my wallet. Cash and card, multiple currencies.

WODGEN session picker with gym, duration and injury toggles above a generated Lower A workout WODGEN generated workout listing warm up and main lifts with sets and reps

WODGEN

A deterministic workout generator built for one athlete: me. Workout apps optimise for everyone, which means they work for no one in particular. This one encodes a specific rotation, equipment access, and fatigue logic. Fully client-side, no API.

How I build

Anyone can ship an app right now. Not everyone governs what they've built.

Single source of truth, enforced

Schema owns the data model, design tokens own the visual layer, docs own the workflow. Anything duplicated drifts.

Audit for drift, not just bugs

Stale references and obsolete docs mislead more than missing ones. Gaps compound quietly.

Refactor before fragility sets in

Remove structural weaknesses before they become load-bearing. Good systems get easier to change over time, not harder.

Fix at the right layer

Data shape issues belong in the schema, not patched in the UI. Style decisions belong in the design system, not inline.

Scope is an architectural decision

A precise MVP with an explicit deferred list prevents accumulation. What you leave out matters as much as what you ship.