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

timeline-navigation.mddocs/

0

# Timeline and Navigation

1

2

Timeline display, minimap navigation, zoom controls, and hover interactions for enhanced waveform navigation and user experience with time-based visualization aids.

3

4

## Capabilities

5

6

### Timeline Plugin

7

8

Display time labels, notches, and markers below or above the waveform for precise time navigation.

9

10

```typescript { .api }

11

/**

12

* Timeline plugin for displaying time labels and notches

13

*/

14

class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {

15

/**

16

* Create a timeline plugin instance

17

* @param options - Timeline configuration options

18

* @returns New TimelinePlugin instance

19

*/

20

static create(options?: TimelinePluginOptions): TimelinePlugin;

21

}

22

23

interface TimelinePluginOptions {

24

/** Height of timeline in pixels, defaults to 20 */

25

height?: number;

26

27

/** Position relative to waveform, defaults to 'afterend' */

28

insertPosition?: 'beforebegin' | 'afterend';

29

30

/** Time interval between major labels in seconds */

31

timeInterval?: number;

32

33

/** Interval between primary labels (larger text) */

34

primaryLabelInterval?: number;

35

36

/** Interval between secondary labels (smaller text) */

37

secondaryLabelInterval?: number;

38

39

/** CSS styles for the timeline container */

40

style?: Partial<CSSStyleDeclaration>;

41

42

/** Format function for time labels */

43

formatTimeCallback?: (seconds: number) => string;

44

45

/** Whether to display labels, defaults to true */

46

displayLabels?: boolean;

47

}

48

49

interface TimelinePluginEvents extends BasePluginEvents {

50

/** When timeline is rendered */

51

'timeline-ready': [];

52

}

53

```

54

55

**Usage Examples:**

56

57

```typescript

58

import Timeline from "wavesurfer.js/dist/plugins/timeline.esm.js";

59

60

// Basic timeline

61

const timeline = Timeline.create({

62

height: 30,

63

timeInterval: 10, // Major marks every 10 seconds

64

primaryLabelInterval: 30, // Large labels every 30 seconds

65

secondaryLabelInterval: 10, // Small labels every 10 seconds

66

});

67

68

wavesurfer.registerPlugin(timeline);

69

70

// Customized timeline appearance

71

const styledTimeline = Timeline.create({

72

height: 40,

73

insertPosition: 'beforebegin', // Above waveform

74

style: {

75

backgroundColor: '#f0f0f0',

76

borderTop: '1px solid #ccc',

77

color: '#333',

78

fontSize: '12px',

79

fontFamily: 'monospace',

80

},

81

formatTimeCallback: (seconds) => {

82

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

83

const secs = Math.floor(seconds % 60);

84

return `${mins}:${secs.toString().padStart(2, '0')}`;

85

},

86

});

87

88

// Minimal timeline for mobile

89

const mobileTimeline = Timeline.create({

90

height: 20,

91

primaryLabelInterval: 60, // Less frequent labels

92

secondaryLabelInterval: 30,

93

displayLabels: true,

94

style: {

95

fontSize: '10px',

96

},

97

});

98

99

// Timeline event handling

100

timeline.on('timeline-ready', () => {

101

console.log('Timeline rendered');

102

});

103

```

104

105

### Minimap Plugin

106

107

Provide a small overview waveform that serves as a scrollbar and navigation aid for the main waveform.

108

109

```typescript { .api }

110

/**

111

* Minimap plugin for waveform overview and navigation

112

*/

113

class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {

114

/**

115

* Create a minimap plugin instance

116

* @param options - Minimap configuration options

117

* @returns New MinimapPlugin instance

118

*/

119

static create(options?: MinimapPluginOptions): MinimapPlugin;

120

}

121

122

interface MinimapPluginOptions {

123

/** Height of minimap in pixels, defaults to 50 */

124

height?: number;

125

126

/** Position relative to waveform container */

127

insertPosition?: string;

128

129

/** Wave color for minimap, defaults to main waveform color */

130

waveColor?: string;

131

132

/** Progress color for minimap, defaults to main progress color */

133

progressColor?: string;

134

135

/** Background color for minimap */

136

backgroundColor?: string;

137

138

/** Border styling for minimap */

139

borderColor?: string;

140

141

/** Opacity of the minimap, defaults to 1 */

142

opacity?: number;

143

144

/** Whether minimap is interactive, defaults to true */

145

interact?: boolean;

146

}

147

148

interface MinimapPluginEvents extends BasePluginEvents {

149

/** When minimap is ready */

150

'minimap-ready': [];

151

152

/** When minimap is clicked */

153

'minimap-click': [progress: number];

154

}

155

```

