diff --git a/cypress/e2e/spectrogram.cy.js b/cypress/e2e/spectrogram.cy.js
index 89938c72e..18d711c17 100644
--- a/cypress/e2e/spectrogram.cy.js
+++ b/cypress/e2e/spectrogram.cy.js
@@ -83,7 +83,7 @@ xdescribe('WaveSurfer Spectrogram plugin tests', () => {
     })
   })
 
-  scales.forEach(scale => {
+  scales.forEach((scale) => {
     it(`should display correct frequency labels with 1kHz tone (${scale})`, () => {
       cy.visit('cypress/e2e/index.html')
       cy.window().then((win) => {
@@ -99,7 +99,7 @@ xdescribe('WaveSurfer Spectrogram plugin tests', () => {
                 scale: scale,
                 frequencyMin: 0,
                 frequencyMax: 4000,
-                splitChannels: false
+                splitChannels: false,
               }),
             ],
           })
diff --git a/examples/record-sync.js b/examples/record-sync.js
index f814fc362..d672fa9fa 100644
--- a/examples/record-sync.js
+++ b/examples/record-sync.js
@@ -21,7 +21,7 @@ const wavesurfer2 = WaveSurfer.create({
   barRadius: 2,
 })
 
-wavesurfer2.on('ready', function() {
+wavesurfer2.on('ready', function () {
   const createWaveSurfer = () => {
     // Destroy the previous wavesurfer instance
     if (wavesurfer) {
diff --git a/examples/regions.js b/examples/regions.js
index 198c10e5e..5beeccbf9 100644
--- a/examples/regions.js
+++ b/examples/regions.js
@@ -93,7 +93,7 @@ document.querySelector('input[type="checkbox"]').onclick = (e) => {
   regions.on('region-clicked', (region, e) => {
     e.stopPropagation() // prevent triggering a click on the waveform
     activeRegion = region
-    region.play()
+    region.play(true)
     region.setOptions({ color: randomColor() })
   })
   // Reset the active region when the user clicks anywhere in the waveform
diff --git a/src/plugins/regions.ts b/src/plugins/regions.ts
index fa99f55b2..55bdba9fa 100644
--- a/src/plugins/regions.ts
+++ b/src/plugins/regions.ts
@@ -38,7 +38,7 @@ export type RegionEvents = {
   /** When dragging or resizing is finished */
   'update-end': []
   /** On play */
-  play: []
+  play: [end?: number]
   /** On mouse click */
   click: [event: MouseEvent]
   /** Double click */
@@ -334,9 +334,9 @@ class SingleRegion extends EventEmitter<RegionEvents> implements Region {
     this.renderPosition()
   }
 
-  /** Play the region from the start */
-  public play() {
-    this.emit('play')
+  /** Play the region from the start, pass `true` to stop at region end */
+  public play(stopAtEnd?: boolean) {
+    this.emit('play', stopAtEnd ? this.end : undefined)
   }
 
   /** Set the HTML content of the region */
@@ -588,9 +588,8 @@ class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions
         this.emit('region-updated', region)
       }),
 
-      region.on('play', () => {
-        this.wavesurfer?.play()
-        this.wavesurfer?.setTime(region.start)
+      region.on('play', (end?: number) => {
+        this.wavesurfer?.play(region.start, end)
       }),
 
       region.on('click', (e) => {
diff --git a/src/plugins/spectrogram.ts b/src/plugins/spectrogram.ts
index 46658fb35..c09ea79ec 100644
--- a/src/plugins/spectrogram.ts
+++ b/src/plugins/spectrogram.ts
@@ -284,11 +284,7 @@ export type SpectrogramPluginOptions = {
    * - igray: Inverted gray scale.
    * - roseus: From https://github.com/dofuuz/roseus/blob/main/roseus/cmap/roseus.py
    */
-  colorMap?:
-  | number[][]
-  | 'gray'
-  | 'igray'
-  | 'roseus'
+  colorMap?: number[][] | 'gray' | 'igray' | 'roseus'
   /** Render a spectrogram for each channel independently when true. */
   splitChannels?: boolean
   /** URL with pre-computed spectrogram JSON data, the data must be a Uint8Array[][] **/
@@ -557,13 +553,7 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
         width,
         Math.round(bitmapHeight * (rMax1 - rMin)),
       ).then((bitmap) => {
-        spectrCc.drawImage(
-            bitmap,
-            0,
-            height * (c + 1 - rMax1 / rMax),
-            width,
-            height * rMax1 / rMax,
-        )
+        spectrCc.drawImage(bitmap, 0, height * (c + 1 - rMax1 / rMax), width, (height * rMax1) / rMax)
       })
     }
 
@@ -593,30 +583,38 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
     const filterMin = hzToScale(0)
     const filterMax = hzToScale(sampleRate / 2)
     const filterBank = Array.from({ length: numFilters }, () => Array(this.fftSamples / 2 + 1).fill(0))
-    const scale = (sampleRate / this.fftSamples)
+    const scale = sampleRate / this.fftSamples
     for (let i = 0; i < numFilters; i++) {
-        let hz = scaleToHz(filterMin + (i / numFilters) * (filterMax - filterMin))
-        let j = Math.floor(hz / scale)
-        let hzLow = j * scale
-        let hzHigh = (j + 1) * scale
-        let r = (hz - hzLow) / (hzHigh - hzLow)
-        filterBank[i][j] = 1 - r
-        filterBank[i][j + 1] = r
+      let hz = scaleToHz(filterMin + (i / numFilters) * (filterMax - filterMin))
+      let j = Math.floor(hz / scale)
+      let hzLow = j * scale
+      let hzHigh = (j + 1) * scale
+      let r = (hz - hzLow) / (hzHigh - hzLow)
+      filterBank[i][j] = 1 - r
+      filterBank[i][j + 1] = r
     }
     return filterBank
   }
 
-  private hzToMel(hz: number) { return 2595 * Math.log10(1 + hz / 700) }
+  private hzToMel(hz: number) {
+    return 2595 * Math.log10(1 + hz / 700)
+  }
 
-  private melToHz(mel: number) { return 700 * (Math.pow(10, mel / 2595) - 1) }
+  private melToHz(mel: number) {
+    return 700 * (Math.pow(10, mel / 2595) - 1)
+  }
 
   private createMelFilterBank(numMelFilters: number, sampleRate: number): number[][] {
     return this.createFilterBank(numMelFilters, sampleRate, this.hzToMel, this.melToHz)
   }
 
-  private hzToLog(hz: number) { return Math.log10(Math.max(1, hz)) }
+  private hzToLog(hz: number) {
+    return Math.log10(Math.max(1, hz))
+  }
 
-  private logToHz(log: number) { return Math.pow(10, log) }
+  private logToHz(log: number) {
+    return Math.pow(10, log)
+  }
 
   private createLogFilterBank(numLogFilters: number, sampleRate: number): number[][] {
     return this.createFilterBank(numLogFilters, sampleRate, this.hzToLog, this.logToHz)
@@ -708,7 +706,8 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
 
   private getFrequencies(buffer: AudioBuffer): Uint8Array[][] {
     const fftSamples = this.fftSamples
-    const channels = this.options.splitChannels ?? this.wavesurfer?.options.splitChannels ? buffer.numberOfChannels : 1
+    const channels =
+      (this.options.splitChannels ?? this.wavesurfer?.options.splitChannels) ? buffer.numberOfChannels : 1
 
     this.frequencyMax = this.frequencyMax || buffer.sampleRate / 2
 
@@ -732,16 +731,16 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
     switch (this.scale) {
       case 'mel':
         filterBank = this.createFilterBank(this.numMelFilters, sampleRate, this.hzToMel, this.melToHz)
-        break;
+        break
       case 'logarithmic':
         filterBank = this.createFilterBank(this.numLogFilters, sampleRate, this.hzToLog, this.logToHz)
-        break;
+        break
       case 'bark':
         filterBank = this.createFilterBank(this.numBarkFilters, sampleRate, this.hzToBark, this.barkToHz)
-        break;
+        break
       case 'erb':
         filterBank = this.createFilterBank(this.numErbFilters, sampleRate, this.hzToErb, this.erbToHz)
-        break;
+        break
     }
 
     for (let c = 0; c < channels; c++) {
@@ -753,7 +752,7 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
       while (currentOffset + fftSamples < channelData.length) {
         const segment = channelData.slice(currentOffset, currentOffset + fftSamples)
         const array = new Uint8Array(fftSamples / 2)
-        let spectrum = fft.calculateSpectrum(segment);
+        let spectrum = fft.calculateSpectrum(segment)
         if (filterBank) {
           spectrum = this.applyFilterBank(spectrum, filterBank)
         }
@@ -766,7 +765,7 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
           } else if (valueDB > -this.gainDB) {
             array[j] = 255
           } else {
-            array[j] = (valueDB + this.gainDB) / this.rangeDB * 255 + 256
+            array[j] = ((valueDB + this.gainDB) / this.rangeDB) * 255 + 256
           }
         }
         channelFreq.push(array)
@@ -789,10 +788,7 @@ class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramP
     return freq >= 1000 ? 'kHz' : 'Hz'
   }
 
-  private getLabelFrequency(
-    index: number,
-    labelIndex: number,
-  ) {
+  private getLabelFrequency(index: number, labelIndex: number) {
     const scaleMin = this.hzToScale(this.frequencyMin)
     const scaleMax = this.hzToScale(this.frequencyMax)
     return this.scaleToHz(scaleMin + (index / labelIndex) * (scaleMax - scaleMin))
diff --git a/src/renderer.ts b/src/renderer.ts
index 1368f5fb9..e6c0c274d 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -169,7 +169,8 @@ class Renderer extends EventEmitter<RendererEvents> {
     const div = document.createElement('div')
     const shadow = div.attachShadow({ mode: 'open' })
 
-    const cspNonce = this.options.cspNonce && typeof this.options.cspNonce === 'string' ? this.options.cspNonce.replace(/"/g, '') : '';
+    const cspNonce =
+      this.options.cspNonce && typeof this.options.cspNonce === 'string' ? this.options.cspNonce.replace(/"/g, '') : ''
 
     shadow.innerHTML = `
       <style${cspNonce ? ` nonce="${cspNonce}"` : ''}>
diff --git a/src/wavesurfer.ts b/src/wavesurfer.ts
index feb777e07..420a47fea 100644
--- a/src/wavesurfer.ts
+++ b/src/wavesurfer.ts
@@ -150,6 +150,7 @@ class WaveSurfer extends Player<WaveSurferEvents> {
   private timer: Timer
   private plugins: GenericPlugin[] = []
   private decodedData: AudioBuffer | null = null
+  private stopAtPosition: number | null = null
   protected subscriptions: Array<() => void> = []
   protected mediaSubscriptions: Array<() => void> = []
   protected abortController: AbortController | null = null
@@ -217,6 +218,11 @@ class WaveSurfer extends Player<WaveSurferEvents> {
           const currentTime = this.updateProgress()
           this.emit('timeupdate', currentTime)
           this.emit('audioprocess', currentTime)
+
+          // Pause audio when it reaches the stopAtPosition
+          if (this.stopAtPosition != null && this.isPlaying() && currentTime >= this.stopAtPosition) {
+            this.pause()
+          }
         }
       }),
     )
@@ -242,15 +248,18 @@ class WaveSurfer extends Player<WaveSurferEvents> {
       this.onMediaEvent('pause', () => {
         this.emit('pause')
         this.timer.stop()
+        this.stopAtPosition = null
       }),
 
       this.onMediaEvent('emptied', () => {
         this.timer.stop()
+        this.stopAtPosition = null
       }),
 
       this.onMediaEvent('ended', () => {
         this.emit('timeupdate', this.getDuration())
         this.emit('finish')
+        this.stopAtPosition = null
       }),
 
       this.onMediaEvent('seeking', () => {
@@ -259,6 +268,7 @@ class WaveSurfer extends Player<WaveSurferEvents> {
 
       this.onMediaEvent('error', (err) => {
         this.emit('error', (this.getMediaElement().error ?? new Error('Media error')) as Error)
+        this.stopAtPosition = null
       }),
     )
   }
@@ -360,10 +370,7 @@ class WaveSurfer extends Player<WaveSurferEvents> {
     }
     if (options.peaks && options.duration) {
       // Create new decoded data buffer from peaks and duration
-      this.decodedData = Decoder.createBuffer(
-        options.peaks,
-        options.duration
-      );
+      this.decodedData = Decoder.createBuffer(options.peaks, options.duration)
     }
     this.renderer.setOptions(this.options)
 
@@ -427,6 +434,7 @@ class WaveSurfer extends Player<WaveSurferEvents> {
     if (!this.options.media && this.isPlaying()) this.pause()
 
     this.decodedData = null
+    this.stopAtPosition = null
 
     // Fetch the entire audio as a blob if pre-decoded data is not provided
     if (!blob && !channelData) {
@@ -558,6 +566,7 @@ class WaveSurfer extends Player<WaveSurferEvents> {
 
   /** Jump to a specific time in the audio (in seconds) */
   public setTime(time: number) {
+    this.stopAtPosition = null
     super.setTime(time)
     this.updateProgress(time)
     this.emit('timeupdate', time)
@@ -569,6 +578,25 @@ class WaveSurfer extends Player<WaveSurferEvents> {
     this.setTime(time)
   }
 
+  /** Start playing the audio */
+  public async play(start?: number, end?: number): Promise<void> {
+    if (start != null) {
+      this.setTime(start)
+    }
+
+    const playPromise = super.play()
+
+    if (end != null) {
+      if (this.media instanceof WebAudioPlayer) {
+        this.media.stopAt(end)
+      } else {
+        this.stopAtPosition = end
+      }
+    }
+
+    return playPromise
+  }
+
   /** Play or pause the audio */
   public async playPause(): Promise<void> {
     return this.isPlaying() ? this.pause() : this.play()