はじめに
「Webサイトに音を付けたい」と思ってWeb Audio APIのドキュメントを開き、AudioContext、OscillatorNode、GainNode……と連なる低レベルAPIの数々にそっとタブを閉じた経験はありませんか?
ブラウザには本格的なオーディオ処理エンジンが標準搭載されているのに、それを直接扱うのはかなり骨が折れます。「ドの音を0.5秒鳴らす」だけでも、ノードの生成・接続・スケジューリングを自前で書く必要があるのです。
そこで登場するのがTone.jsです。Web Audio APIの複雑さを覆い隠し、「シンセを作って、音符を鳴らして、エフェクトをかける」という音楽的な語彙でコードが書けるフレームワークです。この記事では、最初の1音を鳴らすところから、ループやエフェクトを使った実践例まで順に見ていきます。
Tone.jsとは
Tone.jsは、ブラウザ上でインタラクティブな音楽を作るためのWeb Audioフレームワークです。GitHubで14,000を超えるスターを集めており、Webオーディオ分野では事実上の定番ライブラリになっています。ライセンスはMITで、2026年7月現在もv15系として活発にメンテナンスされています。
単なるWeb Audio APIのラッパーにとどまらず、DAW(音楽制作ソフト)でおなじみの概念——グローバルなトランスポート(再生ヘッド)、BPM同期、イベントスケジューリング——をそのままJavaScriptに持ち込んでいるのが最大の特徴です。
主な特徴
- 音楽的な記法 - 周波数を
440と書く代わりに"A4"、秒数の代わりに"8n"(8分音符)のように、楽譜の語彙でコードが書けます - プリセットのシンセとエフェクト -
Synth、FMSynth、PolySynthなどの楽器と、ReverbやFeedbackDelayなどのエフェクトが最初から揃っています - 強力なスケジューリング -
TransportがDAWの再生ヘッドのように機能し、BPMに同期したループや正確なタイミングでのイベント発火を実現します - サンプル再生にも対応 -
PlayerやSamplerで音声ファイルを楽器として扱えます
インストール
npmを使う場合は次のコマンドでインストールします。
npm install tone
CDN経由でサクッと試すこともできます。
<script src="https://unpkg.com/tone"></script>
TypeScriptの型定義も同梱されているため、追加のパッケージは不要です。
基本的な使い方
まずは1音鳴らす
Tone.jsの「Hello, World」はわずか2行です。
import * as Tone from "tone";
const synth = new Tone.Synth().toDestination();
synth.triggerAttackRelease("C4", "8n");
toDestination()はスピーカー(出力先)への接続、triggerAttackRelease("C4", "8n")は「ドの音(C4)を8分音符の長さで鳴らす」という意味です。Web Audio APIを生で書けば数十行になる処理が、音楽の言葉で素直に表現できます。
ユーザー操作で音を開始する
ここで1つ重要な注意点があります。ブラウザの自動再生ポリシーにより、オーディオはユーザー操作をきっかけに開始する必要があります。実際のアプリでは、ボタンクリックなどのイベント内でTone.start()を呼びます。
document.getElementById("play-button").addEventListener("click", async () => {
await Tone.start(); // AudioContextを起動(初回のみ必要)
const synth = new Tone.Synth().toDestination();
synth.triggerAttackRelease("C4", "8n");
});
「コードは正しいのに音が出ない」ときは、まずこのTone.start()を疑ってください。Tone.js初心者が最初につまずくポイントです。
和音を鳴らす(PolySynth)
Synthは単音専用ですが、PolySynthでラップすると和音が扱えます。
const poly = new Tone.PolySynth(Tone.Synth).toDestination();
// Cメジャーコードを2分音符の長さで
poly.triggerAttackRelease(["C4", "E4", "G4"], "2n");
エフェクトをかける
エフェクトはconnect()でチェーンするだけです。ギターのエフェクターを直列につなぐ感覚ですね。
const reverb = new Tone.Reverb({ decay: 4, wet: 0.5 }).toDestination();
const delay = new Tone.FeedbackDelay("8n", 0.4).connect(reverb);
const synth = new Tone.Synth().connect(delay);
synth.triggerAttackRelease("E5", "8n");
これだけで、シンセ → ディレイ → リバーブ → スピーカーという信号経路が完成します。
実践的なユースケース
BPM同期のループを作る
Tone.jsの真価はスケジューリングにあります。Transport(v15ではTone.getTransport()で取得)はDAWの再生ボタンに相当し、すべてのイベントをBPMに同期させられます。
const synth = new Tone.MembraneSynth().toDestination(); // キック系の音色
// 4分音符ごとにキックを鳴らすループ
const loop = new Tone.Loop((time) => {
synth.triggerAttackRelease("C1", "8n", time);
}, "4n");
Tone.getTransport().bpm.value = 120;
loop.start(0);
Tone.getTransport().start();
コールバックが受け取るtime引数をそのままtriggerAttackReleaseに渡すのがポイントです。これによりJavaScriptのタイマーのブレに影響されない、サンプル精度のタイミングで発音されます。
ステップシーケンサー風のメロディ
Tone.Sequenceを使うと、配列でメロディパターンを定義できます。簡易的なシーケンサーがこれだけで作れます。
const synth = new Tone.Synth().toDestination();
const sequence = new Tone.Sequence(
(time, note) => {
if (note !== null) {
synth.triggerAttackRelease(note, "16n", time);
}
},
["C4", "E4", "G4", null, "B3", "D4", "F4", null], // nullは休符
"8n" // 8分音符間隔で進行
);
sequence.start(0);
Tone.getTransport().start();
配列の値を差し替えるだけでパターンが変わるので、UIと組み合わせればブラウザ上のドラムマシンやメロディエディタに発展させられます。
UIのフィードバック音に使う
音楽アプリだけでなく、操作音のような小さな用途にも便利です。たとえば、タスク完了時に軽やかな効果音を鳴らすならこうなります。
const synth = new Tone.Synth({
oscillator: { type: "triangle" },
envelope: { attack: 0.005, decay: 0.1, sustain: 0, release: 0.1 },
}).toDestination();
function playSuccessSound() {
const now = Tone.now();
synth.triggerAttackRelease("C5", "16n", now);
synth.triggerAttackRelease("G5", "16n", now + 0.1);
}
音声ファイルを用意せずにコードだけで効果音を定義できるため、アセット管理が不要になり、パラメータ調整も自由自在です。
まとめ
Tone.jsの魅力を振り返ってみましょう。
- Web Audio APIの低レベルな複雑さを隠し、音楽の語彙でコードが書ける
- シンセ・エフェクト・サンプラーがプリセットで揃っている
TransportとLoop/SequenceによるBPM同期の正確なスケジューリング- 音楽アプリからUI効果音まで、用途の幅が広い
「ブラウザで音を扱う」というと特殊な領域に感じられるかもしれませんが、Tone.jsを使えば普段のJavaScriptの延長で音楽的な表現が手に入ります。まずはTone.Synthで1音鳴らすところから、ぜひ手を動かして試してみてください。公式サイト(https://tonejs.github.io/)には豊富なサンプルとAPIドキュメントが揃っています。