156

157

**Usage Examples:**

158

159

```typescript

160

import Minimap from "wavesurfer.js/dist/plugins/minimap.esm.js";

161

162

// Basic minimap

163

const minimap = Minimap.create({

164

height: 60,

165

waveColor: '#ddd',

166

progressColor: '#999',

167

});

168

169

wavesurfer.registerPlugin(minimap);

170

171

// Styled minimap

172

const styledMinimap = Minimap.create({

173

height: 80,

174

waveColor: 'rgba(255, 255, 255, 0.8)',

175

progressColor: 'rgba(255, 255, 255, 1)',

176

backgroundColor: '#1a1a1a',

177

borderColor: '#333',

178

opacity: 0.9,

179

});

180

181

// Interactive minimap with events

182

const interactiveMinimap = Minimap.create({

183

height: 50,

184

interact: true,

185

});

186

187

interactiveMinimap.on('minimap-click', (progress) => {

188

console.log(`Minimap clicked at ${(progress * 100).toFixed(1)}%`);

189

// Main waveform automatically seeks to clicked position

190

});

191

192

interactiveMinimap.on('minimap-ready', () => {

193

console.log('Minimap ready for interaction');

194

});

195

```

196

197

### Zoom Plugin

198

199

Provide zoom controls and functionality for detailed waveform examination.

200

201

```typescript { .api }

202

/**

203

* Zoom plugin for waveform zoom controls

204

*/

205

class ZoomPlugin extends BasePlugin<ZoomPluginEvents, ZoomPluginOptions> {

206

/**

207

* Create a zoom plugin instance

208

* @param options - Zoom configuration options

209

* @returns New ZoomPlugin instance

210

*/

211

static create(options?: ZoomPluginOptions): ZoomPlugin;

212

213

/**

214

* Zoom in by the configured scale factor

215

*/

216

zoomIn(): void;

217

218

/**

219

* Zoom out by the configured scale factor

220

*/

221

zoomOut(): void;

222

223

/**

224

* Get current zoom level

225

* @returns Current pixels per second

226

*/

227

getCurrentZoom(): number;

228

}

229

230

interface ZoomPluginOptions {

231

/** Zoom scale factor, defaults to 2 */

232

scale?: number;

233

234

/** Maximum zoom level in pixels per second, defaults to 1000 */

235

maxZoom?: number;

236

237

/** Minimum zoom level in pixels per second, defaults to 1 */

238

minZoom?: number;

239

240

/** Mouse wheel sensitivity, defaults to 1 */

241

deltaThreshold?: number;

242

243

/** Enable mouse wheel zooming, defaults to true */

244

wheelZoom?: boolean;

245

246

/** Enable keyboard zoom shortcuts, defaults to true */

247

keyboardZoom?: boolean;

248

}

249

250

interface ZoomPluginEvents extends BasePluginEvents {

251

/** When zoom level changes */

252

'zoom-changed': [zoomLevel: number];

253

254

/** When zoom reaches maximum level */

255

'zoom-max': [zoomLevel: number];

256

257

/** When zoom reaches minimum level */

258

'zoom-min': [zoomLevel: number];

259

}

260

```

261

262

**Usage Examples:**

263

264

```typescript

265

import Zoom from "wavesurfer.js/dist/plugins/zoom.esm.js";

266

267

// Basic zoom controls

268

const zoom = Zoom.create({

269

scale: 1.5, // 1.5x zoom factor

270

maxZoom: 500,

271

minZoom: 10,

272

});

273

274

wavesurfer.registerPlugin(zoom);

275

276

// Zoom control buttons

277

document.getElementById('zoom-in').addEventListener('click', () => {

278

zoom.zoomIn();

279

});

280

281

document.getElementById('zoom-out').addEventListener('click', () => {

282

zoom.zoomOut();

283

});

284

285

// Advanced zoom with custom controls

286

const advancedZoom = Zoom.create({

287

scale: 2,

288

maxZoom: 1000,

289

minZoom: 5,

290

wheelZoom: true,

291

keyboardZoom: true,

292

deltaThreshold: 5,

293

});

294

295

// Zoom event handling

296

advancedZoom.on('zoom-changed', (zoomLevel) => {

297

console.log(`Zoom level: ${zoomLevel} px/sec`);

298

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

299

updateZoomSlider(zoomLevel);

300

});

301

302

advancedZoom.on('zoom-max', (zoomLevel) => {

303

console.log('Maximum zoom reached');

304

document.getElementById('zoom-in').disabled = true;

305

});

306

307

advancedZoom.on('zoom-min', (zoomLevel) => {

308

console.log('Minimum zoom reached');

309

document.getElementById('zoom-out').disabled = true;

310

});

311

312

// Custom zoom slider

313

const zoomSlider = document.getElementById('zoom-slider');

314

zoomSlider.addEventListener('input', (event) => {

315

const zoomLevel = parseInt(event.target.value);

316

wavesurfer.zoom(zoomLevel);

317

});

318

319

function updateZoomSlider(zoomLevel) {

320

zoomSlider.value = zoomLevel;

321

}

322

```

