# How Audio Level Monitoring Provides Real‑Time Feedback to the Meetily UI

> Discover how Meetily uses Rust and React for real-time audio level monitoring. Get dynamic volume meter feedback directly in the UI. Learn more now.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: internals
- Published: 2026-08-01

---

**Audio level monitoring in Meetily streams live microphone and system audio levels from a Rust‑powered Tauri backend to the React frontend via event emitters, enabling dynamic volume meter displays.**

Meetily, an open‑source meeting assistant built by Zackriya‑Solutions, implements real‑time audio level monitoring to help users visualize their microphone and system audio activity during recordings. This feature bridges Rust's low‑level audio processing capabilities with a modern React UI through Tauri's event system. The implementation spans multiple files in `frontend/src-tauri/src/audio/` and demonstrates how to build responsive, non‑blocking audio visualizations in a hybrid desktop application.

## Backend Architecture: Commands and Monitoring Loop

### Tauri Command Registration

The backend exposes three Tauri commands that the frontend invokes to control monitoring. These are registered in [`src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src/lib.rs) and delegate to the helper functions in [`audio/simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/simple_level_monitor.rs).

- `start_audio_level_monitoring(device_names: Vec<String>)` — launches the monitoring loop for specified devices
- `stop_audio_level_monitoring()` — halts the monitoring task
- `is_audio_level_monitoring() -> bool` — queries whether monitoring is currently active

As implemented in `Zackriya‑Solutions/meetily`, the command handlers are thin wrappers that manage the lifecycle of an async monitoring task.

### Core Monitoring Implementation in [`simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/simple_level_monitor.rs)

The [`audio/simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/simple_level_monitor.rs) file contains the central monitoring logic. It spawns an **async task** that runs continuously while monitoring is enabled, performing the following operations on each iteration:

1. **Reads audio levels** from each selected device using the `cpal` crate, calculating RMS (root‑mean‑square) values
2. **Normalizes levels** to a 0‑1 range suitable for UI display
3. **Constructs `AudioLevelData`** structs containing device name, level, and timestamp
4. **Emits the `"audio‑level‑update"` event** with an `AudioLevelUpdate` payload grouping all device levels

The loop executes at a **default interval of approximately 50 milliseconds**, striking a balance between responsiveness and CPU usage. The use of an async task ensures the monitoring never blocks Tauri's main thread or the frontend.

## Data Structures and Type Definitions

The monitoring system relies on structured types defined in [`audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/level_monitor.rs):

```rust
// AudioLevelData — per-device level information
pub struct AudioLevelData {
    pub device_name: String,
    pub level: f32,        // Normalized 0.0 – 1.0
    pub timestamp: u64,    // Milliseconds since epoch
}

// AudioLevelUpdate — payload for event emission
pub struct AudioLevelUpdate {
    pub levels: Vec<AudioLevelData>,
}

```

These structures enable the frontend to render **separate visual meters** for each audio source—typically the microphone and system audio (speaker output) channels.

## Frontend Integration: Event Listeners and State Management

### Subscribing to Level Updates

The React frontend consumes audio level data by listening for Tauri's `"audio‑level‑update"` event. A typical implementation uses the `listen` function from `@tauri‑apps/api/event`:

```tsx
import { listen } from '@tauri-apps/api/event';
import { useEffect, useState } from 'react';

type AudioLevelData = {
  device_name: string;
  level: number;
  timestamp: number;
};

type AudioLevelUpdate = {
  levels: AudioLevelData[];
};

export function useAudioLevels() {
  const [levels, setLevels] = useState<AudioLevelData[]>([]);

  useEffect(() => {
    const unlisten = listen<AudioLevelUpdate>('audio-level-update', (event) => {
      setLevels(event.payload.levels);
    });

    // Cleanup listener on unmount
    return () => {
      unlisten.then((fn) => fn());
    };
  }, []);

  return levels;
}

```

### Rendering Volume Meters

The level data drives visual components. A minimal meter implementation maps normalized levels to CSS heights:

```tsx
export function AudioMeter() {
  const levels = useAudioLevels();

  return (
    <div className="audio-meters">
      {levels.map((device) => (
        <div key={device.device_name} className="meter-container">
          <span className="device-label">{device.device_name}</span>
          <div className="meter-bar">
            <div
              className="meter-fill"
              style={{ height: `${device.level * 100}%` }}
            />
          </div>
        </div>
      ))}
    </div>
  );
}

```

