Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
bbeierle12 avatar

Audio Playback

  • 73 installs
  • 8 repo stars
  • Updated August 4, 2026
  • bbeierle12/skill-mcp-claude

Audio Playback is a skill for implementing browser audio playback and scheduling with Tone.js, including players, transport, and effects.

About

Audio Playback covers audio playback and scheduling in the browser with Tone.js. It documents player control, the global transport clock, event scheduling, effects chains, and reusable patterns like a music player and sound-effects manager. A developer uses it when adding background music, sound effects, or timed audio events to a web application.

  • Audio playback and scheduling for web apps with Tone.js
  • Covers players, transport clock, event scheduling, and effects chains
  • Includes reusable MusicPlayer and SFX manager patterns

Audio Playback by the numbers

  • 73 all-time installs (skills.sh)
  • Ranked #1,146 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

audio-playback capabilities & compatibility

Capabilities
frontend
Use cases
frontend
Pricing
Free
From the docs

What audio-playback says it does

Audio playback using Tone.js including players, transport, scheduling, and loading audio.
SKILL.md
Audio context requires user gesture to start
SKILL.md
npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill audio-playback

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs73
repo stars8
Last updatedAugust 4, 2026
Repositorybbeierle12/skill-mcp-claude

What it does

Add background music, sound effects, or timed audio events to a web app using Tone.js.

Who is it for?

Adding background music, sound effects, or scheduled audio events to a web application

Skip if: Server-side audio processing or non-web audio pipelines

When should I use this skill?

Implementing background music, sound effects, audio synchronization, or timed audio events in a web app

What you get

Working in-browser audio playback with a transport clock, scheduling, and an effects chain

  • audio playback code
  • transport scheduling setup
  • effects chain configuration

Files

SKILL.mdMarkdownGitHub ↗

Audio Playback

Audio playback and scheduling with Tone.js.

Quick Start

npm install tone
import * as Tone from 'tone';

// Simple playback
const player = new Tone.Player('/audio/music.mp3').toDestination();

// Must start audio context after user interaction
document.addEventListener('click', async () => {
  await Tone.start();
  player.start();
});

Core Concepts

Audio Context Initialization

import * as Tone from 'tone';

// Audio context requires user gesture to start
async function initAudio() {
  await Tone.start();
  console.log('Audio context started');
}

// Common pattern: init on first click
document.addEventListener('click', initAudio, { once: true });

Player Basics

// Create player
const player = new Tone.Player({
  url: '/audio/track.mp3',
  loop: true,
  autostart: false,
  onload: () => console.log('Loaded')
}).toDestination();

// Control
player.start();
player.stop();
player.seek(10); // Seek to 10 seconds
player.volume.value = -6; // Volume in dB

// Properties
player.state;      // 'started' | 'stopped'
player.loaded;     // boolean
player.duration;   // in seconds

Loading Audio

// Single file
const player = new Tone.Player('/audio/music.mp3');
await player.load('/audio/music.mp3');

// Multiple files with Players
const players = new Tone.Players({
  kick: '/audio/kick.mp3',
  snare: '/audio/snare.mp3',
  hihat: '/audio/hihat.mp3'
}).toDestination();

// Access individual player
players.player('kick').start();

// Buffer for programmatic access
const buffer = new Tone.Buffer('/audio/sample.mp3', () => {
  console.log('Buffer loaded, duration:', buffer.duration);
});

Transport

Basic Transport Control

// Global transport (master clock)
Tone.Transport.start();
Tone.Transport.stop();
Tone.Transport.pause();

// Position
Tone.Transport.position = '0:0:0'; // bars:beats:sixteenths
Tone.Transport.seconds = 10;        // in seconds

// Tempo
Tone.Transport.bpm.value = 120;

// Time signature
Tone.Transport.timeSignature = [4, 4];

Scheduling Events

// Schedule at specific time
Tone.Transport.schedule((time) => {
  player.start(time);
}, '0:0:0');

// Schedule repeating
Tone.Transport.scheduleRepeat((time) => {
  synth.triggerAttackRelease('C4', '8n', time);
}, '4n'); // Every quarter note

// Schedule once
Tone.Transport.scheduleOnce((time) => {
  console.log('One time event at', time);
}, '4:0:0'); // At bar 4

