🚀 Motivation
There aren’t many (if any!) well-known libraries out there for live audio recording data visualization with a waveform in React Native. So… why not just build one yourself? 😉
🎬 Demo
Pretty neat, right? Let’s dive in.
⚙️ Assumptions
Here’s the setup I used:
`react-native`: 0.79.5
`expo`: 53.0.17
`expo-av`: 15.1.7
`react-native-svg`: 15.12.1
👉 Run it with:
npx expo run:ios
🎤 1. Recording Process
1-1: Enable Metering
The magic trick 🪄 is setting isMeteringEnabled: true
in recording.prepareToRecordAsync()
from expo-av
. This unlocks audio-level metering data (like dB values).
import { Audio, InterruptionModeIOS } from 'expo-av';
const { granted, status } = await Audio.requestPermissionsAsync();
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true,
staysActiveInBackground: true,
interruptionModeIOS: InterruptionModeIOS.DuckOthers,
});
// Adjust as you like 🎛️
const recordingOptions = {
android: {
extension: '.m4a',
outputFormat: Audio.AndroidOutputFormat.MPEG_4,
audioEncoder: Audio.AndroidAudioEncoder.AAC,
sampleRate: 44100,
numberOfChannels: 2,
bitRate: 128000,
},
ios: {
extension: '.m4a',
outputFormat: Audio.IOSOutputFormat.MPEG4AAC,
audioQuality: Audio.IOSAudioQuality.HIGH,
sampleRate: 44100,
numberOfChannels: 2,
bitRate: 128000,
linearPCMBitDepth: 16,
linearPCMIsBigEndian: false,
linearPCMIsFloat: false,
},
web: {
mimeType: 'audio/webm;codecs=opus',
bitsPerSecond: 128000,
},
};
const recording = new Audio.Recording();
await recording.prepareToRecordAsync({
...recordingOptions,
isMeteringEnabled: true, // 🎯 enables audio-level metering
});
await recording.startAsync();
1-2: Poll the Recording State
Now let’s grab that juicy data 📊. Use the recording object and poll its status every 100ms.
// poll metering data every 100ms
const interval = setInterval(async () => {
if (recording) {
const status = await recording.getStatusAsync();
if (status.isRecording && typeof status.metering === 'number') {
setLevels((prev) => {
const newLevels = [...prev, status.metering];
// keep last 100 samples 🪣
return newLevels.slice(-100);
});
}
}
}, 100);
Example of status
:
{"canRecord": true, "durationMillis": 602, "isRecording": true, "mediaServicesDidReset": false, "metering": -48.3475341796875}
The metering value is what you care about 🎧.
👉 Decibel scale basics:
0 dB → loudest possible (max recording level) 🔊
-160 dB → silence 🤫
I only keep the latest 100 values (slice(-100)) because… who wants a million bars on screen? 🙃
Time to visualize! 🎨
Okay let's visualise it.
📊 2. Waveform Visualization with SVG
You don’t need to be a math wizard 🧙 to draw waveforms. Just rectangles on a grid!
How it works:
1
. Draw a rectangle for each audio sample 📦.
- Height = based on audio volume.
- Width = fixed.
2
. Place it at the correct (x, y) coordinate.
Visual guide (yes, it’s really this simple 👇):
Converting dB → amplitude
Formula:
Math.pow(10, db / 20); // standard dB → amplitude
For easier handling, add +40 before conversion:
// Original level: -60dB(almost silence)
Math.pow(10, (-60 + 40) / 20) = Math.pow(10, -1) = 0.1
// Original level: -20dB(small sound volume)
Math.pow(10, (-20 + 40) / 20) = Math.pow(10, 1) = 10
// Original level: 0dB(maximum sound volume)
Math.pow(10, (0 + 40) / 20) = Math.pow(10, 2) = 100
Then scale it with normalizedLevel * height * 0.05
.
(That 0.05
is just a tweak factor — adjust to taste! 🎚️)
And yes, we’ll use Rect
from react-native-svg
to draw the bars.
Codes
// Convert levels to SVG bars
const width = 358;
const height = 280;
const barWidth = 3;
const barSpacing = 1;
const maxBarsCount = Math.floor(width / (barWidth + barSpacing));
const halfHeight = height / 2;
// Create vertical bars for each level
const bars = levels.slice(-maxBarsCount).map((level, index) => {
const normalizedLevel = Math.max(0, Math.pow(10, (level + 40) / 20));
const barHeight = Math.max(1, normalizedLevel * height * 0.05);
// Calculate position so bar extends equally above and below center
const halfBarHeight = barHeight / 2;
const startX = 0;
const x = startX + index * (barWidth + barSpacing);
const y = halfHeight - halfBarHeight;
return (
<Rect
key={index}
x={x}
y={y}
width={barWidth}
height={barHeight}
fill="#8E8E93"
rx={1}
/>
);
});
return (
<View className="bg-white p-2 flex-1">
<Svg height={height} width={width}>
{bars}
</Svg>
</View>
);
- (
levels.slice(-maxBarsCount)
,maxBarsCount
removes the stale bars that overflows in the screen)
✅ Summary
With just expo-av
+ a little SVG magic 🪄, you can visualize live audio waveforms in React Native — no external libraries needed, no hassle.
Next time you record 🎤, make it look cool too.
Top comments (0)