ZYVOPMulti-Platform Sync
SeriesAI NewsWhy ZyVOPJoin Discord
LoginGet Started
ZYVOPMulti-Platform Sync

The Developer Publishing Hub. Write once, publish everywhere, and make your work citation-ready with built-in SEO, AEO, and GEO discovery support. Zero reader paywalls.

Content

  • Categories
  • Tags
  • Badges
  • Leaderboard
  • Write Article
  • Newsletter

Company

  • About Us
  • Why ZyVOP
  • Developer API & CLI
  • Write for Us
  • Contact

Connect

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • DMCA Policy
  • Code of Conduct

© 2026 ZyVOP. Developer Publishing Hub.

Zero paywalls · Full content ownership
All systems operational
HomeAudio That Sees Itself

Audio That Sees Itself

Giovambattista Fazioli
Giovambattista Fazioli
Senior Full-stack Engineer, Lead DeveloperSupport
September 4, 2026
5 min read
Audio That Sees Itself
#reactjsdevelopment#TypeScript#nextjs#react

A Mantine-native audio player for React with waveform visualisation and a live spectrum analyser, built on the Web Audio API. Same three-layer architecture as Mantine Video — drop-in <Audio />, composable compound API, headless useAudio hook — but designed for a medium that has no frame to look at.

Introduction

Mantine Video shipped last week. The next obvious step was to give audio the same treatment — a polished default player, a composable control bar, a headless hook for fully custom UIs. The catch is that audio has no frame to look at: a 30-second video communicates its content the instant you hit play, while a 30-second audio file looks like a thin grey line. So the new component does the opposite of what most React audio players do — it makes the audio look at itself: decoded waveform on <canvas>, live FFT spectrum on the analyser node, every part theme-aware.

Mantine Audio Waveform

What is mantine-audio?

@gfazioli/mantine-audio wraps the native HTML <audio> element with three layers, layered from "drop-in" to "fully custom":

  • A polished default <Audio /> factory with four built-in variants (overlay, minimal, floating, bordered) and five sizes (xs–xl), all CSS-driven via [data-variant] / [data-size] attributes.

  • A composable compound API — ten sub-components you can reorder, replace or restyle: Audio.Controls, Audio.PlayButton, Audio.SkipButton, Audio.Timeline, Audio.TimeDisplay, Audio.MuteButton, Audio.VolumeSlider, Audio.SpeedControl, Audio.Waveform, Audio.Spectrum.

  • A fully headless useAudio hook with 16 state values, 12 actions, the decoded peaks, the AnalyserNode, and currentSrc — to drive a 100% custom UI on top of a plain <audio> element.

It targets Mantine 9 and React 19, is TypeScript-first, and has zero runtime dependencies beyond React, Mantine, and @tabler/icons-react (optional peer).

✨ Key Features

Waveform visualisation

Audio.Waveform decodes the audio file once via fetch + decodeAudioData, downsamples to waveformSamples peaks (default 512), and renders them on a <canvas>. Click or drag to seek. The played / unplayed boundary tracks the playhead at 60fps via requestAnimationFrame, not at the slower timeupdate cadence (~4Hz) — so the colour transition moves smoothly with the audio.

<Audio src="/track.mp3" variant="floating"><Audio.Waveform height={80} mirrorGap={0} /><Audio.Controls /></Audio>

Enter fullscreen mode Exit fullscreen mode

mirror (default true) draws bars symmetrically around the centerline for the classic Audacity look. mirrorGap controls the divider thickness — 0 for a continuous filled shape, >0 for a visible centerline.

Live spectrum analyser

Mantine Audio Spectrum

Audio.Spectrum is a frequency-domain visualiser driven by Web Audio's AnalyserNode. The AudioContext is lazy-initialised on the first play (autoplay-policy compliant), and the bars react to the music in real time. Two color modes — solid (uniform fill) and gradient (vertical fade to transparent). Bin distribution is proportional: no high-frequency bins are dropped when barCount doesn't divide fftSize evenly.

<Audio src="/track.mp3" color="grape"><Audio.Spectrum barCount={48} colorMode="gradient" /><Audio.Controls /></Audio>

Enter fullscreen mode Exit fullscreen mode

Headless useAudio hook

When the default layout doesn't fit, drop the compound layer entirely and drive your own UI:

import { useAudio } from '@gfazioli/mantine-audio';

function MyPlayer() {const { playing, currentTime, duration, peaks, analyser, toggle, audioRef } = useAudio({
    src: '/track.mp3',});

  return (
    <div>
      <audio ref={audioRef} crossOrigin="anonymous" />
      <button onClick={toggle}>{playing ? 'Pause' : 'Play'}</button>
      <span>{currentTime.toFixed(1)} / {duration.toFixed(1)}</span>
    </div>);}

Enter fullscreen mode Exit fullscreen mode

The hook returns 16 state values (playing, currentTime, duration, volume, muted, playbackRate, peaks, analyser, audioContext, currentSrc, …) and 12 action functions (play, pause, toggle, seek, seekBy, setVolume, mute, unmute, toggleMute, setPlaybackRate, …).

scrubSound — hear the seek

Most audio players pause during a seek-drag, then jump to the new position. scrubSound keeps the audio playing during the drag — you hear short snippets of audio as the cursor moves, the way Audacity, Adobe Audition, and iTunes do it. And it works even from a paused state: the player starts on mousedown, plays through the drag, and pauses again on release.

<Audio src="/track.mp3" scrubSound><Audio.Waveform height={80} /><Audio.Controls /></Audio>

Enter fullscreen mode Exit fullscreen mode

The flag propagates via React context, so both Audio.Timeline and Audio.Waveform honour it automatically. Individual sub-components can still override their own scrubSound if you want fine-grained control.

