Saj Chandoo

Portfolio — Organizer turned data builder

Everything I build starts with the person, not the category.

Your voice, not a score. Your words, not “people like you.” Your identity, not a list.

Organizer turned data builder. By day I run the direct response data program at the Human Rights Campaign, across tens of millions of records. By night, two live apps and a third in the wings.

The philosophy

I started by knocking on doors. Grassroots organizing, canvass teams I trained, field operations I ran across multiple states through an election cycle. That work taught me something most data people do not learn from a textbook: data only matters if it reaches the person who needs it, in a form they can actually use.

Every pull I run, every suppression list I build, every audience I cut comes back to whether a real person gets the right message at the right moment. Each role before HRC moved me closer to the systems layer, and each one reinforced the same lesson. The people closest to the problem should be the ones shaping the tools.

That is the line from organizing to data to building. Figure out what people actually need, then make the thing.

The work

Three apps, one habit: each one refuses its category's default. CivicRadar refuses to store who you are. Euphora refuses to score what can't honestly be measured. The third, a still-unnamed taste portrait in development, refuses to average you into people like you. Every case study ends with that choice and what it cost.

CivicRadar

Live

CivicRadar takes every identity and issue you actually hold, queer, immigrant, on Medicaid, whatever the combination, and gives you one feed of the bills that touch any of them, each with the relevant advocacy orgs' positions attached. No following a separate org for every part of your life.

27
identity tags
162
org files
46
states covered
17
jurisdictions live

Nobody is just one issue. Staying informed the honest way means following a separate org for each part of your life and piecing together what applies to you yourself. Almost nobody keeps that up. CivicRadar scores every bill against your full set of identity tags at once and attaches whichever advocacy orgs are relevant to whichever tags a bill touches.

Architecture

Bills come from sources that do not speak the same language: OpenStates for live state search, LegiScan for bulk state ingestion, Congress.gov for federal. Matching is a deterministic keyword scorer, not a model call, weighting 27 identity tags above 26 issue tags across roughly nine additive signals. AI enters at one point only, drafting the email and call script on Claude Haiku. Advocacy positions are 162 reviewed org files across five networks, covering 46 states, joined so every org's stance on a bill shows on one card. It also ships push and email bill-progression alerts, full legislator profile pages, a private watchlist, tap-to-call scripts, and a ZIP-based district view.

Nothing is stored server-side. ZIP, identity tags, and the sentence a user writes live in localStorage only, and the search cache carries no user association even in principle. For a queer immigrant on Medicaid, the risk is not that the tool fails to help, it is that it becomes a list somewhere with your name next to "trans" or "undocumented." The privacy model is a constraint on the architecture, not a policy on top of it. There is no server-side table to subpoena because it does not exist.

Open source

AGPL v3. The code is public because the license only has teeth if the code is visible. The org-positions corpus is the real moat, and it stays separate.

Next.js 16, React 19, TypeScript. Tailwind v4. Upstash Redis, Cloudflare R2, Upstash Vector. Claude Haiku (drafting), Gemini Flash (summaries), Voyage embeddings. OpenStates, LegiScan, Congress.gov, Legistar. Vercel.

Euphora

Live

Euphora is a speech and voice practice app built around one refusal: it will not score your fluency with AI.

5
lanes
6
courses
42
lessons
0
fluency scores

Every consumer AI speech coach scores something, and none publishes validation for what the score measures. For someone who stutters, a fluency score reinforces exactly the goal evidence-based therapy moves people away from, and the detection fails anyway: speech recognition normalizes disfluencies out, and a silent block produces no audio to detect.

Approach

Feedback is tiered by whether the measurement is valid, not by what demos well. Pitch is measurable, so on-device pitch and resonance tracking is the one automated feedback surface, built for the voice masculinization and feminization lanes. Rate metrics never touch transcription and are switched off for the stuttering lane. Every technique carries an evidence tier, A through D, and no coached technique is rated A. Five lanes, six courses, 42 lessons, eleven guided exercises, and a public /research page carrying the full bibliography.

The refusal is enforced in the codebase, not a policy page. A unit test fails if the self-rating rubric ever mentions stuttering, blocks, or disfluency. Gated techniques route through an explanatory click-through. The lane rules are tested functions. No backend exists at all: recordings live in IndexedDB or the device filesystem, nothing leaves except user-initiated exports through the OS share sheet, including a clinician-handoff file, because the app supplements therapy rather than replacing it.

Why I built it

I stutter, and canvassing taught me to keep talking through the fear. Structure did the rest, so I built the structure into an app. I still stutter. That is the point.

TypeScript, React, Vite SPA, installable PWA. Capacitor for iOS and Android. On-device DSP (pitchy, custom FFT/LPC). Dexie/IndexedDB and Capacitor Filesystem. Cloudflare Workers. No backend, no accounts, no analytics.

Live at euphora.app. Native builds packaged, not yet store-submitted.

Untitled taste portrait

In development and unnamed

A taste portrait built from your own ratings and your own words, not a watch log and not a collaborative-filtering black box. Movies, TV, games, and books in one place.

587+
tests

Loggers record what you watched with no room for why. Collaborative filtering recommends by proxy and explains nothing, throwing away the specific thing you actually think about a title. It keeps your own language, one texture word and one 280-character verdict per rating, and makes both load-bearing.

Architecture

