Side Projects & Experiments

Building tools that solve real problems, and some just for fun · see full portfolio

🎵

Music Trivia Agent

AI AgentMay 2026

The Problem

I wanted to build a real autonomous agent, one where I genuinely don't know how many steps it'll take when I hit "go." The music domain felt right because trivia quality is subjective and rewards persistence.

The Solution

Give it a YouTube link and it hunts for the most surprising fact about the song. Runs a custom observe-think-act loop on Cloudflare Workers + Durable Objects: Plan, Search (DuckDuckGo), Extract, Evaluate (1-10 surprise score via Claude Haiku 4.5), Decide (continue or synthesize). Budget-controlled at 8 iterations max and ~$0.007/session.

The Tradeoff

Durable Objects over KV because agents need session state across requests. Single model (Haiku) everywhere instead of a cascade, routing complexity wasn't worth it. A solid 7 in hand beats chasing a 10 that might not exist, so the cost ceiling shapes when the agent stops.

Music Trivia Agent interface
Cloudflare WorkersDurable ObjectsClaude HaikuDuckDuckGoTypeScript
🔎

Case Closed

AI GameApr 2026

The Problem

I wanted to explore how LLMs handle role-playing with hidden state, conditional behavior, and adversarial constraints, but needed a fun, contained sandbox to test it in.

The Solution

An AI-powered whodunit detective game with 6 pre-built scenarios and 1 fully AI-generated mystery. Players interrogate suspects, investigate locations, and make an accusation. Uses a dual-AI strategy: Cloudflare Workers AI (Llama 70B) for mystery generation and Groq (Llama 8B) for high-volume interrogations, with templated fallbacks for cost optimization.

The Tradeoff

70B generates the mystery once, 8B serves conversations cheaply. Templated 5 of 6 scenarios to cut costs ~80%, same logic as caching: if the answer doesn't change, don't recompute it. Serverless couldn't hold state between rounds, which led me to Durable Objects for the next project.

Case Closed detective game interface
JavaScriptCloudflare PagesWorkers AIGroqKV Storage
🦯

Daily Sea Lion

AI GalleryApr 2026

The Problem

I wanted a birthday gift that would keep giving, something that runs on its own every day with zero maintenance and no hosted backend to babysit.

The Solution

A fully automated AI gallery that generates a new sea lion image and poetic caption every morning at 8am PDT. A GitHub Actions cron job triggers image generation via Pollinations API and captioning via Groq (Llama 3.3 70B), commits the result, and Cloudflare Pages auto-deploys.

The Tradeoff

Zero runtime infrastructure. GitHub Actions is the scheduler, git is the database, nothing to monitor. No quality gate on output, hardcoded prompt constraints substitute for an eval loop I couldn't afford on free tier.

Daily Sea Lion gallery preview
ReactViteTailwind CSSGitHub ActionsGroqPollinations API
🎤

Murmur

Voice NotesMar 2026

The Problem

I wanted to dictate voice notes for my blog drafts and transcribe them later, but everything I found was a paid note-taking app. Obsidian eventually helped, but even that had its quirks. I just needed a simple recorder with transcription and nothing else.

The Solution

A minimal, single-page recorder that runs entirely in the browser. Record, play back, and transcribe voice notes with no backend, keeping everything local to your device. Includes a local LLM option for higher-accuracy transcription.

The Tradeoff

No framework, no build step, no CDN. The privacy contract is easier to verify when users can audit the entire source in one file. Web Speech API for free browser-native transcription, local LLM as a step up in accuracy without sending audio to a server.

Murmur voice notes interface preview
JavaScriptWeb AudioWeb Speech APIHTMLCSS
📚

Bento Reads

RSS ReaderFeb 2026

The Problem

I wanted a reading page I could actually share. Subscribe to feeds, organize them by topic, and have a public profile that looks like a newspaper front page, not a feed dump. Existing readers either don't do public profiles or are desktop-only.

The Solution

A monorepo (Turborepo + pnpm) with a Next.js 15 web app and React Native mobile app sharing a Supabase backend. The dashboard uses React Server Components for data fetching and a scored article layout (recency + media presence) to pick hero cards vs. compact headlines. Feeds are polled on a Vercel Cron schedule, and every table uses Row Level Security so public profiles are read-only by default. Five themes via CSS custom properties, OG image generation for snapshot sharing.

The Tradeoff

Monorepo with shared Supabase backend so web and mobile don't diverge. Row Level Security at the database layer means public profiles are secure by default without application-level auth checks. Scored layout algorithm over chronological because recency alone buries good content.

Bento Reads newspaper-style dashboard
Next.js 15React NativeSupabaseTailwind CSSTurborepoVercel
📄

Blogger Takeout to MDX

Content MigrationJan 2026

The Problem

When I moved my blog from Blogspot to Astro, I discovered my posts, images, and comments were trapped in a proprietary export format with no clean path to MDX. There's no migration tool that handles the whole thing.

The Solution

Converts Google Takeout Blogger exports into MDX files with YAML front matter and organized local media assets. Also generates WXR files for importing comments into Disqus, preserving community discussions during platform transitions.

The Tradeoff

Procedural pipeline, not a framework. The conversion is linear (discover, parse, transform, output) so a class hierarchy would be overhead for a tool you run once. Comments exported separately via WXR because Disqus has its own schema and not everyone needs comment migration.