Multiple sources with runtime fallback

For cross-browser compatibility, adaptive bitrate, or media-query switching, pass sources instead of src. The browser picks the first entry whose type it can play via canPlayType():

<Audiosources={[
    { src: '/track.aac',  type: 'audio/aac' },   // iOS preferred
    { src: '/track.ogg',  type: 'audio/ogg' },   // Firefox preferred
    { src: '/track.mp3',  type: 'audio/mpeg' },  // universal fallback]}fallbackSrc="/last-resort.mp3"/>

Enter fullscreen mode Exit fullscreen mode

src and sources are mutually exclusive — a dev-mode warning is logged if both are set. fallbackSrc mirrors the equivalent prop on Mantine Image and kicks in when every entry in sources errors at runtime.

asBackground preset

Turn the same player into an ambient hero / section background in one prop:

<Box pos="relative" h="100vh"><Audio src="/ambient.mp3" asBackground loop muted /><YourHeroContent /></Box>

Enter fullscreen mode Exit fullscreen mode

controls and shortcuts are disabled by default in this mode, and a small floating mute toggle is rendered in the bottom-right corner (disable with backgroundMuteButton={false}).

Sizes & layouts

size scales 11 CSS variables at once — padding, gap, icon size, play button, action button, timeline thumb, time display width, volume slider width, font size. Five presets cover most needs:

<Audio size="xs"  src="/voice-note.mp3" variant="minimal" />  // chat bubble<Audio size="sm"  src="/podcast.mp3"   variant="bordered" /> // card<Audio size="lg"  src="/album.mp3"     variant="floating" /> // hero card<Audio size="xl"  src="/master.wav"    variant="overlay"  /> // studio scrub

Enter fullscreen mode Exit fullscreen mode

And because the default <Audio.Controls /> is just a horizontal row, you can drop it (controls={false}) and arrange the sub-components however you like — vertical stack, custom skip ranges, separate volume slider section — using any layout primitive from @mantine/core.

Keyboard shortcuts

When the player has focus:

  • Space / K — play/pause

  • J / L — seek backward / forward 10s

  • ← / → — seek backward / forward 5s

  • ↑ / ↓ — volume ±5%

  • M — toggle mute

  • > / < — speed ±0.25×

🚀 Getting Started

npm install @gfazioli/mantine-audio

Enter fullscreen mode Exit fullscreen mode

import { Audio } from '@gfazioli/mantine-audio';import '@gfazioli/mantine-audio/styles.css';

function App() {return <Audio src="/track.mp3" />;}

Enter fullscreen mode Exit fullscreen mode

That's the entire setup — you now have a polished, accessible audio player with timeline, time display, mute, volume slider, and speed control.

🎨 Styles API

Audio exposes the full Mantine Styles API: every sub-component is a styles-api selector you can target via classNames or styles. Eighteen selectors in total — root, audio, controls, controlBar, playButton, timeline, timelineBuffered, timeDisplay, muteButton, skipButton, volumeSlider, speedControl, waveform, waveformCanvas, waveformHover, spectrum, spectrumCanvas, backgroundMuteButton.

Nine CSS variables let you re-skin the whole player from the outside:

.mantine-Audio-root {--audio-color: hotpink;--audio-waveform-color: rgba(255, 255, 255, 0.15);--audio-waveform-played-color: hotpink;--audio-spectrum-bar-color: hotpink;--audio-spectrum-bar-color-fade: transparent;}

Enter fullscreen mode Exit fullscreen mode

Five data-* modifiers (data-variant, data-playing, data-paused, data-ended, data-as-background) are available on the root for state-based styling.

Live Demo & Documentation

The full docs site has interactive demos for every feature — usage, configurator, sizes, compound API, custom layout, waveform with a live prop configurator, spectrum with another configurator, volume & speed, asBackground, multiple sources, headless usage, keyboard shortcuts, styles API, and a "use cases" showcase with real-world layouts (inline voice-note bubble, podcast card with cover, mini sticky bar, studio scrub).

→ https://gfazioli.github.io/mantine-audio

Links

  • 📦 npm: https://www.npmjs.com/package/@gfazioli/mantine-audio

  • 📖 Documentation: https://gfazioli.github.io/mantine-audio

  • 🔗 GitHub: https://github.com/gfazioli/mantine-audio

  • 🌐 Mantine Extensions Hub: https://mantine-extensions.vercel.app/

Comments (0)

Login to post a comment.

Giovambattista Fazioli
Giovambattista Fazioli

Senior Full-stack Engineer, Lead Developer

Italian Senior Full-stack Engineer and Lead Developer on the Cloud team at Namecheap. I build developer tools, React/Mantine UI components and native macOS apps — mostly open source. I work across TypeScript, Next.js, Go and SwiftUI, and I've been coding since the Commodore/Assembly days. Creator of WP Bones and 25+ Mantine extensions, and maintainer of the Undolog open-source studio.

Support
Subscribe to Giovambattista Fazioli's Newsletter

More from Giovambattista Fazioli

View profile

FinderGit 0.29.0 — open a commit like a folder

There’s a moment, reading a commit list, where you want to know one small thing: which files did that one touch?

3 minSep 4

Netfox 0.18.0 — is it my connection, or the internet?

There is a question that comes up every time something feels slow, and until now Netfox could not...

3 minSep 4

Mantine Book — Grab Any Edge, Turn Any Page

A realistic iBooks-style book for React built on Mantine: stack two-sided pages and turn them by...

5 minSep 4

Netfox — The macOS app that never loses track of your network

I built Netfox because every existing answer to "what's actually on my network?" annoyed me. The...

5 minSep 4

The Player, the Pieces, and the Hook

A Mantine-native video player for React that ships as three layers in one package: a polished...

6 minSep 4