Signal
Sound as bars, waveforms, a scrolling history or an LED meter.
The signal draws sound: bars, waveforms, a scrolling history or an LED meter. Feed it a voice source and it follows the audio.
See every signal pattern liveWhen to use which pattern
| Pattern | pattern | When to use it |
|---|---|---|
| Bars | signaling | Live level: bars that follow the audio. |
| Waveform | waveform | Live voice: layered waveform lines. |
| Scrolling | scrolling | Voice over time: a scrolling history of levels. |
| Meter | metering | A precise meter: an LED matrix with peaks. |
| Playing | playing | A recorded voice message: its waveform and how far it has played. |
A voice message
playing draws a recorded message the way a chat app shows one: the waveform, the played part in full ink and the rest dimmed. It fills a wide box instead of a square. SinuaVoiceMessage wraps it with drag-to-seek and a slider for screen readers (not on React Native yet). Sinua decodes no audio: your app passes up to 64 loudness values as envelope, and progress from 0 to 1.
import { useState } from "react";
import { SinuaVoiceMessage } from "@sinua/web/react";
// A voice message in a chat bubble: the waveform, the played part in full ink, and a
// drag or tap anywhere on it to seek. Your app plays the audio; Sinua draws it.
export function VoiceMessage({ peaks, audio }: { peaks: number[]; audio: HTMLAudioElement }) {
const [progress, setProgress] = useState(0);
audio.ontimeupdate = () => setProgress(audio.currentTime / audio.duration);
return (
<SinuaVoiceMessage
envelope={peaks} // up to 64 loudness values, 0..1
progress={progress}
onSeek={(p) => (audio.currentTime = p * audio.duration)}
style={{ width: 220, height: 40 }}
/>
);
}
// Sinua decodes no audio. One way to get the peaks in a browser:
export async function peaksOf(url: string, count = 48): Promise<number[]> {
const data = await (await fetch(url)).arrayBuffer();
const samples = (await new AudioContext().decodeAudioData(data)).getChannelData(0);
const step = Math.floor(samples.length / count);
const peaks = Array.from({ length: count }, (_, i) => {
let max = 0;
for (let j = i * step; j < (i + 1) * step; j++) max = Math.max(max, Math.abs(samples[j]));
return max;
});
const top = Math.max(...peaks) || 1;
return peaks.map((p) => p / top);
}
Props
Every prop, per pattern. Values are your app's data; everything else is style. Defaults can differ per size (20 · 32 · 64).
The signal component is SinuaSignal. Pick a look with pattern; each pattern reads the props below. Sizes are engine sizes (20, 32, 64): defaults can differ per size.
| Pattern | Mode | Speed | Values (app data) |
|---|---|---|---|
Bars (signaling) | bar | 1× | — |
Waveform (waveform) | waveform | 1× | — |
Scrolling (scrolling) | scroll | 1× | — |
Meter (metering) | matrix | 1× | — |
Playing (playing) | playback | 1× | progress, envelope |
Bars
pattern: "signaling" · mode bar · speed 1× · sizes 20, 32, 64
| Prop | Category | Type | Range | Default | Description |
|---|---|---|---|---|---|
barCount | Appearance | integer | 1–64 (UI 4–20) | 9 | Number of level bars. |
barWidth | Appearance | number | 0.05–1 (UI 0.2–0.9) fraction | 0.55 | Bar width as a fraction of each slot. |
hue | Appearance | number | 0–360 deg | 200 | Ink hue in degrees; applies once saturation is above 0. |
ink | Appearance | number | 0–1 fraction | 1 | How present the whole visual is: 1 draws it as the pattern defines it, lower fades everything including its halos. A voice assistant rests below 1 when idle and comes to full ink when it listens. |
minHeight | Appearance | number | 0–1 (UI 0–0.6) fraction | 0.2 | Height of a silent bar, as a fraction of full height. |
saturation | Appearance | number | 0–1 fraction | 0 | How far the ink moves from grey toward the hue (0 = the grey ink). |
Waveform
pattern: "waveform" · mode waveform · speed 1× · sizes 20, 32, 64
| Prop | Category | Type | Range | Default | Description |
|---|---|---|---|---|---|
hue | Appearance | number | 0–360 deg | 200 | Ink hue in degrees; applies once saturation is above 0. |
ink | Appearance | number | 0–1 fraction | 1 | How present the whole visual is: 1 draws it as the pattern defines it, lower fades everything including its halos. A voice assistant rests below 1 when idle and comes to full ink when it listens. |
layerCount | Appearance | integer | 1–6 (UI 1–5) | 3 | Stacked waveform layers (fainter behind). |
lineWidth | Appearance | number | 0.005–0.2 (UI 0.01–0.08) fraction | 0.035 | Stroke width as a fraction of the frame. |
pointCount | Appearance | integer | 2–512 (UI 16–128) | 96 | Points along each waveform line. |
saturation | Appearance | number | 0–1 fraction | 0 | How far the ink moves from grey toward the hue (0 = the grey ink). |
amplitude | Motion | number | 0–0.5 (UI 0.1–0.45) fraction | 0.26 | Peak wave height as a fraction of the frame. |
Scrolling
pattern: "scrolling" · mode scroll · speed 1× · sizes 20, 32, 64
| Prop | Category | Type | Range | Default | Description |
|---|---|---|---|---|---|
barWidth | Appearance | number | 0.05–1 (UI 0.2–0.9) fraction | 0.6 | Bar width as a fraction of each slot. |
hue | Appearance | number | 0–360 deg | 200 | Ink hue in degrees; applies once saturation is above 0. |
ink | Appearance | number | 0–1 fraction | 1 | How present the whole visual is: 1 draws it as the pattern defines it, lower fades everything including its halos. A voice assistant rests below 1 when idle and comes to full ink when it listens. |
minHeight | Appearance | number | 0–1 (UI 0–0.4) fraction | 0.08 | Height of a silent bar, as a fraction of full height. |
saturation | Appearance | number | 0–1 fraction | 0 | How far the ink moves from grey toward the hue (0 = the grey ink). |
fadeWidth | Energy | number | 0.01–1 (UI 0.05–0.8) fraction | 0.3 | Width of the fade at the edges, as a fraction of the frame. |
Meter
pattern: "metering" · mode matrix · speed 1× · sizes 20, 32, 64
| Prop | Category | Type | Range | Default | Description |
|---|---|---|---|---|---|
columnCount | Appearance | integer | 1–32 (UI 4–32) | 12 | LED columns. |
hue | Appearance | number | 0–360 deg | 200 | Ink hue in degrees; applies once saturation is above 0. |
ink | Appearance | number | 0–1 fraction | 1 | How present the whole visual is: 1 draws it as the pattern defines it, lower fades everything including its halos. A voice assistant rests below 1 when idle and comes to full ink when it listens. |
ledCount | Appearance | integer | 2–24 | 8 | LEDs per column. |
ledSize | Appearance | number | 0.2–1 (UI 0.3–1) fraction | 0.7 | LED size as a fraction of its cell. |
minLevel | Appearance | number | 0–1 (UI 0–0.5) fraction | 0.125 | Level of a silent column. |
mirror | Appearance | boolean | 0 | Columns grow out from the middle row instead of rising from the bottom. | |
saturation | Appearance | number | 0–1 fraction | 0 | How far the ink moves from grey toward the hue (0 = the grey ink). |
Playing
pattern: "playing" · mode playback · speed 1× · sizes 20, 32, 64
| Prop | Category | Type | Range | Default | Description |
|---|---|---|---|---|---|
barCount | Appearance | integer | 0–128 (UI 0–96) | 0 | How many bars to draw: 0 = one per envelope value; otherwise the envelope is resampled (keeping peaks). |
barWidth | Appearance | number | 0.05–1 (UI 0.2–0.9) fraction | 0.6 | Bar width as a fraction of each slot. |
hue | Appearance | number | 0–360 deg | 200 | Ink hue in degrees; applies once saturation is above 0. |
ink | Appearance | number | 0–1 fraction | 1 | How present the whole visual is: 1 draws it as the pattern defines it, lower fades everything including its halos. A voice assistant rests below 1 when idle and comes to full ink when it listens. |
minHeight | Appearance | number | 0–1 (UI 0–0.4) fraction | 0.08 | Height of a silent bar, as a fraction of full height. |
playhead | Appearance | boolean | 0 | Draw a thin line at the playback position. | |
saturation | Appearance | number | 0–1 fraction | 0 | How far the ink moves from grey toward the hue (0 = the grey ink). |
unplayedOpacity | Appearance | number | 0–1 fraction | 0.35 | Opacity of the bars not played yet, relative to the played ones. |
progress | Inputs | number | 0–1 fraction | 0 | Playback position, 0 to 1: bars before it are played (full ink), the rest dimmed. |
Advanced props (1)
| Prop | Category | Type | Range | Default | Description |
|---|---|---|---|---|---|
envelope | Inputs | number[] (1–64) | 0–1 fraction | [] | The recorded clip's loudness, oldest first, 0 to 1 per value (up to 64; resample longer clips). Without it a fixed sample message is drawn. |