# Audio Playback with Tone.js ## Overview Tone.js provides browser-based MIDI playback with synthesis, allowing users to hear their notation. ## Why Tone.js? - High-level WebAudio API wrapper - Built-in Transport for timing and tempo control - Multiple synthesis methods (samplers, synths) - Scheduling for precise timing - Easy MIDI playback ## Basic Playback Implementation ```typescript import * as Tone from 'tone'; class PlaybackEngine { private sampler: Tone.Sampler; private isPlaying: boolean = false; constructor() { // Load piano samples this.sampler = new Tone.Sampler({ urls: { A0: "A0.mp3", C1: "C1.mp3", // ... more samples across piano range C8: "C8.mp3", }, baseUrl: "https://tonejs.github.io/audio/salamander/", }).toDestination(); } async play(notes: Note[]) { await Tone.start(); // Required for browser autoplay policy const now = Tone.now(); notes.forEach((note, index) => { const time = now + index * 0.5; // 0.5s between notes this.sampler.triggerAttackRelease(note.pitch, note.duration, time); }); this.isPlaying = true; } stop() { Tone.Transport.stop(); this.isPlaying = false; } } ``` ## Playback Controls Component ```typescript export const PlaybackControls: React.FC = () => { const [isPlaying, setIsPlaying] = useState(false); const [tempo, setTempo] = useState(120); // BPM const [currentBeat, setCurrentBeat] = useState(0); const playback = useRef(new PlaybackEngine()); const handlePlay = async () => { const { score } = useNotationStore.getState(); await playback.current.play(score.measures.flatMap(m => m.notes)); setIsPlaying(true); }; const handlePause = () => { playback.current.stop(); setIsPlaying(false); }; const handleTempoChange = (newTempo: number) => { setTempo(newTempo); Tone.Transport.bpm.value = newTempo; }; return (