or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

audio-processing.mdaudio-recording.mdcore-waveform-control.mdevent-system.mdindex.mdplugin-system.mdregions-plugin.mdtimeline-navigation.mdvisual-customization.md

event-system.mddocs/

0

# Event System

1

2

Comprehensive event handling system for lifecycle, playback, user interaction, and waveform state changes with subscription management and error handling.

3

4

## Capabilities

5

6

### Event Subscription and Management

7

8

Subscribe to events and manage event listeners with unsubscribe functionality.

9

10

```typescript { .api }

11

interface WaveSurfer {

12

/**

13

* Subscribe to an event with callback function

14

* @param event - Event name to listen for

15

* @param listener - Callback function to execute

16

* @param options - Optional subscription options

17

* @returns Unsubscribe function

18

*/

19

on<EventName extends keyof WaveSurferEvents>(

20

event: EventName,

21

listener: (...args: WaveSurferEvents[EventName]) => void,

22

options?: { once?: boolean }

23

): () => void;

24

25

/**

26

* Subscribe to an event only once

27

* @param event - Event name to listen for

28

* @param listener - Callback function to execute

29

* @returns Unsubscribe function

30

*/

31

once<EventName extends keyof WaveSurferEvents>(

32

event: EventName,

33

listener: (...args: WaveSurferEvents[EventName]) => void

34

): () => void;

35

36

/**

37

* Unsubscribe from an event

38

* @param event - Event name to unsubscribe from

39

* @param listener - Specific callback function to remove

40

*/

41

un<EventName extends keyof WaveSurferEvents>(

42

event: EventName,

43

listener: (...args: WaveSurferEvents[EventName]) => void

44

): void;

45

46

/**

47

* Clear all event listeners

48

*/

49

unAll(): void;

50

}

51

```

52

53

**Usage Examples:**

54

55

```typescript

56

// Basic event subscription

57

const unsubscribe = wavesurfer.on("ready", (duration) => {

58

console.log(`Audio loaded, duration: ${duration} seconds`);

59

});

60

61

// One-time event subscription

62

wavesurfer.once("play", () => {

63

console.log("First time playing!");

64

});

65

66

// Unsubscribe from specific event

67

const playHandler = () => console.log("Playing");

68

wavesurfer.on("play", playHandler);

69

wavesurfer.un("play", playHandler); // Remove specific handler

70

71

// Unsubscribe using returned function

72

const unsub = wavesurfer.on("pause", () => console.log("Paused"));

73

unsub(); // Remove this subscription

74

75

// Clear all event listeners

76

wavesurfer.unAll();

77

```

78

79

### Lifecycle Events

80

81

Events related to WaveSurfer initialization, loading, and destruction.

82

83

```typescript { .api }

84

interface WaveSurferEvents {

85

/** Fired after WaveSurfer instance is created and initialized */

86

init: [];

87

88

/** Fired when the audio is fully loaded and ready to play */

89

ready: [duration: number];

90

91

/** Fired just before the WaveSurfer instance is destroyed */

92

destroy: [];

93

}

94

```

95

96

**Usage Examples:**

97

98

```typescript

99

// Initialize event - setup UI

100

wavesurfer.on("init", () => {

101

console.log("WaveSurfer initialized");

102

document.getElementById("loading").style.display = "none";

103

});

104

105

// Ready event - enable playback controls

106

wavesurfer.on("ready", (duration) => {

107

console.log(`Audio ready, duration: ${duration.toFixed(2)}s`);

108

document.getElementById("play-button").disabled = false;

109

document.getElementById("duration").textContent = formatTime(duration);

110

});

111

112

// Destroy event - cleanup

113

wavesurfer.on("destroy", () => {

114

console.log("WaveSurfer destroyed, cleaning up");

115

document.getElementById("controls").innerHTML = "";

116

});

117

118

function formatTime(seconds) {

119

const mins = Math.floor(seconds / 60);

120

const secs = Math.floor(seconds % 60).toString().padStart(2, "0");

121

return `${mins}:${secs}`;

122

}

123

```

