OHMOHM Studio

Cookbook: triage form on a tablet

Build an ER triage app — record, extract, print — in 200 lines.

View as Markdown

End-to-end recipe: an ER nurse picks up a tablet, taps Record, narrates the patient's vitals + complaint, and a structured triage card is printed and shared with the consulting doctor — under 30 seconds.

Studio side

  1. Clone Emergency Triage as your starter API.
  2. Trim the schema to: vitals, chiefComplaint, onset, triageCategory (choice: red / orange / yellow / green), actions (textarea).
  3. Add an input stationId: string.
  4. Publish as slug er-triage.
  5. Mint an ohms_test_* key for the demo build.

Web build (Vite + React)

src/Triage.tsx
import { OHM } from "@ohm_studio/sdk";
import { OhmProvider, useRecorder } from "@ohm_studio/sdk/react";

const ohm = new OHM({
  apiKey: import.meta.env.VITE_OHM_TEST_KEY!,
  baseUrl: "https://api.ohm.doctor",
});

export default function App() {
  return (
    <OhmProvider client={ohm}>
      <Triage />
    </OhmProvider>
  );
}

function Triage() {
  const r = useRecorder({
    apiSlug: "er-triage",
    extractInputs: { stationId: "ER-2" },
    speakerMode: "doctor_patient",      // nurse + patient triage
    extractLanguage: "auto",
    silenceAutoStop: { ms: 6000 },
    maxDurationMs: 5 * 60_000,
    persist: { metadata: { stationId: "ER-2" } },
    wakeLock: true,
  });

  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-6">
      {!r.data && !r.extracting && (
        <button
          onClick={r.isRecording ? r.stop : r.start}
          className={`w-40 h-40 rounded-full text-2xl font-bold text-white ${
            r.isRecording ? "bg-red-600 animate-pulse" : "bg-emerald-700"
          }`}
        >
          {r.isRecording ? `STOP\n${r.durationSec.toFixed(0)}s` : "RECORD"}
        </button>
      )}
      {r.extracting && <div className="text-lg">Extracting…</div>}
      {r.data && (
        <article className="max-w-md w-full p-6 rounded-xl border bg-white">
          <h1 className="text-xl font-semibold">Triage card</h1>
          <p className="text-sm text-slate-600 mb-4">{r.transcript}</p>
          <pre className="text-xs">{JSON.stringify(r.data, null, 2)}</pre>
          <button onClick={() => window.print()}>Print</button>
        </article>
      )}
    </div>
  );
}

Note the useRecorder hook in @ohm_studio/sdk/react does the codec cascade, mic permission, VU metering, silence auto-stop, wake-lock, and the audio.extract call for you. persist: true writes the recording to IndexedDB before the upload — if the tablet reboots mid-upload you can recover via usePendingRecordings() and retry. See Browser Recorder.

Hardening for production

  • Move the live key behind a backend proxy (see RN key handling — same pattern works for web).
  • Add a OhmRateLimitError handler — show "busy, retry in N seconds".
  • Wire onUsage into your hospital's metrics so finance can track volume per station.
  • Set retainPayloads: true on this API if you want the audit log to keep transcripts for medico-legal review.

Performance

End-to-end on a 30-second consult: typically 2–4 seconds wall clock (STT + extraction). The doctor receives the structured card before they reach the patient bay.