323

324

### Hover Plugin

325

326

Display vertical line and timestamp on waveform hover for precise time identification.

327

328

```typescript { .api }

329

/**

330

* Hover plugin for showing time information on waveform hover

331

*/

332

class HoverPlugin extends BasePlugin<HoverPluginEvents, HoverPluginOptions> {

333

/**

334

* Create a hover plugin instance

335

* @param options - Hover configuration options

336

* @returns New HoverPlugin instance

337

*/

338

static create(options?: HoverPluginOptions): HoverPlugin;

339

}

340

341

interface HoverPluginOptions {

342

/** Color of the hover line, defaults to '#333' */

343

lineColor?: string;

344

345

/** Width of the hover line, defaults to '1px' */

346

lineWidth?: string;

347

348

/** Background color of time label, defaults to '#fff' */

349

labelBackground?: string;

350

351

/** Text color of time label, defaults to '#000' */

352

labelColor?: string;

353

354

/** Font size of time label, defaults to '12px' */

355

labelSize?: string;

356

357

/** Custom time formatting function */

358

formatTimeCallback?: (seconds: number) => string;

359

360

/** Whether to show the time label, defaults to true */

361

showLabel?: boolean;

362

363

/** Whether to show the hover line, defaults to true */

364

showLine?: boolean;

365

}

366

367

interface HoverPluginEvents extends BasePluginEvents {

368

/** When hover starts */

369

'hover-start': [time: number];

370

371

/** When hover position changes */

372

'hover-move': [time: number];

373

374

/** When hover ends */

375

'hover-end': [];

376

}

377

```

378

379

**Usage Examples:**

380

381

```typescript

382

import Hover from "wavesurfer.js/dist/plugins/hover.esm.js";

383

384

// Basic hover functionality

385

const hover = Hover.create({

386

lineColor: '#ff0000',

387

lineWidth: '2px',

388

labelBackground: 'rgba(0, 0, 0, 0.8)',

389

labelColor: '#fff',

390

labelSize: '14px',

391

});

392

393

wavesurfer.registerPlugin(hover);

394

395

// Custom time formatting

396

const customHover = Hover.create({

397

formatTimeCallback: (seconds) => {

398

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

399

const secs = (seconds % 60).toFixed(2);

400

return `${mins}:${secs.padStart(5, '0')}`;

401

},

402

showLabel: true,

403

showLine: true,

404

});

405

406

// Hover event handling for custom actions

407

hover.on('hover-start', (time) => {

408

console.log(`Hover started at ${time}s`);

409

showTimeTooltip(time);

410

});

411

412

hover.on('hover-move', (time) => {

413

updateTimeTooltip(time);

414

415

// Show additional information at hover position

416

showContextualInfo(time);

417

});

418

419

hover.on('hover-end', () => {

420

console.log('Hover ended');

421

hideTimeTooltip();

422

hideContextualInfo();

423

});

424

425

// Minimal hover for mobile (line only)

426

const mobileHover = Hover.create({

427

lineColor: 'rgba(255, 255, 255, 0.8)',

428

lineWidth: '1px',

429

showLabel: false, // Hide label on mobile

430

showLine: true,

431

});

432

```

433

434

### Navigation Integration

435

436

Combine navigation plugins for comprehensive waveform control and user experience.

437

438

**Usage Examples:**

439

440

