CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-wavesurfer-js

Interactive audio waveform rendering and playback library for web applications

Pending
Overview
Eval results
Files

regions-plugin.mddocs/

Regions Plugin

Visual overlays and markers for audio segments with interactive drag, resize, content support, and comprehensive event handling for creating time-based annotations and selections.

Capabilities

Regions Plugin Class

Create and manage a regions plugin instance for visual audio segment marking.

/**
 * Regions plugin for creating visual overlays on the waveform
 */
class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
  /**
   * Create a regions plugin instance
   * @param options - Plugin configuration (currently undefined/no options)
   * @returns New RegionsPlugin instance
   */
  static create(options?: RegionsPluginOptions): RegionsPlugin;
  
  /**
   * Add a new region to the waveform
   * @param params - Region configuration parameters
   * @returns Created region instance
   */
  addRegion(params: RegionParams): Region;
  
  /**
   * Remove all regions from the waveform
   */
  clearRegions(): void;
  
  /**
   * Get all current regions
   * @returns Array of all region instances
   */
  getRegions(): Region[];
  
  /**
   * Enable drag selection to create regions by dragging on waveform
   * @param options - Drag selection configuration
   * @returns Function to disable drag selection
   */
  enableDragSelection(options?: DragSelectionOptions): () => void;
}

type RegionsPluginOptions = undefined;

interface DragSelectionOptions {
  color?: string;
  [key: string]: any;
}

Usage Examples:

import Regions from "wavesurfer.js/dist/plugins/regions.esm.js";

// Create and register regions plugin
const regions = Regions.create();
wavesurfer.registerPlugin(regions);

// Add basic region
const region1 = regions.addRegion({
  start: 10,
  end: 25,
  color: "rgba(255, 0, 0, 0.3)",
  content: "Verse 1",
});

// Add complex region with full options
const region2 = regions.addRegion({
  id: "chorus-1",
  start: 30,
  end: 60,
  drag: true,
  resize: true,
  color: "rgba(0, 255, 0, 0.3)",
  content: "Chorus",
  minLength: 5,
  maxLength: 120,
});

// Enable drag to create regions
const disableDragSelect = regions.enableDragSelection({
  color: "rgba(0, 0, 255, 0.2)",
});

// Get all regions
const allRegions = regions.getRegions();
console.log(`${allRegions.length} regions created`);

// Clear all regions
regions.clearRegions();

Region Instance

Individual region objects with playback, modification, and content management capabilities.

interface Region {
  /** Unique region identifier */
  id: string;
  
  /** Start time in seconds */
  start: number;
  
  /** End time in seconds */
  end: number;
  
  /** Whether region can be dragged */
  drag: boolean;
  
  /** Whether region can be resized */
  resize: boolean;
  
  /** Region background color */
  color: string;
  
  /** Region content element */
  content?: HTMLElement;
  
  /** Minimum region length in seconds */
  minLength: number;
  
  /** Maximum region length in seconds */
  maxLength: number;
  
  /** Audio channel index */
  channelIdx: number;
  
  /**
   * Play the region audio, optionally stopping at a specific time
   * @param end - Optional end time, defaults to region end
   * @returns Promise that resolves when playback starts
   */
  play(end?: number): Promise<void>;
  
  /**
   * Update region parameters
   * @param params - Partial region parameters to update
   */
  setOptions(params: Partial<RegionParams>): void;
  
  /**
   * Remove the region from the waveform
   */
  remove(): void;
  
  /**
   * Set or update region content (text or HTML element)
   * @param content - Text string or HTML element
   */
  setContent(content: string | HTMLElement): void;
}

Usage Examples:

// Create region with all properties
const region = regions.addRegion({
  id: "intro",
  start: 0,
  end: 15,
  drag: true,
  resize: true,
  resizeStart: true,
  resizeEnd: true,
  color: "rgba(255, 255, 0, 0.4)",
  content: "Introduction",
  minLength: 2,
  maxLength: 30,
  channelIdx: 0,
  contentEditable: true,
});

// Play region
await region.play(); // Play from region start to end
await region.play(12); // Play from region start to 12 seconds

// Update region properties
region.setOptions({
  color: "rgba(255, 0, 255, 0.4)",
  content: "Updated Introduction",
  end: 18,
});

