Inspiration
We were tired of dating apps that feel like job interview: curated photos, calculated bios, and the same five conversation starters. We wanted something where the first impression is honest, a little messy, and actually fun. A bad drawing says more about someone than a filtered selfie ever could.
What it does
Draw Singles is a matching app where your profile picture is something you draw. You sketch on a canvas (or upload a photo of a doodle), answer three quick questions, and the app does the rest. A trained visual encoder reads your drawing and turns it into a 250-dimensional similarity vector. The app clusters you with people who drew similar things, visualizes the whole population as an interactive constellation graph, and serves you a swipe deck of doodles.
When two people like each other's drawings, they can chat.
The app also includes an animation lab where doodles come to life, and an image-to-SVG tracer that vectorizes photos of drawings into smooth cubic béziers.
How we built it
Backend: Flask with SQLite. The core matching is powered by a frozen PyTorch classifier (sketch_matcher + best.pt) that extracts visual embeddings from drawings.
Frontend: React 18 + TypeScript + Vite. We deliberately avoided component libraries and wrote vanilla CSS with hand-drawn design tokens — wobbly border radii, graph-paper dot backgrounds, and hand-written heading fonts. The swipe deck uses pointer events with physics-based transforms. Chat polls for new messages every 2.5 seconds.
ML architecture generates candidates from visual similarity.
Challenges we ran into
Not breaking the existing ML. Even though there were pretrained models such as doodlenet, those do not include sketches of a full person. So, Res-Net 152 was fine tuned using TU-Berlin human sketch dataset (Eitz, Hays & Alexa, 2012) obtaining sketch_matcher.py and best.pt, where we calculated the similarity of the sketches. On top of it sits a second model, social_projection.pt, which learns from real conversations. It buckets the 250 categories into 10 semantic groups and estimates, for every pair of groups, whether that combination converses well — using reciprocity, min(msgs_A, msgs_B), so a monologue doesn't count as a success.
Handling missing PyTorch gracefully. We wanted the app to run for demo and development without the 2 GB PyTorch install. We wrapped every torch import in lazy try/except blocks so the backend starts cleanly and simply skips social refinement until the user installs torch and trains the model.
Duplicate liked profiles. The swipe deck's keyboard listener re-attached on every render, so rapid arrow-key presses could fire fly('right') multiple times and add the same person to the liked array repeatedly. We fixed this with ref-based callback stabilization and an idempotent Set deduplication guard.
Accomplishments that we're proud of
Built a chat system that integrates naturally into the existing swipe flow.
Built and deployed a 225 MB PyTorch visual encoder (sketch_matcher + best.pt) that reads raw doodles and turns them into 250-D similarity vectors — the engine that drives groups, the constellation graph, and the swipe deck.
Created a hand-drawn UI that feels cohesive and on-brand, down to the wobbly CSS border radii.
What we learned
Triplet loss and online metric learning. We learned how to mine anchor-positive-negative triplets from a relational database and train a projection network that respects cosine similarity geometry.
How to fine tune ML systems. The big insight was that you do not always need to retrain a giant model. A simple fine tune can do the job.
Lazy dependency loading in Python. We learned patterns for making heavy ML dependencies optional so the app degrades gracefully instead of crashing at import time.
Pointer events for swipe physics. Building a Tinder-style deck with proper drag physics, tilt, and snap-back taught us a lot about React refs, requestAnimationFrame, and why you should not store transient pointer state in useState.
What's next for Draw Singles
Doodle chat. Let users draw inside chat threads, not just type. Each doodle becomes another vector for the social model to learn from.
Browser-side inference. Export the social projection to ONNX or TensorFlow.js so match ranking can happen client-side without server round-trips.
Better social signals. Incorporate reply latency, message sentiment, and whether a conversation led to an exchange of contact info.
Multi-modal profiles. Let users record a short voice note or write a free-text answer, and embed those alongside the drawing vector.
Log in or sign up for Devpost to join the conversation.