Panic Button
One button. The right public agency. Works with no signal.
Track: Civic Tech · Event: RLC Hacks 2026
Inspiration
Ask almost anyone in the US what number to call for a mental health emergency and you'll get one answer: 911.
For a mental health event, that is frequently the wrong door. It dispatches police to a medical call.
The strange part is that the alternatives already exist, they're already funded, and they already work. There are five separate public entry points, run by three different levels of government. Most people can name one of them.
| Door | Who runs it | What it's actually for |
|---|---|---|
| 911 | Local dispatch | Physical danger, injury |
| 988 | Federal — SAMHSA/HHS | Crisis counseling — and can send a mobile crisis team instead of police |
| Mobile crisis team | County | Mental health responders who come to you, no police |
| County behavioral health | County | Ongoing care, sliding scale, Medicaid |
| 211 | Regional | The housing, food, and utility problems underneath the crisis |
Rows 3 through 5 are effectively invisible. Mobile crisis teams matter most — they're the direct alternative to a police response — and because they're county-run, coverage changes at the county line and there is no national directory to look them up in.
So the gap isn't funding or staffing. It's navigation. That's a civic tech problem, and it happens to hit people at the exact moment they have the least capacity to go looking for the answer.
What it does
A stark white screen with one button. Press it, and the screen drops into a dark, calm environment with:
- Plain-language triage — what 911, 988, and 211 are each actually for. For most people this is the first time anyone has explained that 988 can dispatch a mobile crisis team instead of police.
- "Who handles this," localized to your county — the app reverse-geocodes to a county via the US Census geocoder, then names the specific public offices serving it, with what each one does and when to use it.
- Immediate crisis lines — 988 call/text/chat and 911, one tap, plus eight more national lines including Veterans, Trevor Project, Trans Lifeline, and Spanish-language.
- Nearby licensed facilities — via the SAMHSA treatment locator, with distance, phone, and directions.
- A breathing pacer — optional, 12-second cycle, for grounding while you decide.
And it works with no internet. Airplane mode, dead data, dying phone: the app loads, the triage renders, and every number still dials. Calling doesn't need a data connection.
How I built it
A zero-dependency static PWA — plain HTML, CSS, and ES modules. No framework, no build
step, no npm install between cloning it and running it.
The architectural rule everything follows from:
The crisis lines, the triage, and the agency list are painted synchronously — before any network request, permission prompt, or geolocation call begins.
Nothing async is ever awaited before 988 is on screen. Location doesn't populate the page, it only relabels what already rendered. That means every failure mode — permission denied, API down, offline, geocoder unreachable — degrades a screen that is already useful, instead of leaving a spinner in front of someone in crisis.
Both lookup chains degrade through tiers to a floor that cannot fail, because the bottom tier is pure string construction that's correct with no network at all:
- Facilities: SAMHSA → Google Places → deep links with coordinates pre-filled
- County: US Census geocoder → Nominatim → generic agency labels
A service worker precaches the app shell but deliberately excludes every API response, so the offline experience can never serve a stale treatment-center number.
The hardest decision
Every phone number in this app is national and fixed. Not one is generated, guessed, or derived from a location.
I could have made the local agency cards look far more impressive by generating county phone numbers. I deliberately didn't. There's no public per-county database of mobile crisis numbers to work from, which means any local number would have been fabricated — and a wrong number dialed at 3am costs someone an attempt they may not make twice.
So local agencies are search handoffs only. A search link can be unhelpful. It cannot be a wrong number.
The same rule is why facility results are excluded from the offline cache, and why NAMI's business-hours limitation is stated inline on the card rather than left to imply 24/7 availability.
The demo would look better if I'd broken that rule. The app would be worse.
Challenges
Building against APIs I couldn't reach. SAMHSA, the Census geocoder, and Nominatim were all blocked by my development environment's network policy. Rather than guess and hope, I wrote every parser defensively against multiple documented response shapes, then replayed realistic payloads through Playwright route interception to exercise the success paths I couldn't hit live. That verified county suffixing, rows without names being dropped, three different phone formats normalizing, string distances coercing, and mixed service-tag shapes handling.
Designing for someone whose arousal is already too high. This drove more decisions
than anything else: no red anywhere (911 is amber — red reads as alarm); never pure
white on pure black; particles that each drift on their own period so the field never
pulses in unison, because synchronized pulsing reads as urgent; full
prefers-reduced-motion support that kills all motion while leaving layout and color
identical; and Escape to exit instantly, because if someone walks in, hunting for a
button is the wrong thing to ask.
A bug that testing caught and I would have shipped. [hidden] was silently losing
to my id- and class-scoped display rules, which left collapsed sections expanded and
put a full dead viewport at the top of the support screen. It only surfaced because I
was screenshotting across five scenarios instead of trusting that it looked fine.
Accomplishments I'm proud of
- It genuinely works offline. Not "degrades gracefully" — reload in airplane mode and 988 still dials.
- Zero dependencies. Nothing to install, nothing to break during a demo, no supply chain.
- It's honest about what it doesn't know. The README and AI disclosure both state plainly that no live API run has happened and that parsing is verified against mocks, not real responses. A crisis tool that overstates its reliability is worse than one that doesn't exist.
- The safety rule held even when breaking it would have demoed better.
What I learned
That the interesting civic tech problem usually isn't a missing system — it's a system that exists and can't be found. 988 works. Mobile crisis teams work. County behavioral health works. All of it is already paid for. The failure is a navigation layer nobody built, and that layer is far cheaper to build than any of the services underneath it.
I also learned how much of designing for a crisis is subtraction: fewer decisions, fewer colors, less motion, less to read.
What's next
- A real per-county mobile crisis directory. This is the highest-value addition by a wide margin, and it's a data-collection problem more than an engineering one.
- Verify against the live SAMHSA API. There's an official developer guide and access request process; the parser should be confirmed against documented fields.
- Warmlines — peer support for the enormous space between "fine" and "emergency."
- Internationalization. Everything here assumes US public services.
- Accessibility audit with actual screen reader users, not just correct ARIA.
Built with
javascript · html5 · css3 · pwa · service-workers · canvas · web-audio-api
· geolocation-api · samhsa-findtreatment-api · us-census-geocoder · nominatim
· google-places-api · playwright · node.js
Try it
git clone https://github.com/TONZHub/PanicButton
cd PanicButton
node tools/serve.mjs # → http://localhost:4173
No dependencies, no build step, no API key required.
Best demo: open it, turn on airplane mode, reload, and press the button. Everything still works.
A note on scope
Panic Button is not a clinical tool. It performs no risk assessment, gives no advice, and makes no claim to clinical efficacy. It is a router to trained humans and public agencies, deliberately and only that.
AI tool usage is disclosed in full in AI-DISCLOSURE.md, per hackathon rules.
If you are in crisis right now: please call or text 988. You don't need this app to do that.
Built With
- canvas
- claudecode
- css3
- geolocation-api
- google-places
- html5
- javascript
- node.js
- nominatim
- playwright
- pwa
- samhsa-findtreatment-api
- service-workers
- us-census-geocoder
- web-audio-api
Log in or sign up for Devpost to join the conversation.