Hover plugin

The Hover plugin draws a vertical line and an elapsed-time label that follow the mouse cursor over the waveform. It works with zero configuration and is the quickest way to give users a scrub-preview experience.

See it in action: live Hover example.


Setup

Install

The plugin ships with wavesurfer.js — no separate package is needed.

import WaveSurfer from 'wavesurfer.js'
import HoverPlugin from 'wavesurfer.js/dist/plugins/hover.esm.js'

Or via CDN (ESM):

import HoverPlugin from 'https://unpkg.com/wavesurfer.js@7/dist/plugins/hover.esm.js'

Register the plugin

Pass a plugin instance to WaveSurfer.create() through the plugins array:

const ws = WaveSurfer.create({
  container: '#waveform',
  url: '/audio.mp3',
  plugins: [
    HoverPlugin.create(),
  ],
})

No further setup is required. The cursor line and label appear as soon as the mouse enters the waveform.


Options

All options are optional. When lineColor is not set the plugin inherits the waveform’s cursorColor, then progressColor, as a fallback.

Option Type Default Description
lineColor string (inherited) Color of the vertical cursor line. Falls back to the main WaveSurfer cursorColor, then progressColor, when omitted.
lineWidth string | number 1 Width of the cursor line. Plain numbers are treated as pixels; pass a string such as '2px' to be explicit.
labelBackground string (none) Background color of the timestamp label. Leave unset for a transparent label.
labelColor string (inherited) Color of the timestamp label text.
labelSize string | number 11 Font size of the label text. Plain numbers are treated as pixels.
labelPreferLeft boolean false When true the label appears to the left of the cursor whenever there is enough space, instead of to the right.
formatTimeCallback (seconds: number) => string m:ss Custom function to format the timestamp string shown in the label.

formatTimeCallback

The callback receives one argument — the hovered position in seconds — and must return a string. The default formats as m:ss:

HoverPlugin.create({
  formatTimeCallback: (seconds) => {
    const m = Math.floor(seconds / 60)
    const s = Math.floor(seconds % 60)
    return `${m}:${String(s).padStart(2, '0')}`
  },
})

To show milliseconds, compute the fractional part of seconds:

formatTimeCallback: (seconds) => {
  const m = Math.floor(seconds / 60)
  const s = Math.floor(seconds % 60)
  const ms = Math.round((seconds % 1) * 1000)
  return `${m}:${String(s).padStart(2, '0')}.${String(ms).padStart(3, '0')}`
},

Events

The plugin emits one event:

Event Arguments Description
hover relX: number Fired on every pointermove over the waveform. relX is the cursor’s horizontal position as a fraction of the total waveform width, from 0 (start) to 1 (end).
const hover = HoverPlugin.create()

const ws = WaveSurfer.create({
  container: '#waveform',
  url: '/audio.mp3',
  plugins: [hover],
})

hover.on('hover', (relX) => {
  // relX is between 0 and 1
  const duration = ws.getDuration()
  console.log('Hovered at', (relX * duration).toFixed(2), 's')
})

Common pitfalls

Hover + Zoom together can cause a jittery or blank cursor line. When the Zoom plugin is active it changes the waveform’s scroll position and rendered dimensions while the user is also moving the mouse — a rapid series of resize and scroll events can make the hover line flicker or disappear entirely. Both plugins are designed to co-exist (the Hover plugin re-computes its position on zoom and scroll events), but a few things help keep the combination smooth:

  • Register both plugins at construction time in the plugins array. Registering them at different times can cause the hover line to be sized against a stale wrapper width.
  • If you set a very low debounceTime on the Zoom plugin (or 0), every scroll tick triggers a full re-render. Setting debounceTime to at least 200 ms significantly reduces the number of concurrent updates and eliminates most jitter.
  • On low-powered devices, setting a shorter barWidth or disabling normalize on the main WaveSurfer instance reduces render cost and makes both plugins feel more responsive.

See Zoom plugin for debounceTime and related options.