Postgres on Neon behind an API-first boundary. The fingerprint weights your free-text flavor tags by frequency and rating and renders as an SVG word cloud, no chart library. Recommendations are two-stage: a pure scorer maps your tags to TMDB, IGDB, and Open Library candidates, then Gemini curates from that pool only, grounded in your own verdicts, with a hallucination guard. Claude powers exactly one surface, the extended-thinking deep portrait.

Your own words over other people's behavior. A smaller signal than a million watch histories, but yours, and the system keeps it legible instead of averaging it away. Deliberately the structural opposite of CivicRadar on privacy: server-side with accounts because taste is meant to be shared, where CivicRadar is localStorage-only because its users' risk is ending up on a list.

Next.js 16, React 19, TypeScript. Tailwind, Fraunces display type. PostgreSQL (Neon, Drizzle). TanStack Query. Auth.js. Gemini Flash (curation), Claude Sonnet (deep portrait). TMDB, IGDB, Open Library. Vercel.

Fully built and tested, not yet deployed.

Also building

A row under the three apps for real builds that are not full webapps.

Genealogy analyzerPersonal tool, on GitHub

A Python engine that reconciles DNA match data from four incompatible clustering tools into one paternal-only report, with a five-method convergence score that separates a real surname lead from a coincidence. It also does narrative work no other project can: it is the "start with the person, not the category" thesis applied to reconstructing my own family, and it predates every shipped app, so it reads as the honest origin of the whole approach.

GitHub →
Master ScribePrivate build, shown not linked

A pipeline that turns voice and meeting transcripts into structured output across four destinations, built with the rigor of a system handling someone else's data. It carries the site's second-strongest governance story: a quarantine gate that refuses the most sensitive content on every unattended path (human-present processing only), and a deterministic clinical-safety check the model is never asked to perform, now tested in two independent implementations. The repo is permanently private, since the prompt engineering is inseparable from real names; it shows here the way the life dashboard does, architecture and pattern with no link.

The day job

Data Manager, Human Rights Campaign

Every direct response pull at the largest LGBTQ+ advocacy organization in the country runs through me: direct mail, digital and SMS, telemarketing, and events. That means audience selection, suppression logic, and quality control across tens of millions of records spanning a CRM, a data warehouse, and the voter file. The stakes are real (a bad pull can waste tens of thousands of dollars or put the wrong message in front of the wrong person), and last year I carried about 45 percent of the team's request volume at a faster cycle time than the team average.

That scale is the same thesis from the other side. Segmentation and suppression are how tens of millions of records still resolve to one person getting the right message at the right moment, or no message at all when that is the respectful call. The craft example I would point to: our major-donor prospecting pulls treated one bad channel, like a dead mailing address, as a reason to drop a donor from every channel. I redesigned the suppression to run per channel, so a donor with a bad address but a working email stays reachable by email. The category ruling was erasing the person, and the per-channel fix is now the standard.

The role keeps widening from executing pulls to owning analysis and standards: a 208,000-row unified analysis of eight years of event attendees that changed how invite lists get built, the team's GitHub governance and project intake, and the semantic layer for our AI analytics platform. I got here through the pipeline a lot of mission-driven data people know (organizing, then donor databases, then the full operational picture), and when I took this role I did not know SQL, which the job needed for about eighty percent of the work. I taught myself, took over the full program within six months, and have not stopped building since. I lead how the team adopts AI, responsibly rather than fast, and my working position is that the integration layer beats the prompt: the durable skill is connecting systems together reliably, not prompting well.

Stack

Core, daily, at scale

  • SQL at scale (Redshift, Oracle, Azure)
  • enterprise CRM
  • cloud data warehouse
  • voter file
  • audience and suppression logic
  • BI and semantic-layer tooling

Build with, shipped with

  • TypeScript
  • React
  • Next.js
  • Vite
  • Tailwind
  • Capacitor (iOS/Android)
  • PostgreSQL (Neon, Drizzle)
  • on-device audio DSP (Web Audio, custom FFT/LPC)
  • Vercel
  • Cloudflare Workers
  • Git and GitHub
  • REST and third-party APIs (OpenStates, LegiScan, Congress.gov, TMDB, IGDB, Open Library)
  • LLM integration (Claude, Gemini)

Working knowledge, growing

  • Python (helper scripts and query automation at work, Pandas in the genealogy analyzer)

This tier list gets stronger the moment the internal AI-governance tool ships, since that moves Python up a tier honestly and puts a real production pipeline on it.

Systems

Boring reliability: systems that hold at any energy level, not just peak performance. The proof is the private ops dashboard I live in, sixteen services pulled into one static page, rebuilt six times a day, with an automation layer of eighteen scheduled workflows and thirteen Claude Code routines. Of the repo's last seventy commits, eight were typed by a human.

Private build

A private ops dashboard that pulls sixteen services into one static page: calendar, email, health, money, tasks, reading, contacts, commute, weather, and my own knowledge base, rebuilt six times a day behind real auth.

The interesting parts are derived, not displayed: a recovery score from sleep and heart-rate-variability deltas against a rolling baseline, step counts deduped across devices, cross-calendar conflict detection, a priority cascade for how each evening gets used, a money burn-rate projection, and a staleness backstop that recomputes freshness at render instead of trusting its own last-good status.

8 of the last 70 commits, typed by a human.