dB Meter
A sound level meter in the chat, from the user's microphone or a connected sensor.
≈ 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
- Min
- Not measured yet
- Max
- Not measured yet
- Average
- Not measured yet
- Measured
- Not measured yet
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
measurementresult arrives. - Say live levels are approximate when you answer, and base the answer on the
average. - Set
purposewhen the user names one:recording,sleeporworkplace. It adds a verdict line. - Reading: every value comes from the sensor's tool result. Set
allowAlertonly 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, setfetchedAt(the reading'sasOf) andstaleAfterMs(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;expiresAtisn't needed for either source.
How it works
| Source | Shows | Controls |
|---|---|---|
live | The 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 s | Start measuring / Stop, and "Stops in 0:18" |
reading | The 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:
recordingaims for under 40 dB,sleepfor under 35 dB, andworkplacerecommends 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).workplaceadds 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
import { tool } from "ai"
import { dbMeterTool } from "@/components/genui/db-meter.schema"
const tools = {
[dbMeterTool.name]: tool({
description: dbMeterTool.description,
inputSchema: dbMeterTool.inputSchema,
}),
}<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
| Prop | Type | Notes |
|---|---|---|
source | "live" | "reading" | Microphone, or a sensor reading |
title | string? | 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 |
calibrationOffset | number | Live: dB ≈ dBFS + offset. Default 94 |
durationSeconds | number | Live: auto-stop, 5–300. Default 30 |
allowAlert | boolean | Reading: adds an "Alert above N dB" field. Default false |
Host props: status (streaming, ready, measured, alert-set), onResult, result and className.