// Modify region content
region.setContent("New Title");
region.setContent(document.createElement("div")); // HTML element

// Remove region
region.remove();

// Access region properties
console.log(`Region "${region.id}" from ${region.start}s to ${region.end}s`);
console.log(`Duration: ${region.end - region.start}s`);

Region Parameters

Configuration options for creating and updating regions.

interface RegionParams {
  /** The id of the region, defaults to random string */
  id?: string;
  
  /** The start position of the region (in seconds) - required */
  start: number;
  
  /** The end position of the region (in seconds), defaults to start + small duration */
  end?: number;
  
  /** Allow/disallow dragging the region, defaults to true */
  drag?: boolean;
  
  /** Allow/disallow resizing the region, defaults to true */
  resize?: boolean;
  
  /** Allow/disallow resizing the start of the region, defaults to resize value */
  resizeStart?: boolean;
  
  /** Allow/disallow resizing the end of the region, defaults to resize value */
  resizeEnd?: boolean;
  
  /** The color of the region (CSS color), defaults to random color */
  color?: string;
  
  /** Content string or HTML element */
  content?: string | HTMLElement;
  
  /** Min length when resizing (in seconds), defaults to 0 */
  minLength?: number;
  
  /** Max length when resizing (in seconds), defaults to Infinity */
  maxLength?: number;
  
  /** The index of the channel, defaults to 0 */
  channelIdx?: number;
  
  /** Allow/Disallow contenteditable property for content, defaults to false */
  contentEditable?: boolean;
}

Usage Examples:

// Minimal region (only start required)
const quickRegion = regions.addRegion({
  start: 45,
  // end defaults to start + small duration
  // other properties use defaults
});

// Comprehensive region configuration
const detailedRegion = regions.addRegion({
  id: "bridge-section",
  start: 120,
  end: 150,
  drag: true,
  resize: true,
  resizeStart: false, // Can't resize start
  resizeEnd: true,    // Can resize end
  color: "#ff6b6b",
  content: "Bridge",
  minLength: 10,
  maxLength: 60,
  channelIdx: 0,
  contentEditable: true,
});

// Read-only region
const staticRegion = regions.addRegion({
  id: "marker",
  start: 75,
  end: 75.1, // Very short duration for marker
  drag: false,
  resize: false,
  color: "red",
  content: "Important Moment",
});

// Multi-channel region
const stereoRegion = regions.addRegion({
  start: 30,
  end: 40,
  channelIdx: 1, // Second channel
  color: "rgba(0, 255, 255, 0.3)",
});

Region Events

Events emitted by individual regions and the regions plugin for interaction tracking.

interface RegionEvents {
  /** Before the region is removed */
  remove: [];
  
  /** When the region's parameters are being updated */
  update: [side?: 'start' | 'end'];
  
  /** When dragging or resizing is finished */
  'update-end': [];
  
  /** On region play */
  play: [end?: number];
  
  /** On mouse click */
  click: [event: MouseEvent];
  
  /** Double click */
  dblclick: [event: MouseEvent];
  
  /** Mouse over */
  over: [event: MouseEvent];
  
  /** Mouse leave */ 
  leave: [event: MouseEvent];
  
  /** Content changed */
  'content-changed': [];
}

interface RegionsPluginEvents extends BasePluginEvents {
  /** When a new region is initialized but not rendered yet */
  'region-initialized': [region: Region];
  
  /** When a region is created and rendered */
  'region-created': [region: Region];
  
  /** When a region is being updated */
  'region-update': [region: Region, side?: 'start' | 'end'];
  
  /** When a region is done updating */
  'region-updated': [region: Region];
  
  /** When a region is removed */
  'region-removed': [region: Region];
  
  /** When a region is clicked */
  'region-clicked': [region: Region, e: MouseEvent];
  
  /** When a region is double-clicked */
  'region-double-clicked': [region: Region, e: MouseEvent];
  
  /** When playback enters a region */
  'region-in': [region: Region];
  
  /** When playback leaves a region */
  'region-out': [region: Region];
  
  /** When region content is changed */
  'region-content-changed': [region: Region];
}

Usage Examples:

// Listen to plugin-level events
regions.on("region-created", (region) => {
  console.log(`New region created: ${region.id}`);
  setupRegionControls(region);
});