124

125

### Audio Loading Events

126

127

Events that track the audio loading and decoding process.

128

129

```typescript { .api }

130

interface WaveSurferEvents {

131

/** Fired when audio loading starts */

132

load: [url: string];

133

134

/** Fired during audio loading with progress percentage */

135

loading: [percent: number];

136

137

/** Fired when the audio has been decoded into waveform data */

138

decode: [duration: number];

139

}

140

```

141

142

**Usage Examples:**

143

144

```typescript

145

// Track loading process

146

wavesurfer.on("load", (url) => {

147

console.log(`Loading audio from: ${url}`);

148

showLoadingSpinner();

149

});

150

151

wavesurfer.on("loading", (percent) => {

152

console.log(`Loading progress: ${percent}%`);

153

updateProgressBar(percent);

154

});

155

156

wavesurfer.on("decode", (duration) => {

157

console.log(`Audio decoded, duration: ${duration}s`);

158

hideLoadingSpinner();

159

});

160

161

function showLoadingSpinner() {

162

document.getElementById("spinner").style.display = "block";

163

}

164

165

function updateProgressBar(percent) {

166

document.getElementById("progress-bar").style.width = `${percent}%`;

167

}

168

169

function hideLoadingSpinner() {

170

document.getElementById("spinner").style.display = "none";

171

document.getElementById("progress-bar").style.width = "0%";

172

}

173

```

174

175

### Playback Events

176

177

Events related to audio playback state changes and position updates.

178

179

```typescript { .api }

180

interface WaveSurferEvents {

181

/** Fired when audio playback starts */

182

play: [];

183

184

/** Fired when audio playback is paused */

185

pause: [];

186

187

/** Fired when audio playback finishes naturally */

188

finish: [];

189

190

/** Fired continuously during playback with current time */

191

timeupdate: [currentTime: number];

192

193

/** Alias for timeupdate, but only fired when audio is playing */

194

audioprocess: [currentTime: number];

195

196

/** Fired when user seeks to a new position */

197

seeking: [currentTime: number];

198

}

199

```

200

201

**Usage Examples:**

202

203

```typescript

204

// Playback state management

205

wavesurfer.on("play", () => {

206

document.getElementById("play-button").textContent = "Pause";

207

document.body.classList.add("playing");

208

});

209

210

wavesurfer.on("pause", () => {

211

document.getElementById("play-button").textContent = "Play";

212

document.body.classList.remove("playing");

213

});

214

215

wavesurfer.on("finish", () => {

216

document.getElementById("play-button").textContent = "Play";

217

document.body.classList.remove("playing");

218

console.log("Playback finished");

219

});

220

221

// Time tracking and display

222

wavesurfer.on("timeupdate", (currentTime) => {

223

const duration = wavesurfer.getDuration();

224

const progress = (currentTime / duration) * 100;

225

226

document.getElementById("current-time").textContent = formatTime(currentTime);

227

document.getElementById("progress").style.width = `${progress}%`;

228

});

229

230

// Seeking feedback

231

wavesurfer.on("seeking", (currentTime) => {

232

console.log(`Seeking to: ${formatTime(currentTime)}`);

233

showSeekingIndicator();

234

setTimeout(hideSeekingIndicator, 500);

235

});

236

```

237

238

### User Interaction Events

239

240

Events triggered by user interactions with the waveform.

241

242

```typescript { .api }

243

interface WaveSurferEvents {

244

/** Fired when user interacts with the waveform (clicks or drags) */

245

interaction: [newTime: number];

246

247

/** Fired when user clicks on the waveform */

248

click: [relativeX: number, relativeY: number];

249

250

/** Fired when user double-clicks on the waveform */

251

dblclick: [relativeX: number, relativeY: number];

252

253

/** Fired when user drags the cursor */

254

drag: [relativeX: number];

255

256

/** Fired when user starts dragging the cursor */

257

dragstart: [relativeX: number];

258

259

/** Fired when user ends dragging the cursor */

260

dragend: [relativeX: number];

261

}

262

```

