Projects

GrabPic
Personal
After weddings and events, I kept doing the same thing: searching the album for one person's face, sending them their photos, and then starting over for the next person. GrabPic replaces that with a single link. The host uploads everything to one album, and each guest takes a selfie to get only the photos they appear in.
Here's how it works behind that link:
- It runs as three services that deploy on their own: a Next.js frontend, a Spring Boot API, and a Python worker that uses DeepFace to turn every face into an embedding. New photos reach the worker through an Amazon SQS queue, so uploads never wait on face processing.
- A selfie search is a nearest-neighbor lookup in pgvector over an HNSW index, which brings back matches from albums of 500+ photos in under 200 milliseconds.
- Photos upload straight from the browser to S3 through presigned URLs. Redis rate limiting and Cloudflare Turnstile keep bots out, and protected photos only show up for people whose face is in them.
- I later moved the servers off AWS App Runner and EC2 onto self-hosted containers behind a Cloudflare Tunnel, which cut hosting from about $85 a month to about $1.

PaperPulse
Personal
Keeping up with research means checking arXiv, PubMed, and a few other databases every day and sorting through a lot of papers that don't matter to you. PaperPulse does that search overnight, ranks everything against your interests, and has the 25 most relevant papers waiting in your feed the next morning. You can also ask it questions and get answers pulled from the papers themselves, with citations.
Here's what happens each night and when you ask a question:
- The nightly pipeline pulls from arXiv, Semantic Scholar, PubMed, and OpenAlex, extracts the text from each PDF, and embeds it for search, then Cohere reranks the results for each user.
- Answers come from a three-stage hybrid retrieval pipeline over pgvector, plus context from a Neo4j knowledge graph that links papers to their authors, concepts, and citations.
- For literature reviews, an AI agent explores that graph on its own, following citations and shared concepts to find themes and gaps before it writes the review.
- Built with FastAPI and Next.js, with the backend deployed as a Docker image on AWS App Runner.

Queue Up
Personal
Queue Up is for meeting people through music. It reads your Spotify history, from top artists to saved songs, shows you the people whose taste overlaps with yours the most, and lets you start chatting once you both swipe right.
The matching and chat are built like this:
- Matches are ranked with a weighted score, where a shared favorite artist counts for more than a shared saved song, and each profile shows exactly what you have in common.
- Chat runs over WebSockets with typing indicators, online status, and live match notifications, and file attachments go straight to S3 through presigned URLs.
- The React frontend is compiled into the Spring Boot app, so everything ships as a single Docker image backed by PostgreSQL, with JWT auth in HTTP-only cookies.

CoSign
Personal
It's easy to ignore a to-do list when nobody's checking. In CoSign, someone you pick has to approve your proof before a task counts as done, and if the deadline passes first, they get emailed a penalty you wrote ahead of time and would rather keep private.
I built it to be hard to cheat:
- Each task moves through a state machine from waiting on proof to approved or missed, and deadlines are enforced automatically.
- Penalties are encrypted with AES and stay hidden until a deadline is missed, and each one is hashed so it can't be reused after it's exposed.
- Built with Spring Boot, React, and TypeScript, with live WebSocket updates and recurring tasks, all shipped as one Docker container.
BitTorrent Client JS
Personal
A BitTorrent client I wrote in Node.js. Give it a .torrent file or a magnet link and it finds peers, connects to them directly, and downloads the file piece by piece.
I built each part of the protocol from scratch:
- It includes its own bencode encoder and decoder, finds peers through HTTP trackers, and speaks the BitTorrent wire protocol over TCP.
- Every piece is checked against its SHA-1 hash before the file is put back together.
- For magnet links, which don't include the file's metadata, it uses the extension protocol to get that metadata from peers first.

Albatross
Hackathon
Two of my teammates had felt unsafe walking through parts of Boston, so at HackHarvard 2024 our team of four built Albatross in 36 hours. It finds a fast walking route that stays out of crime hot zones.
We split the system into a few pieces:
- We loaded 50K+ crime records into Databricks and used MLflow to cluster them into hot zones.
- Cloudflare Workers check each route against those zones, and I built the Vue.js frontend that shows the zones and the safer route on the map.

SweetFriend
Hackathon
One of my teammates at PennApps 2024 lives with type 1 diabetes, and carb counting and insulin dosing were a daily guessing game for them. In 36 hours we built SweetFriend, which shows live readings from a Dexcom glucose monitor and estimates the carbs in a meal from a photo.
Here's how it came together:
- I built the React frontend, including a Chart.js glucose chart where meals and workouts show up on the timeline.
- The Flask and MongoDB backend sends meal photos to a GPT-4o vision model for carb estimates and texts you through Twilio when your glucose gets dangerous.