regions.on("region-clicked", (region, event) => {
  console.log(`Clicked region: ${region.id}`);
  showRegionDetails(region);
});

regions.on("region-updated", (region) => {
  console.log(`Region ${region.id} updated: ${region.start}s - ${region.end}s`);
  saveRegionData(region);
});

regions.on("region-in", (region) => {
  console.log(`Entered region: ${region.id}`);
  highlightRegion(region);
});

regions.on("region-out", (region) => {
  console.log(`Left region: ${region.id}`);
  unhighlightRegion(region);
});

// Listen to individual region events
const region = regions.addRegion({ start: 10, end: 20, content: "Test" });

region.on("click", (event) => {
  event.stopPropagation();
  toggleRegionSelection(region);
});

region.on("dblclick", (event) => {
  region.play();
});

region.on("update", (side) => {
  console.log(`Updating region ${side || 'position'}`);
  showUpdateIndicator();
});

region.on("update-end", () => {
  console.log("Region update complete");
  hideUpdateIndicator();
  validateRegion(region);
});

region.on("content-changed", () => {
  console.log("Region content changed");
  autosaveContent(region);
});

Advanced Region Features

Additional region functionality for complex use cases and applications.

Usage Examples:

// Region templates for consistent styling
const regionTemplates = {
  verse: {
    color: "rgba(255, 0, 0, 0.3)",
    drag: true,
    resize: true,
    minLength: 15,
    maxLength: 45,
  },
  chorus: {
    color: "rgba(0, 255, 0, 0.3)",
    drag: true,
    resize: true,
    minLength: 20,
    maxLength: 40,
  },
  bridge: {
    color: "rgba(0, 0, 255, 0.3)",
    drag: true,
    resize: true,
    minLength: 10,
    maxLength: 30,
  },
};

// Create templated regions
function createTemplatedRegion(type, start, end, content) {
  return regions.addRegion({
    ...regionTemplates[type],
    start,
    end,
    content,
    id: `${type}-${Date.now()}`,
  });
}

// Batch region operations
const songStructure = [
  { type: "verse", start: 0, end: 30, content: "Verse 1" },
  { type: "chorus", start: 30, end: 60, content: "Chorus 1" },
  { type: "verse", start: 60, end: 90, content: "Verse 2" },
  { type: "chorus", start: 90, end: 120, content: "Chorus 2" },
];

songStructure.forEach(section => {
  createTemplatedRegion(section.type, section.start, section.end, section.content);
});

// Region export/import for persistence
function exportRegions() {
  return regions.getRegions().map(region => ({
    id: region.id,
    start: region.start,
    end: region.end,
    color: region.color,
    content: region.content?.textContent || "",
    drag: region.drag,
    resize: region.resize,
  }));
}

function importRegions(regionData) {
  regions.clearRegions();
  regionData.forEach(data => {
    regions.addRegion(data);
  });
}

// Save/load regions
localStorage.setItem("savedRegions", JSON.stringify(exportRegions()));
const savedRegions = JSON.parse(localStorage.getItem("savedRegions") || "[]");
importRegions(savedRegions);

// Region validation and constraints
function validateRegionOverlap(newRegion) {
  const existingRegions = regions.getRegions();
  
  for (const region of existingRegions) {
    if (region === newRegion) continue;
    
    const hasOverlap = 
      (newRegion.start < region.end && newRegion.end > region.start);
    
    if (hasOverlap) {
      console.warn(`Region ${newRegion.id} overlaps with ${region.id}`);
      return false;
    }
  }
  return true;
}

// Auto-adjust regions to prevent overlap
regions.on("region-update", (region) => {
  if (!validateRegionOverlap(region)) {
    // Auto-adjust to prevent overlap
    const conflicts = regions.getRegions().filter(r => 
      r !== region && r.start < region.end && r.end > region.start
    );
    
    if (conflicts.length > 0) {
      const nearestEnd = Math.min(...conflicts.map(r => r.end));
      region.setOptions({ start: nearestEnd });
    }
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-wavesurfer-js

docs

audio-processing.md

audio-recording.md

core-waveform-control.md

event-system.md

index.md

plugin-system.md

regions-plugin.md

timeline-navigation.md

visual-customization.md

tile.json