263

264

**Usage Examples:**

265

266

```typescript

267

// General interaction tracking

268

wavesurfer.on("interaction", (newTime) => {

269

console.log(`User interacted, jumped to: ${formatTime(newTime)}`);

270

showInteractionFeedback();

271

});

272

273

// Click handling

274

wavesurfer.on("click", (relativeX, relativeY) => {

275

const duration = wavesurfer.getDuration();

276

const clickTime = relativeX * duration;

277

console.log(`Clicked at ${formatTime(clickTime)} (${(relativeX * 100).toFixed(1)}%)`);

278

});

279

280

// Double-click for special actions

281

wavesurfer.on("dblclick", (relativeX, relativeY) => {

282

const duration = wavesurfer.getDuration();

283

const clickTime = relativeX * duration;

284

console.log(`Double-clicked at ${formatTime(clickTime)}`);

285

286

// Example: Toggle playback or create marker

287

if (wavesurfer.isPlaying()) {

288

wavesurfer.pause();

289

} else {

290

wavesurfer.play();

291

}

292

});

293

294

// Drag interaction feedback

295

wavesurfer.on("dragstart", (relativeX) => {

296

document.body.classList.add("dragging");

297

console.log("Started dragging");

298

});

299

300

wavesurfer.on("drag", (relativeX) => {

301

const duration = wavesurfer.getDuration();

302

const dragTime = relativeX * duration;

303

document.getElementById("drag-time").textContent = formatTime(dragTime);

304

});

305

306

wavesurfer.on("dragend", (relativeX) => {

307

document.body.classList.remove("dragging");

308

document.getElementById("drag-time").textContent = "";

309

console.log("Finished dragging");

310

});

311

```

312

313

### Display and Rendering Events

314

315

Events related to waveform visualization and rendering updates.

316

317

```typescript { .api }

318

interface WaveSurferEvents {

319

/** Fired when the visible waveform is redrawn */

320

redraw: [];

321

322

/** Fired when all audio channel chunks have finished drawing */

323

redrawcomplete: [];

324

325

/** Fired when the waveform is scrolled/panned */

326

scroll: [visibleStartTime: number, visibleEndTime: number, scrollLeft: number, scrollRight: number];

327

328

/** Fired when the zoom level changes */

329

zoom: [minPxPerSec: number];

330

}

331

```

332

333

**Usage Examples:**

334

335

```typescript

336

// Rendering progress tracking

337

wavesurfer.on("redraw", () => {

338

console.log("Waveform redrawing...");

339

showRenderingIndicator();

340

});

341

342

wavesurfer.on("redrawcomplete", () => {

343

console.log("Waveform rendering complete");

344

hideRenderingIndicator();

345

});

346

347

// Scroll tracking for custom controls

348

wavesurfer.on("scroll", (startTime, endTime, scrollLeft, scrollRight) => {

349

console.log(`Viewing: ${formatTime(startTime)} - ${formatTime(endTime)}`);

350

updateScrollIndicator(scrollLeft, scrollRight);

351

});

352

353

// Zoom level updates

354

wavesurfer.on("zoom", (minPxPerSec) => {

355

console.log(`Zoom level: ${minPxPerSec} pixels/second`);

356

document.getElementById("zoom-level").textContent = `${minPxPerSec}px/s`;

357

updateZoomControls(minPxPerSec);

358

});

359

360

function updateScrollIndicator(scrollLeft, scrollRight) {

361

const totalWidth = wavesurfer.getWidth();

362

const viewportStart = (scrollLeft / totalWidth) * 100;

363

const viewportEnd = (scrollRight / totalWidth) * 100;

364

365

document.getElementById("scroll-indicator").style.left = `${viewportStart}%`;

366

document.getElementById("scroll-indicator").style.width = `${viewportEnd - viewportStart}%`;

367

}

368

```

369

370

