dB Meter

A sound level meter in the chat, from the user's microphone or a connected sensor.

Pattern

≈ 1,043 tokens per use (tool 757 · guidance 275 · typical payload 11)How token costs are estimated

Example: dB Meter

How loud is it in here?

Sound level

Level
No level yet
Min
Not measured yet
Max
Not measured yet
Average
Not measured yet
Measured
Not measured yet
Nothing is recorded or sent.

Not started

When to use

  • The user asks how loud it is where they are: "How loud is it in here?", "Is my office quiet enough to record a podcast?", "Is the baby's room quiet enough?"
  • The agent has a reading from a connected sound sensor or tool ("the warehouse is at 88 dB") and wants to show it with context.

When not to use

  • The user asks about typical loudness in general ("How loud is a jet?"). Answer in text.
  • Hearing health or medical questions. The meter gives general safety information, not medical advice.
  • A calibrated or legal measurement is needed. Browser readings are approximate and unweighted.

How the agent should use it

  • Live: the user presses Start. The meter never starts on its own. Don't state a level before the measurement result arrives.
  • Say live levels are approximate when you answer, and base the answer on the average.
  • Set purpose when the user names one: recording, sleep or workplace. It adds a verdict line.
  • Reading: every value comes from the sensor's tool result. Set allowAlert only when you can actually create the alert.
  • On alert, create the alert at the threshold and confirm it in one line.
  • Lifecycle: for a reading, set fetchedAt (the reading's asOf) and staleAfterMs (e.g. 5 minutes) on the call so the Renderer marks old readings as stale and offers a refresh. Live sessions measure in the moment and need neither; expiresAt isn't needed for either source.

How it works

SourceShowsControls
liveThe level now, the band, a gauge with peak hold, min / max / average / time measured (after a session the average is the big number, so it isn't repeated), the last 60 sStart measuring / Stop, and "Stops in 0:18"
readingThe sensor's current level, the band, a gauge (peak marker at max), min / max / average / period, the history. The header's one quiet line is the location and "As of"Optional "Alert above N dB"
  • Live levels are approximate. Browser microphones aren't calibrated, so the meter takes the RMS of the time-domain samples (dBFS) and adds a fixed offset: dB ≈ dBFS + calibrationOffset. The default offset is 94, so −20 dBFS ≈ 74 dB. The big live number is labelled "approx." once; the statistics and the summary sent back carry the same caveat (approximate: true).
  • Unweighted. No A-weighting is applied in the browser, so levels can read higher than an A-weighted meter in rooms with a lot of low-frequency noise.
  • Privacy. The microphone is only requested after the user presses Start measuring, and the card says "Nothing is recorded or sent." beside the button. Audio is analysed on the device and never recorded, played back or sent; only the summary goes to the agent. Stop (or the time limit, durationSeconds, default 30 s, max 300 s) releases the microphone and closes the audio context.
  • Bands: Quiet (under 40), Moderate (40–59), Conversation (60–69), Loud (70–84), Very loud (85–99), Harmful (100 and up). Each band shows an icon and its name next to the number. For reference: a quiet bedroom at night is around 30 dB, a quiet office 50 dB, normal conversation 60 dB, a vacuum cleaner 75 dB, a food blender 90 dB, a rock concert 110 dB.
  • Verdicts: recording aims for under 40 dB, sleep for under 35 dB, and workplace recommends hearing protection above 85 dB for long exposure. Live verdicts judge the average so far, since the instant level jumps around (a reading's verdict judges the current level). workplace adds one line: "Not medical advice."
  • Gauge: 50 segments from 20 to 120 dB, filled with one chart color at increasing strength, and the destructive color only from 85 dB. The band name is always written out, so color is never the only signal. The peak marker holds for 1.5 s, then falls.
  • Statistics: the average is the energy average (Leq), 10·log10(mean(10^(L/10))), so a loud minute counts for more than a quiet one: the Leq of 60 and 70 dB is 67.4 dB.
  • Motion and battery: the gauge and sparkline update every frame; with reduced motion they update once a second. The analyser loop and the session clock pause while the tab is hidden.
  • Screen readers: the big digits are hidden; a text value ("64 dB, approximate, Conversation") updates at most once a second, and a polite announcement is made only when the band changes. The gauge is decorative; the sparkline has a text summary.
  • Errors: a blocked permission shows "Microphone access was blocked. Allow it in your browser's site settings to measure." Browsers without microphone access (or pages not served over HTTPS) show an unsupported message, and Start measuring is disabled.

Installation

npx shadcn@latest add https://your-site.com/r/db-meter.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { dbMeterTool } from "@/components/genui/db-meter.schema"

const tools = {
  [dbMeterTool.name]: tool({
    description: dbMeterTool.description,
    inputSchema: dbMeterTool.inputSchema,
  }),
}
components/message.tsx
<DbMeter
  {...part.input}
  status={part.state === "input-streaming" ? "streaming" : undefined}
  result={part.state === "output-available" ? part.output : undefined}
  onResult={(result) =>
    addToolResult({ tool: "show_sound_meter", toolCallId: part.toolCallId, output: result })
  }
/>

A live session sends one result, when it stops. A reading sends one only if the user sets an alert; without allowAlert it's display-only, so give the tool an execute that returns immediately.

{ action: "measurement", average: 52.3, min: 44.1, max: 67.8, durationSeconds: 20, approximate: true }
{ action: "alert", threshold: 90 }

With LangGraph

Push it from a graph node with ui.push({ name: "show_sound_meter", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.

Restoring from history

Pass the stored result back as result. A measurement shows its average, min, max and duration (without the sparkline, which isn't stored); an alert shows "Alert requested above 90 dB". onResult is never called again.

<DbMeter {...part.input} result={part.state === "output-available" ? part.output : undefined} />

Schema

PropTypeNotes
source"live" | "reading"Microphone, or a sensor reading
titlestring?Up to 60 characters. Default "Sound level"
reading{ current, min?, max?, average?, history?, location?, asOf }?Required for reading. All DATA-BOUND. average is the Leq; history is { t, value }[], oldest first
purpose"general" | "recording" | "sleep" | "workplace"Adds a verdict line. Default general
calibrationOffsetnumberLive: dB ≈ dBFS + offset. Default 94
durationSecondsnumberLive: auto-stop, 5–300. Default 30
allowAlertbooleanReading: adds an "Alert above N dB" field. Default false

Host props: status (streaming, ready, measured, alert-set), onResult, result and className.

Source

On this page