Time Notation

FormatDescriptionExample
'4n'Quarter noteOne beat at 4/4
'8n'Eighth noteHalf a beat
'16n'Sixteenth noteQuarter beat
'1m'One measureFull bar
'2:0:0'Bars:beats:16thsStart of bar 2
'+0.5'Relative seconds0.5s from now
0.5Absolute secondsAt 0.5 seconds

Effects Chain

Basic Signal Flow

// Source → Effects → Destination
const player = new Tone.Player('/audio/track.mp3');
const reverb = new Tone.Reverb(2);
const volume = new Tone.Volume(-6);

player.chain(reverb, volume, Tone.Destination);

Common Effects

// Reverb
const reverb = new Tone.Reverb({
  decay: 2.5,
  wet: 0.4
});

// Delay
const delay = new Tone.FeedbackDelay({
  delayTime: '8n',
  feedback: 0.3,
  wet: 0.25
});

// Filter
const filter = new Tone.Filter({
  frequency: 1000,
  type: 'lowpass',
  Q: 2
});

// Compressor
const compressor = new Tone.Compressor({
  threshold: -24,
  ratio: 4,
  attack: 0.003,
  release: 0.25
});

// Volume/Gain
const volume = new Tone.Volume(-12);
const gain = new Tone.Gain(0.5);

Effect Wet/Dry Mix

const reverb = new Tone.Reverb(2);
reverb.wet.value = 0.5; // 50% wet, 50% dry

// Automate wet mix
reverb.wet.rampTo(1, 2); // Ramp to 100% wet over 2 seconds

Playback Patterns

Music Player

class MusicPlayer {
  constructor() {
    this.player = new Tone.Player().toDestination();
    this.isPlaying = false;
  }

  async load(url) {
    await this.player.load(url);
  }

  async play() {
    await Tone.start();
    this.player.start();
    this.isPlaying = true;
  }

  pause() {
    this.player.stop();
    this.isPlaying = false;
  }

  setVolume(db) {
    this.player.volume.value = db;
  }

  seek(seconds) {
    const wasPlaying = this.isPlaying;
    this.player.stop();
    this.player.seek(seconds);
    if (wasPlaying) this.player.start();
  }

  get duration() {
    return this.player.buffer?.duration || 0;
  }

  get currentTime() {
    return this.player.immediate();
  }
}

Sound Effects Manager

class SFXManager {
  constructor() {
    this.sounds = {};
  }

  async load(name, url) {
    const player = new Tone.Player(url).toDestination();
    await player.load(url);
    this.sounds[name] = player;
  }

  play(name) {
    const sound = this.sounds[name];
    if (sound) {
      sound.stop();  // Stop if already playing
      sound.start();
    }
  }

  setVolume(name, db) {
    if (this.sounds[name]) {
      this.sounds[name].volume.value = db;
    }
  }

  setMasterVolume(db) {
    Tone.Destination.volume.value = db;
  }
}

// Usage
const sfx = new SFXManager();
await sfx.load('click', '/audio/click.mp3');
await sfx.load('success', '/audio/success.mp3');
sfx.play('click');

Looping Ambient Layer

class AmbientLayer {
  constructor(url) {
    this.player = new Tone.Player({
      url,
      loop: true,
      fadeIn: 2,
      fadeOut: 2
    });

    this.volume = new Tone.Volume(-12);
    this.reverb = new Tone.Reverb(4);

    this.player.chain(this.reverb, this.volume, Tone.Destination);
  }

  async start() {
    await Tone.start();
    this.player.start();
  }

  stop() {
    this.player.stop();
  }

  setIntensity(value) {
    // 0-1 range
    this.volume.volume.value = -24 + (value * 18); // -24dB to -6dB
    this.reverb.wet.value = 0.3 + (value * 0.4);   // 30% to 70% wet
  }
}

Crossfading

class CrossfadePlayer {
  constructor() {
    this.playerA = new Tone.Player();
    this.playerB = new Tone.Player();
    this.crossfade = new Tone.CrossFade();

    this.playerA.connect(this.crossfade.a);
    this.playerB.connect(this.crossfade.b);
    this.crossfade.toDestination();

    this.current = 'a';
  }

