Developer guide

Build, fork, ship.

OpenHush is a TypeScript monorepo: a Next.js dashboard, an SDK for authoring Whispers, and firmware that talks to both. Web standards. Boring tech. Cloneable in a minute.

Architecture

Three pieces, one stack.

Every layer speaks the same language and shares the same types. No hidden services, no proprietary protocols.

Hush (device)

Open-hardware speaker with NFC reader and offline-first audio cache. Firmware fetches playlists from your dashboard and plays locally. Optional cloud sync for multi-device households.

firmware · NFC · audio

Whispers (cards)

NFC tags bound to playlists in your library. Pre-printed sets ship with art and audio; blank cards let you bind anything to anything. Tap → play. Remove → stop.

NFC · playlists · binding

Dashboard

Next.js app for authoring Whispers, uploading audio, managing devices, and seeing what's been played. Self-host on your LAN, a Pi, or a VPS — your call.

Next.js · Drizzle · self-hosted
Quick start

From clone to first Whisper in under five minutes.

No accounts to create, no API keys to wait for. Everything runs locally first; sync to the cloud only if you want to.

  1. Clone the repo

    Pull the monorepo. The website, dashboard, and firmware all live together.

    terminal
    git clone https://github.com/OpenHush/website.git
    cd openhush
  2. Install dependencies

    Everything is pnpm-based. One command, the whole stack.

    terminal
    pnpm install
  3. Run the dashboard

    The dashboard is where you author Whispers, manage devices, and bind cards.

    terminal
    pnpm --filter dashboard dev
    
    # Open http://localhost:3000
  4. Configure your Hush

    Point your Hush at your dashboard URL. Local dev works over your home network — no public tunnel required.

    hush.config.ts
    // hush.config.ts
    export default {
      dashboard: "http://192.168.1.42:3000",
      cache: "./.audio-cache",
      ledColor: "warm",
    } satisfies HushConfig;
  5. Author your first Whisper

    Upload audio, drop tracks into a playlist, scan a blank NFC card, and you're done. Tap on Hush — it plays.

    scripts/seed-whisper.ts
    // scripts/seed-whisper.ts
    import { createWhisper } from "@openhush/sdk";
    
    await createWhisper({
      title: "Bedtime stories",
      tracks: [
        { src: "./audio/01-the-moon.mp3", title: "The moon" },
        { src: "./audio/02-the-fox.mp3", title: "The fox" },
      ],
      cardUid: "04:A2:B7:33:5E",
    });
Stack

What's under the hood.

Boring, well-supported, well-documented technology. On purpose.

  • TypeScript — strict mode, end to end.
  • Next.js + React — for the dashboard and authoring tools.
  • Drizzle ORM + SQLite — local-first database, runs anywhere.
  • NextAuth — authentication for the dashboard.
  • pnpm workspaces — one repo, many packages.
  • Astro — for this very website.
  • Open hardware — schematics in the repo, BOM listed, no NDAs.
SDK preview

Author Whispers programmatically.

The SDK is a thin TypeScript wrapper over the dashboard API. Build importers, scripts, or full integrations.

bind-card.ts
import { OpenHush } from "@openhush/sdk";

const hush = new OpenHush({
  baseUrl: process.env.DASHBOARD_URL!,
  token: process.env.OPENHUSH_TOKEN!,
});

const whisper = await hush.whispers.create({
  title: "Forest sounds",
  description: "Ambient loop for quiet time.",
  tracks: ["./audio/birds.mp3", "./audio/leaves.mp3"],
});

await hush.cards.bind({
  cardUid: "04:A2:B7:33:5E",
  whisperId: whisper.id,
});
webhook.ts
import { onPlayback } from "@openhush/sdk/events";

onPlayback(async (event) => {
  // Log, analyze, or react however you like.
  console.log(`[${event.deviceId}] ${event.whisper.title}`);

  if (event.track.title === "The moon") {
    await dim_lights_in_kids_room();
  }
});
Contribute

This is a community project.

Bugs, ideas, hardware mods, audio packs, translations — all welcome. Open an issue, send a PR, or just say hi.

Code of conduct, contribution guide, and roadmap live in the repo. Licensed under MIT.