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/webhook checks the X-Signature HMAC-SHA256 over the exact bytes received, in constant time, before parsing anything, then drops Ring's retries by request_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.

Built With

  • aria
  • deepgram
  • ffmpeg
  • hmac
  • next.js
  • node.js
  • playwright
  • react
  • remotion
  • render
  • ring-api
  • ring-developer-playground
  • server-sent-events
  • tailwindcss
  • typescript
  • vibration-api
  • wcag
  • web-crypto-api
  • web-speech-api
  • webrtc
  • whep
Share this project:

Updates

Submission history