  async loadAndCrossfade(url, duration = 2) {
    const nextPlayer = this.current === 'a' ? this.playerB : this.playerA;
    const targetFade = this.current === 'a' ? 1 : 0;

    await nextPlayer.load(url);
    nextPlayer.start();

    this.crossfade.fade.rampTo(targetFade, duration);

    // Stop old player after crossfade
    setTimeout(() => {
      const oldPlayer = this.current === 'a' ? this.playerA : this.playerB;
      oldPlayer.stop();
    }, duration * 1000);

    this.current = this.current === 'a' ? 'b' : 'a';
  }
}

Synced Playback

Sync to Transport

// Player synced to transport
const player = new Tone.Player('/audio/track.mp3');
player.sync().start(0).toDestination();

// Now transport controls playback
Tone.Transport.start();
Tone.Transport.pause();
Tone.Transport.stop();

Multiple Synced Players

const drums = new Tone.Player('/audio/drums.mp3').toDestination();
const bass = new Tone.Player('/audio/bass.mp3').toDestination();
const melody = new Tone.Player('/audio/melody.mp3').toDestination();

// Sync all to transport
drums.sync().start(0);
bass.sync().start(0);
melody.sync().start(0);

// Set tempo
Tone.Transport.bpm.value = 120;

// Control all with transport
Tone.Transport.start();

Temporal Collapse Patterns

Countdown Audio Manager

class CountdownAudio {
  constructor() {
    this.ambient = new Tone.Player({ loop: true });
    this.tickSound = new Tone.Player();
    this.finalTicks = new Tone.Player();
    this.celebration = new Tone.Player();

    // Effects
    this.filter = new Tone.Filter(2000, 'lowpass');
    this.reverb = new Tone.Reverb(3);

    // Routing
    this.ambient.chain(this.filter, this.reverb, Tone.Destination);
    this.tickSound.toDestination();
    this.finalTicks.toDestination();
    this.celebration.toDestination();
  }

  async loadAll() {
    await Promise.all([
      this.ambient.load('/audio/cosmic-ambient.mp3'),
      this.tickSound.load('/audio/tick.mp3'),
      this.finalTicks.load('/audio/final-tick.mp3'),
      this.celebration.load('/audio/celebration.mp3')
    ]);
  }

  async start() {
    await Tone.start();
    this.ambient.start();
  }

  tick(secondsRemaining) {
    if (secondsRemaining <= 10) {
      // Intense ticks for final 10 seconds
      this.finalTicks.start();
    } else {
      this.tickSound.start();
    }
  }

  setIntensity(value) {
    // 0-1, increases as countdown nears zero
    this.filter.frequency.value = 500 + (value * 3500);
    this.ambient.volume.value = -12 + (value * 6);
  }

  celebrate() {
    this.ambient.stop();
    this.celebration.start();
  }
}

Time-Synced Audio Events

function scheduleCountdownAudio(targetDate) {
  const checkInterval = setInterval(() => {
    const now = Date.now();
    const remaining = targetDate - now;
    const seconds = Math.floor(remaining / 1000);

    if (seconds <= 0) {
      clearInterval(checkInterval);
      audio.celebrate();
      return;
    }

    // Tick every second
    audio.tick(seconds);

    // Increase intensity as countdown progresses
    const intensity = Math.max(0, 1 - (seconds / 3600)); // Over 1 hour
    audio.setIntensity(intensity);

  }, 1000);
}

Performance Tips

// 1. Preload audio before needed
await player.load(url);

// 2. Reuse players instead of creating new ones
player.stop();
player.start(); // Reuse same player

// 3. Dispose when done
player.dispose();

// 4. Use buffer for frequently played sounds
const buffer = new Tone.Buffer(url);
// Create players from buffer
const player = new Tone.Player(buffer);

// 5. Limit concurrent sounds
const limiter = new Tone.Limiter(-3).toDestination();
players.forEach(p => p.connect(limiter));

Reference

  • See audio-analysis for FFT and frequency extraction
  • See audio-reactive for visual-audio binding
  • See audio-router for audio domain routing

Related skills

FAQ

What library does Audio Playback use?

Tone.js, installed with npm install tone.

Why must audio start after a user click?

The browser audio context requires a user gesture to start, so Tone.start() is called after the first interaction.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.