## Lifecycle Management: Starting and Stopping Monitoring

### Initiating Monitoring

When the user enters a meeting or enables audio preview, the frontend invokes the start command with specific device names discovered through [`audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/devices/discovery.rs):

```tsx
import { invoke } from '@tauri-apps/api/tauri';

async function startMonitoring(microphone: string, systemAudio: string) {
  await invoke('start_audio_level_monitoring', {
    deviceNames: [microphone, systemAudio],
  });
}

```

The `deviceNames` parameter must match valid device identifiers returned by the discovery utilities in [`audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/devices/discovery.rs).

### Terminating Monitoring

Proper cleanup prevents resource leaks and unnecessary CPU consumption. The frontend calls:

```tsx
async function stopMonitoring() {
  await invoke('stop_audio_level_monitoring');
}

```

This pattern is typically executed in a `useEffect` cleanup function or when the meeting session ends.

## Complete Data Flow

The end‑to‑end audio level monitoring operates as follows:

1. **UI invokes** `start_audio_level_monitoring` with target device names
2. **Backend spawns** an async loop in [`simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/simple_level_monitor.rs)
3. **Loop iterates** every ~50ms, sampling RMS levels via `cpal`
4. **Backend emits** `"audio‑level‑update"` events with `AudioLevelUpdate` payloads
5. **UI receives** events through Tauri's listener API
6. **React state updates**, triggering re‑render of volume meter components
7. **UI invokes** `stop_audio_level_monitoring` to terminate the loop

This architecture decouples the audio processing from UI concerns: the Rust backend never holds references to frontend components, and the frontend remains agnostic to how levels are calculated.

## Key Implementation Files

| Path | Purpose |
|------|---------|
| [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs) | Registers Tauri commands and wires handlers |
| [`frontend/src-tauri/src/audio/simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/simple_level_monitor.rs) | Async monitoring loop and event emission |
| [`frontend/src-tauri/src/audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/level_monitor.rs) | Type definitions for level data structures |
| [`frontend/src-tauri/src/audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/discovery.rs) | Device enumeration for monitoring selection |
| [`frontend/src/components/AudioMeter.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/components/AudioMeter.tsx) (example path) | React component consuming level events |

## Summary

- **Rust backend** in [`simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/simple_level_monitor.rs) calculates real‑time RMS audio levels using `cpal`
- **Tauri events** carry `AudioLevelUpdate` payloads from backend to frontend without blocking either side
- **Frontend listeners** receive `"audio‑level‑update"` and drive React state for live meter rendering
- **Lifecycle commands** (`start_`, `stop_`, `is_`) provide explicit control over monitoring state
- **50ms polling interval** balances responsiveness with resource efficiency

## Frequently Asked Questions

### How does Meetily prevent audio level monitoring from freezing the UI?

The monitoring runs in a **dedicated async task** spawned by the Tauri runtime, not on the main thread. Event emission uses Tauri's non‑blocking `app.emit()` method, and the frontend processes updates through its standard React event loop. This design ensures smooth 60fps UI performance even during active audio processing.

### Can the monitoring interval be adjusted for lower CPU usage?

The default ~50ms interval is hardcoded in [`simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/simple_level_monitor.rs). To modify it, you would adjust the sleep duration in the monitoring loop. A longer interval (e.g., 100–200ms) reduces CPU load but decreases visual responsiveness. The current implementation prioritizes smooth meter animation over minimal resource consumption.

### Why does the frontend need to pass device names rather than IDs?

The `start_audio_level_monitoring` command accepts `Vec<String>` device names that must match the human‑readable identifiers returned by [`audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/devices/discovery.rs). This approach aligns with how `cpal` enumerates devices and simplifies debugging. The backend performs name‑to‑device resolution internally when opening audio streams.

### What happens if a monitored device disconnects during operation?

The [`simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/simple_level_monitor.rs) implementation handles stream errors by continuing the loop and attempting reconnections or skipping unavailable devices. The frontend receives level updates only for successfully sampled devices, so meters for disconnected hardware simply stop updating while others continue functioning normally.