0
# Regions Plugin
1
2
Visual overlays and markers for audio segments with interactive drag, resize, content support, and comprehensive event handling for creating time-based annotations and selections.
3
4
## Capabilities
5
6
### Regions Plugin Class
7
8
Create and manage a regions plugin instance for visual audio segment marking.
9
10
```typescript { .api }
11
/**
12
* Regions plugin for creating visual overlays on the waveform
13
*/
14
class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
15
/**
16
* Create a regions plugin instance
17
* @param options - Plugin configuration (currently undefined/no options)
18
* @returns New RegionsPlugin instance
19
*/
20
static create(options?: RegionsPluginOptions): RegionsPlugin;
21
22
/**
23
* Add a new region to the waveform
24
* @param params - Region configuration parameters
25
* @returns Created region instance
26
*/
27
addRegion(params: RegionParams): Region;
28
29
/**
30
* Remove all regions from the waveform
31
*/
32
clearRegions(): void;
33
34
/**
35
* Get all current regions
36
* @returns Array of all region instances
37
*/
38
getRegions(): Region[];
39
40
/**
41
* Enable drag selection to create regions by dragging on waveform
42
* @param options - Drag selection configuration
43
* @returns Function to disable drag selection
44
*/
45
enableDragSelection(options?: DragSelectionOptions): () => void;
46
}
47
48
type RegionsPluginOptions = undefined;
49
50
interface DragSelectionOptions {
51
color?: string;
52
[key: string]: any;
53
}
54
```
55
56
**Usage Examples:**
57
58
```typescript
59
import Regions from "wavesurfer.js/dist/plugins/regions.esm.js";
60
61
// Create and register regions plugin
62
const regions = Regions.create();
63
wavesurfer.registerPlugin(regions);
64
65
// Add basic region
66
const region1 = regions.addRegion({
67
start: 10,
68
end: 25,
69
color: "rgba(255, 0, 0, 0.3)",
70
content: "Verse 1",
71
});
72
73
// Add complex region with full options
74
const region2 = regions.addRegion({
75
id: "chorus-1",
76
start: 30,
77
end: 60,
78
drag: true,
79
resize: true,
80
color: "rgba(0, 255, 0, 0.3)",
81
content: "Chorus",
82
minLength: 5,
83
maxLength: 120,
84
});
85
86
// Enable drag to create regions
87
const disableDragSelect = regions.enableDragSelection({
88
color: "rgba(0, 0, 255, 0.2)",
89
});
90
91
// Get all regions
92
const allRegions = regions.getRegions();
93
console.log(`${allRegions.length} regions created`);
94
95
// Clear all regions
96
regions.clearRegions();
97
```
98
99
### Region Instance
100
101
Individual region objects with playback, modification, and content management capabilities.
102
103
```typescript { .api }
104
interface Region {
105
/** Unique region identifier */
106
id: string;
107
108
/** Start time in seconds */
109
start: number;
110
111
/** End time in seconds */
112
end: number;
113
114
/** Whether region can be dragged */
115
drag: boolean;
116
117
/** Whether region can be resized */
118
resize: boolean;
119
120
/** Region background color */
121
color: string;
122
123
/** Region content element */
124
content?: HTMLElement;
125
126
/** Minimum region length in seconds */
127
minLength: number;
128
129
/** Maximum region length in seconds */
130
maxLength: number;
131
132
/** Audio channel index */
133
channelIdx: number;
134
135
/**
136
* Play the region audio, optionally stopping at a specific time
137
* @param end - Optional end time, defaults to region end
138
* @returns Promise that resolves when playback starts
139
*/
140
play(end?: number): Promise<void>;
141
142
/**
143
* Update region parameters
144
* @param params - Partial region parameters to update
145
*/
146
setOptions(params: Partial<RegionParams>): void;
147
148
/**
149
* Remove the region from the waveform
150
*/
151
remove(): void;
152
153
/**
154
* Set or update region content (text or HTML element)
155
* @param content - Text string or HTML element
156
*/
157
setContent(content: string | HTMLElement): void;
158
}
159
```
160
161
**Usage Examples:**
162
163
```typescript
164
// Create region with all properties
165
const region = regions.addRegion({
166
id: "intro",
167
start: 0,
168
end: 15,
169
drag: true,
170
resize: true,
171
resizeStart: true,
172
resizeEnd: true,
173
color: "rgba(255, 255, 0, 0.4)",
174
content: "Introduction",
175
minLength: 2,
176
maxLength: 30,
177
channelIdx: 0,
178
contentEditable: true,
179
});
180
181
// Play region
182
await region.play(); // Play from region start to end
183
await region.play(12); // Play from region start to 12 seconds
184
185
// Update region properties
186
region.setOptions({
187
color: "rgba(255, 0, 255, 0.4)",
188
content: "Updated Introduction",
189
end: 18,
190
});
191
192
// Modify region content
193
region.setContent("New Title");
194
region.setContent(document.createElement("div")); // HTML element
195
196
// Remove region
197
region.remove();
198
199
// Access region properties
200
console.log(`Region "${region.id}" from ${region.start}s to ${region.end}s`);
201
console.log(`Duration: ${region.end - region.start}s`);
202
```
203
204
### Region Parameters
205
206
Configuration options for creating and updating regions.
207
208
```typescript { .api }
209
interface RegionParams {
210
/** The id of the region, defaults to random string */
211
id?: string;
212
213
/** The start position of the region (in seconds) - required */
214
start: number;
215
216
/** The end position of the region (in seconds), defaults to start + small duration */
217
end?: number;
218
219
/** Allow/disallow dragging the region, defaults to true */
220
drag?: boolean;
221
222
/** Allow/disallow resizing the region, defaults to true */
223
resize?: boolean;
224
225
/** Allow/disallow resizing the start of the region, defaults to resize value */
226
resizeStart?: boolean;
227
228
/** Allow/disallow resizing the end of the region, defaults to resize value */
229
resizeEnd?: boolean;
230
231
/** The color of the region (CSS color), defaults to random color */
232
color?: string;
233
234
/** Content string or HTML element */
235
content?: string | HTMLElement;
236
237
/** Min length when resizing (in seconds), defaults to 0 */
238
minLength?: number;
239
240
/** Max length when resizing (in seconds), defaults to Infinity */
241
maxLength?: number;
242
243
/** The index of the channel, defaults to 0 */
244
channelIdx?: number;
245
246
/** Allow/Disallow contenteditable property for content, defaults to false */
247
contentEditable?: boolean;
248
}
249
```
250
251
**Usage Examples:**
252
253
```typescript
254
// Minimal region (only start required)
255
const quickRegion = regions.addRegion({
256
start: 45,
257
// end defaults to start + small duration
258
// other properties use defaults
259
});
260
261
// Comprehensive region configuration
262
const detailedRegion = regions.addRegion({
263
id: "bridge-section",
264
start: 120,
265
end: 150,
266
drag: true,
267
resize: true,
268
resizeStart: false, // Can't resize start
269
resizeEnd: true, // Can resize end
270
color: "#ff6b6b",
271
content: "Bridge",
272
minLength: 10,
273
maxLength: 60,
274
channelIdx: 0,
275
contentEditable: true,
276
});
277
278
// Read-only region
279
const staticRegion = regions.addRegion({
280
id: "marker",
281
start: 75,
282
end: 75.1, // Very short duration for marker
283
drag: false,
284
resize: false,
285
color: "red",
286
content: "Important Moment",
287
});
288
289
// Multi-channel region
290
const stereoRegion = regions.addRegion({
291
start: 30,
292
end: 40,
293
channelIdx: 1, // Second channel
294
color: "rgba(0, 255, 255, 0.3)",
295
});
296
```
297
298
### Region Events
299
300
Events emitted by individual regions and the regions plugin for interaction tracking.
301
302
```typescript { .api }
303
interface RegionEvents {
304
/** Before the region is removed */
305
remove: [];
306
307
/** When the region's parameters are being updated */
308
update: [side?: 'start' | 'end'];
309
310
/** When dragging or resizing is finished */
311
'update-end': [];
312
313
/** On region play */
314
play: [end?: number];
315
316
/** On mouse click */
317
click: [event: MouseEvent];
318
319
/** Double click */
320
dblclick: [event: MouseEvent];
321
322
/** Mouse over */
323
over: [event: MouseEvent];
324
325
/** Mouse leave */
326
leave: [event: MouseEvent];
327
328
/** Content changed */
329
'content-changed': [];
330
}
331
332
interface RegionsPluginEvents extends BasePluginEvents {
333
/** When a new region is initialized but not rendered yet */
334
'region-initialized': [region: Region];
335
336
/** When a region is created and rendered */
337
'region-created': [region: Region];
338
339
/** When a region is being updated */
340
'region-update': [region: Region, side?: 'start' | 'end'];
341
342
/** When a region is done updating */
343
'region-updated': [region: Region];
344
345
/** When a region is removed */
346
'region-removed': [region: Region];
347
348
/** When a region is clicked */
349
'region-clicked': [region: Region, e: MouseEvent];
350
351
/** When a region is double-clicked */
352
'region-double-clicked': [region: Region, e: MouseEvent];
353
354
/** When playback enters a region */
355
'region-in': [region: Region];
356
357
/** When playback leaves a region */
358
'region-out': [region: Region];
359
360
/** When region content is changed */
361
'region-content-changed': [region: Region];
362
}
363
```
364
365
**Usage Examples:**
366
367
```typescript
368
// Listen to plugin-level events
369
regions.on("region-created", (region) => {
370
console.log(`New region created: ${region.id}`);
371
setupRegionControls(region);
372
});
373
374
regions.on("region-clicked", (region, event) => {
375
console.log(`Clicked region: ${region.id}`);
376
showRegionDetails(region);
377
});
378
379
regions.on("region-updated", (region) => {
380
console.log(`Region ${region.id} updated: ${region.start}s - ${region.end}s`);
381
saveRegionData(region);
382
});
383
384
regions.on("region-in", (region) => {
385
console.log(`Entered region: ${region.id}`);
386
highlightRegion(region);
387
});
388
389
regions.on("region-out", (region) => {
390
console.log(`Left region: ${region.id}`);
391
unhighlightRegion(region);
392
});
393
394
// Listen to individual region events
395
const region = regions.addRegion({ start: 10, end: 20, content: "Test" });
396
397
region.on("click", (event) => {
398
event.stopPropagation();
399
toggleRegionSelection(region);
400
});
401
402
region.on("dblclick", (event) => {
403
region.play();
404
});
405
406
region.on("update", (side) => {
407
console.log(`Updating region ${side || 'position'}`);
408
showUpdateIndicator();
409
});
410
411
region.on("update-end", () => {
412
console.log("Region update complete");
413
hideUpdateIndicator();
414
validateRegion(region);
415
});
416
417
region.on("content-changed", () => {
418
console.log("Region content changed");
419
autosaveContent(region);
420
});
421
```
422
423
### Advanced Region Features
424
425
Additional region functionality for complex use cases and applications.
426
427
**Usage Examples:**
428
429
```typescript
430
// Region templates for consistent styling
431
const regionTemplates = {
432
verse: {
433
color: "rgba(255, 0, 0, 0.3)",
434
drag: true,
435
resize: true,
436
minLength: 15,
437
maxLength: 45,
438
},
439
chorus: {
440
color: "rgba(0, 255, 0, 0.3)",
441
drag: true,
442
resize: true,
443
minLength: 20,
444
maxLength: 40,
445
},
446
bridge: {
447
color: "rgba(0, 0, 255, 0.3)",
448
drag: true,
449
resize: true,
450
minLength: 10,
451
maxLength: 30,
452
},
453
};
454
455
// Create templated regions
456
function createTemplatedRegion(type, start, end, content) {
457
return regions.addRegion({
458
...regionTemplates[type],
459
start,
460
end,
461
content,
462
id: `${type}-${Date.now()}`,
463
});
464
}
465
466
// Batch region operations
467
const songStructure = [
468
{ type: "verse", start: 0, end: 30, content: "Verse 1" },
469
{ type: "chorus", start: 30, end: 60, content: "Chorus 1" },
470
{ type: "verse", start: 60, end: 90, content: "Verse 2" },
471
{ type: "chorus", start: 90, end: 120, content: "Chorus 2" },
472
];
473
474
songStructure.forEach(section => {
475
createTemplatedRegion(section.type, section.start, section.end, section.content);
476
});
477
478
// Region export/import for persistence
479
function exportRegions() {
480
return regions.getRegions().map(region => ({
481
id: region.id,
482
start: region.start,
483
end: region.end,
484
color: region.color,
485
content: region.content?.textContent || "",
486
drag: region.drag,
487
resize: region.resize,
488
}));
489
}
490
491
function importRegions(regionData) {
492
regions.clearRegions();
493
regionData.forEach(data => {
494
regions.addRegion(data);
495
});
496
}
497
498
// Save/load regions
499
localStorage.setItem("savedRegions", JSON.stringify(exportRegions()));
500
const savedRegions = JSON.parse(localStorage.getItem("savedRegions") || "[]");
501
importRegions(savedRegions);
502
503
// Region validation and constraints
504
function validateRegionOverlap(newRegion) {
505
const existingRegions = regions.getRegions();
506
507
for (const region of existingRegions) {
508
if (region === newRegion) continue;
509
510
const hasOverlap =
511
(newRegion.start < region.end && newRegion.end > region.start);
512
513
if (hasOverlap) {
514
console.warn(`Region ${newRegion.id} overlaps with ${region.id}`);
515
return false;
516
}
517
}
518
return true;
519
}
520
521
// Auto-adjust regions to prevent overlap
522
regions.on("region-update", (region) => {
523
if (!validateRegionOverlap(region)) {
524
// Auto-adjust to prevent overlap
525
const conflicts = regions.getRegions().filter(r =>
526
r !== region && r.start < region.end && r.end > region.start
527
);
528
529
if (conflicts.length > 0) {
530
const nearestEnd = Math.min(...conflicts.map(r => r.end));
531
region.setOptions({ start: nearestEnd });
532
}
533
}
534
});
535
```