Blog site built with migrated content
PythonMDXAstroWXRDisqus
📱

Office Days

Mobile DevelopmentAug 2025

The Problem

I needed a simple way to track my own office visits and get a reminder when I'm falling behind. Nothing I found did just that without being a full-blown HR tool.

The Solution

A mobile app with a calendar interface for one-tap office day logging. Provides monthly, quarterly, and yearly attendance statistics with configurable business quarters, all stored locally on-device for privacy.

The Tradeoff

Device calendar as the source of truth, not an app database. React Context over Redux because 4 screens don't need a state management library. UTC-first date handling throughout because timezone bugs in calendar apps are silent and deadly.

Office Days app screenshot
React NativeExpoTypeScriptJest
⚙️

CSV2Dataset

Data PlatformSep 2022

The Problem

I was exploring ways to locally visualize large CSV files from Data.gov and Kaggle, but managing dozens of them wastes disk space and makes discovery painful. There's no easy way to compress, catalog, and query them without spinning up a full database.

The Solution

A local web app that converts CSV files to Apache Avro format, achieving 60-90%+ compression while preserving queryability. Upload, preview, tag, and manage datasets through a clean interface with REST and GraphQL APIs.

The Tradeoff

Avro over Parquet for random access and row-level compression, trading columnar analytics flexibility for local storage efficiency. Spring Boot for the web layer, Spark for heavy processing, a dual-runtime that lets the web stay lightweight while file transforms scale.

CSV2Dataset import interface
JavaSpring BootApache AvroApache SparkThymeleafGraphQL
🏦

Mortgage Delinquency Analysis

Financial Analytics2021

The Problem

After Angry Consumer, I wanted another go at data. This time I picked CFPB's National Mortgage Database to see if I could make delinquency trends across U.S. regions actually interpretable through visualization.

The Solution

Analyzes residential mortgage delinquency trends (2008–2021) across states, metros, and counties. Tracks early-stage (30–89 day) and serious (90+ day) delinquencies through Jupyter notebooks and an interactive Streamlit web app.

The Tradeoff

Streamlit over Flask because the value is in the analysis, not the UI. Jupyter for exploration, Streamlit for sharing. Two interfaces to the same data pipeline through a shared module, so transformation logic lives in one place.

Average delinquency trends visualization
PythonPandasStreamlitJupyter
❤️

Simple Health Tracker

Health TechSep 2021

The Problem

Daily health logging (blood pressure, glucose, weight, meals) shouldn't require trusting a third-party service with sensitive data. Most health apps store your information on their servers.

The Solution

A privacy-first health logger that sends entries directly to your email via SMTP. No database, no server-side storage. Your health data lives in your inbox, under your control, ready for extraction whenever you need it.

The Tradeoff

Email as the database. Zero server-side storage eliminates compliance burden entirely, no HIPAA or GDPR data residency to worry about. Trading query capability for privacy: your inbox is searchable enough for personal health records.

Simple Health Tracker interface
Node.jsExpressNodemailerBootstrap
🏠

HouseHunt

Data AnalysisJul 2018

The Problem

I was house hunting in the East Bay and wanted objective data to back my decisions. Professional real estate APIs are expensive, and manually comparing Redfin listings is tedious and error-prone.

The Solution

Built during a real East Bay home search in 2018. Click any property in the data table to see comparable recently-sold homes and average pricing, turning raw Redfin CSV exports into actionable buying intelligence.

The Tradeoff

R/Shiny for reactive filtering without needing a frontend/backend split. Redfin CSV exports over paid APIs because the data is free and I needed it now, not after negotiating API access. Simple heuristic matching (beds, baths, sqft, age) over ML because explainability matters when you're making an offer.

HouseHunt data table interface
RShinyDTdata.table
📢

Angry Consumer

Data VisualizationDec 2016

The Problem

This was my first real attempt at data visualization using public datasets from Data.gov. The CFPB publishes millions of consumer finance complaints, but the raw data is overwhelming with no easy way to explore patterns or trends.

The Solution

An interactive web app with server-side filtering and Google Charts visualizations that make CFPB complaint data explorable. Browse by company, product type, and date range to surface patterns in consumer finance complaints.

The Tradeoff

Server-side filtering over client-side because the CFPB dataset is too large to send to the browser. Pre-import column selection to keep the database lean, only storing dimensions I planned to visualize. Google Charts over D3 because the chart types I needed were standard and didn't justify a custom implementation.

Angry Consumer data table
MongoDBExpressAngularJSNode.jsGoogle Charts
🎓

EduExplore

Web ApplicationOct 2015

The Problem

I was helping a relative with his M.S. in Data Science search in 2016, and there was no single place to compare programs across universities. Official databases were dry, and community knowledge was scattered across random forums.

The Solution

A crowd-sourced platform built on the Database of Accredited Postsecondary Institutions. Users can search, compare, and contribute program information, making college research collaborative instead of solitary.

The Tradeoff

MongoDB for flexible schema because program data varies wildly across universities, no two schools structure their requirements the same way. Reactive watch-based filtering so searches update as users adjust criteria, no submit button needed.

EduExplore homepage
MongoDBExpressAngularJSNode.jsBootstrap