### Error Handling

371

372

Handle errors that occur during audio loading, decoding, or playback.

373

374

```typescript { .api }

375

interface WaveSurferEvents {

376

/** Fired when an error occurs during loading, decoding, or playback */

377

error: [error: Error];

378

}

379

```

380

381

**Usage Examples:**

382

383

```typescript

384

// Comprehensive error handling

385

wavesurfer.on("error", (error) => {

386

console.error("WaveSurfer error:", error);

387

388

// Handle different error types

389

if (error.message.includes("fetch")) {

390

showError("Failed to load audio file. Please check the URL and try again.");

391

} else if (error.message.includes("decode")) {

392

showError("Unable to decode audio file. The file may be corrupted or in an unsupported format.");

393

} else if (error.message.includes("Media")) {

394

showError("Media playback error. Please try refreshing the page.");

395

} else {

396

showError(`An error occurred: ${error.message}`);

397

}

398

399

// Reset UI state

400

hideLoadingSpinner();

401

document.getElementById("play-button").disabled = true;

402

});

403

404

function showError(message) {

405

const errorDiv = document.getElementById("error-message");

406

errorDiv.textContent = message;

407

errorDiv.style.display = "block";

408

409

// Auto-hide after 5 seconds

410

setTimeout(() => {

411

errorDiv.style.display = "none";

412

}, 5000);

413

}

414

415

// Retry mechanism

416

let retryCount = 0;

417

const maxRetries = 3;

418

419

wavesurfer.on("error", async (error) => {

420

if (retryCount < maxRetries) {

421

retryCount++;

422

console.log(`Retrying... (${retryCount}/${maxRetries})`);

423

424

// Wait before retry

425

await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));

426

427

try {

428

await wavesurfer.load(originalUrl);

429

retryCount = 0; // Reset on success

430

} catch (retryError) {

431

console.error(`Retry ${retryCount} failed:`, retryError);

432

}

433

} else {

434

console.error("Max retries exceeded, giving up");

435

showError("Failed to load audio after multiple attempts.");

436

}

437

});

438

```

439

440

### Event Chaining and Complex Workflows

441

442

Combine multiple events for complex application workflows.

443

444

```typescript { .api }

445

// Events can be chained and combined for complex behaviors

446

```

447

448

**Usage Examples:**

449

450

```typescript

451

// Audio loading workflow

452

let loadingStartTime;

453

454

wavesurfer.on("load", (url) => {

455

loadingStartTime = Date.now();

456

console.log(`Starting to load: ${url}`);

457

});

458

459

wavesurfer.on("decode", () => {

460

const loadingTime = Date.now() - loadingStartTime;

461

console.log(`Audio decoded in ${loadingTime}ms`);

462

});

463

464

wavesurfer.on("ready", (duration) => {

465

const totalTime = Date.now() - loadingStartTime;

466

console.log(`Audio ready in ${totalTime}ms, duration: ${duration}s`);

467

468

// Auto-play if requested

469

if (shouldAutoPlay) {

470

wavesurfer.play();

471

}

472

});

473

474

// Playback session tracking

475

let playbackSessions = [];

476

let sessionStart;

477

478

wavesurfer.on("play", () => {

479

sessionStart = {

480

startTime: wavesurfer.getCurrentTime(),

481

timestamp: Date.now(),

482

};

483

});

484

485

wavesurfer.on("pause", () => {

486

if (sessionStart) {

487

playbackSessions.push({

488

...sessionStart,

489

endTime: wavesurfer.getCurrentTime(),

490

duration: Date.now() - sessionStart.timestamp,

491

});

492

}

493

});

494

495

wavesurfer.on("finish", () => {

496

console.log(`Total playback sessions: ${playbackSessions.length}`);

497

const totalListeningTime = playbackSessions.reduce(

498

(sum, session) => sum + (session.endTime - session.startTime), 0

499

);

500

console.log(`Total listening time: ${formatTime(totalListeningTime)}`);

501

});

502

```