Inspiration
A doorbell is made for ears, and a camera is made for eyes. About 430 million people live with disabling hearing loss (WHO) and 43 million people are blind (Lancet Global Health, 2021). For them, a smart doorbell mostly says nothing at all. A missed chime is a missed delivery, a missed visitor, or simply not knowing someone is at your door. I wanted a Ring doorbell to reach every sense, not just one.
What it does
Herald turns Ring doorbell and camera events into alerts people can see, hear or feel.
- One event, every sense. When Ring reports a doorbell press, a person, a package, a vehicle, movement, or a camera going offline, every open Herald screen responds in the ways its user can perceive: a full-screen colour flash, a vibration rhythm that's different for each kind of event, a natural spoken alert, and screen-reader announcements that also reach refreshable braille displays.
- Set up per screen. Each phone, tablet, laptop or TV picks a profile: Deaf or hard of hearing, Blind or low vision, Deafblind, or a custom mix. A wall TV can flash while a phone in the same home vibrates.
- Only what matters interrupts. Doorbell presses and camera problems always interrupt. People and packages interrupt by default. Cars and general movement go quietly into History.
- Live view. Watch the door over Ring's WHEP live stream, with the Ring token kept on the server.
- Works without a device. A built-in simulator sends real HMAC-signed Ring webhooks (v1.1) to Herald's own endpoint, so the whole pipeline runs from the Ring Developer Playground.
How I built it
- Next.js 16, React 19, TypeScript and Tailwind 4, running as one long-lived Node process.
- ring-kit, an open-source (MIT) TypeScript client I wrote for the Ring Partner API: devices and status, WHEP live-view sessions, snapshots, event history and clip downloads, plus webhook signing, verification and a signed event simulator.
- Webhooks:
/api/webhookchecks theX-SignatureHMAC-SHA256 over the exact bytes received, in constant time, before parsing anything, then drops Ring's retries byrequest_id. Alerts go onto an in-memory bus and out to every open screen over Server-Sent Events. - Live view: the browser makes a WebRTC offer with one receive-only video track. Herald's server adds the token and relays it to Ring's WHEP endpoint, so the token never reaches the browser.
- Voice: alerts are spoken in Deepgram's Aura-2 voice, synthesised once per sentence on the server and cached. The key never reaches the browser, and the endpoint only speaks Herald's own alert sentences. With no key or a slow answer, the browser's built-in voice takes over.
- Accessibility as the spec: WCAG AAA (7:1) text contrast in both themes, checked by a script across 41 colour pairs; a flash that pulses at 0.75 a second (WCAG 2.3.1 allows 3) and holds steady with reduce motion; a modal alert dialog that moves focus to Dismiss and back; live regions; Atkinson Hyperlegible type.
- 52 unit tests on Node's built-in test runner.
Challenges I ran into
- No device, no webhooks. Testing with a real device needs a Ring device located in the US plus Ring Protection, and I'm in India. The Developer Playground streams simulated video but has no doorbell press and sends no webhooks. So I built a webhook simulator into ring-kit that signs v1.1 payloads exactly as documented and sends them through Herald's real verification path.
- The sample wasn't safe to copy. The reference app checks a bearer header Ring never sends instead of the HMAC signature, and returns the access token to any caller. I wrote verification and the live-view relay from the documentation instead, and logged both for the Ring team.
- 30-minute tokens. Herald reads the token on every request, so a fresh Playground token works without a restart, and the interface says when it has expired.
- Browsers block sound and vibration until someone interacts with the page. Herald says so on screen and turns them on at the first tap or key press.
- Flashing safely. The flash has to be impossible to miss without being dangerous. Only the margins pulse, slowly, and the text stays on a solid panel so its contrast never drops.
Accomplishments that I'm proud of
- Verified against the real Ring API: device list and status, WHEP live view, snapshots, and simulated events addressed to the real Playground device and account.
- Every profile keeps at least one output its user can perceive, and a test enforces it.
- An 11-entry friction log for the Ring team, each entry with a concrete suggestion, such as a "Send test webhook" button in the Playground.
- ring-kit, which any Ring developer can use to verify webhooks properly and to test without a device.
- The demo video captions every word and sound, and its narration describes the screen, so it works with the sound off or with your eyes closed.
What I learned
- Verify the raw bytes, then parse. Re-serialized JSON changes the bytes and breaks the HMAC, and a test covers exactly that.
- Accessibility details compound. First-run setup has a confirm button because arrow keys select radio buttons as they move: saving on select would store the first option a screen-reader user landed on.
- Deploying taught me one more: behind an HTTPS proxy, Next.js takes the scheme from
X-Forwarded-Proto, so a server calling its own address has to use plain HTTP.
What's next
- Ring account linking with OAuth and token refresh, so Herald works with real homes.
- Web Push, so a phone in a pocket gets alerts while Herald is closed.
- Spoken descriptions of who is at the door, from a live-view frame.
- A Fire TV layout for the screen people watch most.
Log in or sign up for Devpost to join the conversation.