```typescript

441

// Complete navigation setup

442

function setupCompleteNavigation() {

443

// Timeline for time reference

444

const timeline = Timeline.create({

445

height: 25,

446

timeInterval: 5,

447

primaryLabelInterval: 15,

448

secondaryLabelInterval: 5,

449

formatTimeCallback: formatTimeMmSs,

450

});

451

452

// Minimap for overview

453

const minimap = Minimap.create({

454

height: 50,

455

waveColor: '#e0e0e0',

456

progressColor: '#888',

457

});

458

459

// Zoom controls

460

const zoom = Zoom.create({

461

scale: 1.8,

462

maxZoom: 800,

463

minZoom: 20,

464

});

465

466

// Hover feedback

467

const hover = Hover.create({

468

lineColor: '#4A90E2',

469

formatTimeCallback: formatTimeMmSs,

470

});

471

472

// Register all plugins

473

[timeline, minimap, zoom, hover].forEach(plugin => {

474

wavesurfer.registerPlugin(plugin);

475

});

476

477

// Synchronized zoom updates

478

zoom.on('zoom-changed', (level) => {

479

updateZoomIndicator(level);

480

saveZoomPreference(level);

481

});

482

483

// Minimap click feedback

484

minimap.on('minimap-click', (progress) => {

485

const time = progress * wavesurfer.getDuration();

486

showSeekFeedback(time);

487

});

488

}

489

490

// Navigation keyboard shortcuts

491

function setupKeyboardNavigation() {

492

document.addEventListener('keydown', (event) => {

493

if (!wavesurfer) return;

494

495

switch (event.key) {

496

case 'ArrowLeft':

497

wavesurfer.skip(-5); // Skip back 5 seconds

498

event.preventDefault();

499

break;

500

case 'ArrowRight':

501

wavesurfer.skip(5); // Skip forward 5 seconds

502

event.preventDefault();

503

break;

504

case '=':

505

case '+':

506

zoom?.zoomIn();

507

event.preventDefault();

508

break;

509

case '-':

510

zoom?.zoomOut();

511

event.preventDefault();

512

break;

513

case ' ':

514

wavesurfer.playPause();

515

event.preventDefault();

516

break;

517

case 'Home':

518

wavesurfer.setTime(0);

519

event.preventDefault();

520

break;

521

case 'End':

522

wavesurfer.setTime(wavesurfer.getDuration());

523

event.preventDefault();

524

break;

525

}

526

});

527

}

528

529

// Navigation state persistence

530

function setupNavigationPersistence() {

531

// Save navigation state

532

function saveNavigationState() {

533

const state = {

534

zoom: zoom?.getCurrentZoom() || wavesurfer.options.minPxPerSec,

535

position: wavesurfer.getCurrentTime(),

536

scroll: wavesurfer.getScroll(),

537

};

538

localStorage.setItem('waveform-navigation', JSON.stringify(state));

539

}

540

541

// Restore navigation state

542

function restoreNavigationState() {

543

const saved = localStorage.getItem('waveform-navigation');

544

if (saved) {

545

const state = JSON.parse(saved);

546

547

// Restore zoom

548

if (state.zoom) {

549

wavesurfer.zoom(state.zoom);

550

}

551

552

// Restore position and scroll

553

if (state.position) {

554

wavesurfer.setTime(state.position);

555

}

556

if (state.scroll) {

557

wavesurfer.setScroll(state.scroll);

558

}

559

}

560

}

561

562

// Save state on changes

563

wavesurfer.on('zoom', saveNavigationState);

564

wavesurfer.on('scroll', saveNavigationState);

565

wavesurfer.on('seeking', saveNavigationState);

566

567

// Restore on load

568

wavesurfer.on('ready', restoreNavigationState);

569

}

570

571

// Responsive navigation

572

function setupResponsiveNavigation() {

573

const isMobile = window.innerWidth < 768;

574

575

// Mobile-optimized plugins

576

if (isMobile) {

577

const mobileTimeline = Timeline.create({

578

height: 20,

579

primaryLabelInterval: 30,

580

style: { fontSize: '10px' },

581

});

582

583

const mobileHover = Hover.create({

584

showLabel: false, // Touch interfaces don't need hover labels

585

lineColor: 'rgba(255, 255, 255, 0.8)',

586

});

587

588

// No minimap on mobile to save space

589

wavesurfer.registerPlugin(mobileTimeline);

590

wavesurfer.registerPlugin(mobileHover);

591

} else {

592

// Full desktop navigation

593

setupCompleteNavigation();

594

}

595

596

// Handle orientation changes

597

window.addEventListener('orientationchange', () => {

598

setTimeout(() => {

599

// Refresh navigation layout

600

wavesurfer.getActivePlugins().forEach(plugin => {

601

if (plugin.onResize) plugin.onResize();

602

});

603

}, 100);

604

});

605

}

606

607

// Utility functions

608

function formatTimeMmSs(seconds) {

609

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

610

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

611

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

612

}

613

614

function showSeekFeedback(time) {

615

const feedback = document.getElementById('seek-feedback');

616

feedback.textContent = `Seeking to ${formatTimeMmSs(time)}`;

617

feedback.style.display = 'block';

618

setTimeout(() => {

619

feedback.style.display = 'none';

620

}, 1000);

621

}

622

```