tonejs について
tonejs は、Tone.js と Web Audio API を使ったブラウザー向け音楽アプリの開発を支援するスキルです。シンセシス、サンプリング、音楽的なイベントのスケジューリング、エフェクト、映像との同期を扱います。
主な機能
- 01シンセとポリフォニック楽器の作成
- 02音声ファイルとサンプルの再生
- 03音楽時間に沿ったイベント予約
- 04エフェクトの接続とパラメーター自動化
- 05音声と映像の更新を同期
活用シーン
- 01ステップシーケンサーやドラムマシンの構築
- 02ゲームへの音楽や効果音の追加
- 03リアルタイム音声ビジュアライゼーションの作成
- 04精密なタイミングでの音楽再生
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ plyght/ tonejs-skill --skill tonejsインストール後、Claude Code で/tonejsを入力して読み込まれたか確認します。
同梱ファイルREADME.md
詳細
Tone.js スキル
Tone.js の高度な抽象化を通じて Web Audio API を使用し、ブラウザーでインタラクティブな音楽アプリケーションを構築します。
このスキルを使用する場面
次のような場合に Tone.js を使用します。
- シンセサイザー、サンプラー、または楽器を作成する
- ステップシーケンサー、ドラムマシン、または DAW を構築する
- ゲームに効果音や音楽を追加する
- サウンドと同期するオーディオビジュアライゼーションを実装する
- エフェクトを使ってオーディオをリアルタイムで処理する
- 正確なタイミングで音楽イベントをスケジュールする
- 音楽の概念(音符、テンポ、小節)を扱う
コアコンセプト
1. コンテキストと初期化
AudioContext はユーザー操作によって開始する必要があります(ブラウザーの要件)。
import * as Tone from "tone";
// ALWAYS call Tone.start() from user interaction
document.querySelector("button").addEventListener("click", async () => {
await Tone.start();
console.log("Audio context ready");
// Now safe to play audio
});
2. オーディオグラフとルーティング
すべてのオーディオノードは、Tone.Destination(スピーカー)へとつながるグラフ内で接続します。
// Basic connection
const synth = new Tone.Synth().toDestination();
// Chain through effects
const synth = new Tone.Synth();
const filter = new Tone.Filter(400, "lowpass");
const delay = new Tone.FeedbackDelay(0.125, 0.5);
synth.chain(filter, delay, Tone.Destination);
// Parallel routing (split signal)
const reverb = new Tone.Reverb().toDestination();
const delay = new Tone.Delay(0.2).toDestination();
synth.connect(reverb);
synth.connect(delay);
3. 時間とスケジューリング
Tone.js は時間を音楽記譜法で抽象化します。
"4n"= 四分音符"8n"= 八分音符"2m"= 2 小節"8t"= 八分音符の三連符- 数値 = 秒
重要:コールバックに渡される time パラメーターを必ず使用してください。
// CORRECT - sample-accurate timing
const loop = new Tone.Loop((time) => {
synth.triggerAttackRelease("C4", "8n", time);
}, "4n");
// WRONG - JavaScript timing is imprecise
const loop = new Tone.Loop(() => {
synth.triggerAttackRelease("C4", "8n"); // Will drift
}, "4n");
4. トランスポートシステム
イベントを同期するためのグローバルなタイムキーパーです。
// Schedule events on the Transport
const loop = new Tone.Loop((time) => {
synth.triggerAttackRelease("C4", "8n", time);
}, "4n").start(0);
// Control the Transport
Tone.Transport.start();
Tone.Transport.stop();
Tone.Transport.pause();
Tone.Transport.bpm.value = 120; // Set tempo
手順
タスク 1:基本的なシンセサイザーを作成する
- Tone.js をインポートする
- シンセサイザーを作成し、出力に接続する
- オーディオを開始するためにユーザー操作を待つ
triggerAttackReleaseを使って音符を再生する
import * as Tone from "tone";
const synth = new Tone.Synth().toDestination();
button.addEventListener("click", async () => {
await Tone.start();
// Play C4 for an eighth note
synth.triggerAttackRelease("C4", "8n");
});
タスク 2:ポリフォニック楽器を作成する
PolySynthを使ってモノフォニックシンセサイザーをラップする- 複数の音符を渡して和音を再生する
- 必要に応じて特定の音符をリリースする
const polySynth = new Tone.PolySynth(Tone.Synth).toDestination();
// Play a chord
polySynth.triggerAttack(["C4", "E4", "G4"]);
// Release specific notes
polySynth.triggerRelease(["E4"], "+1");
タスク 3:オーディオファイルを読み込んで再生する
PlayerまたはSamplerを作成するTone.loaded()の Promise を待つ- 再生を開始する
const player = new Tone.Player("https://example.com/audio.mp3").toDestination();
await Tone.loaded();
player.start();
// For multi-sample instruments
const sampler = new Tone.Sampler({
urls: {
C4: "C4.mp3",
"D#4": "Ds4.mp3",
"F#4": "Fs4.mp3",
},
baseUrl: "https://example.com/samples/",
}).toDestination();
await Tone.loaded();
sampler.triggerAttackRelease(["C4", "E4"], 1);
タスク 4:ループパターンを作成する
- パターンには
Tone.LoopまたはTone.Sequenceを使う - 楽器のトリガーに
timeパラメーターを渡す - ループと Transport を開始する
const synth = new Tone.Synth().toDestination();
const loop = new Tone.Loop((time) => {
synth.triggerAttackRelease("C4", "8n", time);
}, "4n").start(0);
await Tone.start();
Tone.Transport.start();
タスク 5:エフェクト処理を追加する
- エフェクトのインスタンスを作成する
- 希望する順序(直列または並列)で接続する
- 必要に応じてウェット/ドライミックスを調整する
const synth = new Tone.Synth();
const distortion = new Tone.Distortion(0.4);
const reverb = new Tone.Reverb({
decay: 2.5,
wet: 0.5, // 50% effect, 50% dry
});
synth.chain(distortion, reverb, Tone.Destination);
タスク 6:パラメーターを自動化する
- プロパティ(例:
frequency、volume)を使ってパラメーターにアクセスする rampTo、linearRampTo、exponentialRampToなどのメソッドを使うtimeパラメーターを指定して変更をスケジュールする
const osc = new Tone.Oscillator(440, "sine").toDestination();
osc.start();
// Ramp frequency to 880 Hz over 2 seconds
osc.frequency.rampTo(880, 2);
// Set value at specific time
osc.frequency.setValueAtTime(440, "+4");
// Exponential ramp (better for frequency)
osc.frequency.exponentialRampTo(220, 1, "+4");
タスク 7:ビジュアルをオーディオと同期する
- ビジュアルを更新するには
Tone.Draw.schedule()を使う - オーディオイベントと同じコールバック内でスケジュールする
- ビジュアルの更新はオーディオの再生直前に行われる
const loop = new Tone.Loop((time) => {
synth.triggerAttackRelease("C4", "8n", time);
// Schedule visual update
Tone.Draw.schedule(() => {
element.classList.add("active");
}, time);
}, "4n");
よくあるパターン
パターン:ステップシーケンサー
const synth = new Tone.Synth().toDestination();
const notes = ["C4", "D4", "E4", "G4"];
const seq = new Tone.Sequence(
(time, note) => {
synth.triggerAttackRelease(note, "8n", time);
},
notes,
"8n"
).start(0);
Tone.Transport.start();
パターン:確率的な再生
const loop = new Tone.Loop((time) => {
if (Math.random() > 0.5) {
synth.triggerAttackRelease("C4", "8n", time);
}
}, "8n");
パターン:エフェクトパラメーターの動的制御
const filter = new Tone.Filter(1000, "lowpass").toDestination();
const lfo = new Tone.LFO(4, 200, 2000); // 4Hz, 200-2000Hz range
lfo.connect(filter.frequency);
lfo.start();
サウンドデザインの原則
主な知見
聴覚処理は視覚処理より 10 倍速い(約 25ms 対 約 250ms)。サウンドは即座にフィードバックを返し、操作への応答性を高めます。視覚的なフィードバックが同じでも、クリック音の鳴るボタンは鳴らないボタンよりも速く感じられます。
サウンドは感情を瞬時に伝えます。 単音でも、成功、エラー、緊張感を、視覚的な演出より効果的に伝えられます。オーディオとビジュアルが一緒に同じストーリーを伝えると、どちらか一方だけの場合よりも体験が強くなります。
少ないほど効果的です。 ほとんどの操作では音を鳴らさないようにします。音は重要な場面に限って使用します。主な操作の確認、見落とせないエラー、状態の遷移、通知などです。アクセシビリティのため、サウンドには必ずビジュアルを組み合わせてください。サウンドは体験を補強するものであり、決して置き換えるものではありません。参考としてゲームを研究しましょう。情報性があり、感情に訴え、邪魔にならないオーディオフィードバックを実現しています。
デザインの理念
優れたサウンドデザインは、ウェブアプリ、モバイルアプリ、デスクトップアプリケーション、ゲームなど、あらゆるプラットフォームでユーザー体験を変革します。これらの原則は、通知音、UI フィードバック、音楽的なインタラクションのいずれを作成する場合にも、普遍的に当てはまります。
サウンドは、誰もが理解できる普遍的な言語です。オーディオをデザインする際は、次の点を考慮してください。
基本的な問いを立てる:
- このアプリ/機能の本質は何ですか?
- どのような感情を呼び起こしたいですか?
- アプリのビジュアルデザインとどのように調和しますか?
- 画面を見なくても、ユーザーはこのインタラクションをどのように理解できますか?
コンテキストを考慮する:
- ユーザーはどこでこの音を聞きますか?(ポケットの中、デスク、にぎやかな通り、静かな部屋)
- ユーザーは何をしていますか?(仕事、通勤、ゲーム)
- どのくらいの頻度で聞きますか?(1 日に 1 回か、何百回もか)
通知音のデザイン
効果的な通知音には、次のような特徴があります。
1. 聞き分けやすい
- アプリを識別できる独自の音の特徴を作ります
- 特徴的な音色やメロディーパターンを使います
- シンプルな要素を重ねて、認識しやすさを高めます
- システムのデフォルト音や、よくある他の音をまねないようにします
2. 意味を伝える
- 音はメッセージと結び付くものにします(直接的ではなく、暗示的に)
- 水や天候には液体のような音、アラートには金属的な音、成功には温かみのある音色を使います
- 音色とエンベロープを使って、内容を想起させます
- 効果音ではなく、抽象的に表現します
3. 親しみやすく、適切である
- メッセージの重要度に緊急度を合わせます
- 穏やかな音:アタックを柔らかく(50ms 以上)、滑らかな音色(サイン波、三角波)にします
- 緊急性の高い音:アタックを速く(5ms 未満)、明るい音色(矩形波、FM 合成)にします
- 音量と明るさで優先度を示します
4. シンプルで明瞭である
- 複雑なレイヤーや密度の高い倍音成分は避けます
- メロディーよりも、1 音または 2 音のパターンが効果的です
- 心地よい音色を選ぶと、繰り返し聞いても不快になりません
- 奇抜さより明瞭さを優先します
5. 邪魔にならず、繰り返し聞ける
- 通知音の長さは、最大でも 0.3~0.8 秒にします
- 頻繁に鳴る音には、柔らかい音色(サイン波、三角波)を使います
- 耳障りで複雑な音色や、極端に明るい音色は避けます
- 1 日に 50 回以上聞いても心地よい音にします
6. 耳障りにならず、周囲の雑音に埋もれない
- 中音域の周波数(300~3000Hz)が最も効果的です
- 極端な高音(>8kHz)と低音(<80Hz)は避けます
- 耳障りにならずに、騒がしい環境でも聞こえるように設計します
- 三角波とサイン波は、矩形波よりも穏やかです
UI サウンドのデザイン
ボタン、インタラクション、画面遷移には、次の点を考慮してください。
1. 使用は控えめに
- 重要な状態変化や確認にのみ音を付けます
- 音を出さないことが最善の場合もよくあります
- ホバー、マウスオーバー、細かなインタラクションのすべてに音を付けないようにします
- 意味のある場面に限って、音によるフィードバックを使います
2. 目的に応じた音量
- UI サウンド:通知音よりもかなり小さくします(-20~-30dB)
- ユーザーはデバイスを手に持っているため、控えめな音で十分です
- 通知音:周囲の雑音に埋もれないよう、より大きくします(-10~-15dB)
3. 同期が重要
- オーディオとビジュアルの更新は、同時に発生する必要があります(許容誤差 < 10ms)
- ずれがあると、反応が遅く、プロらしくない印象になります
- おおよそのタイミングではなく、正確なスケジューリングを使います
4. インタラクションの性質に合わせる
- 素早いタップ:持続時間を短く(<50ms)、周波数を高めにします
- 強く押す操作:持続時間を長く(100~200ms)、周波数を低めにします
- ドラッグ操作:連続したフィードバックを返すか、音を出しません
- 成功した操作:上昇する音高や明るい音色にします
- 失敗した操作:下降する音高や鈍い音色にします
5. 奥行きと動きを伝える
- 前方への移動:時間とともに音高を上げます
- 後方への移動:時間とともに音高を下げます
- 開く操作:エンベロープを広げます(フェードイン)
- 閉じる操作:エンベロープを縮めます(フェードアウト)
- エンベロープの形状で、物理的な動きを想起させます
クリエイティブなプロセス
1. まずサウンドパレットを用意する
- さまざまな合成方式を試します(サイン波、三角波、矩形波、FM、AM、ノイズ)
- コンセプトに合った音を録音または合成します
- 物理的なオブジェクト、日常の音、伝統楽器なども利用できます
- 候補となる音を5~10種類集めます
2. 音を用途に合わせる
- UI 要素ごとに、その目的に合った音を1つだけ割り当てます
- ボタンクリック:素早く反応する音(高めの音程、短い減衰)
- トグルスイッチ:オンとオフで異なる2つの音
- 通知:長めで、より特徴的な音
- 複数の候補を用意する必要はありません。最適なものを選びます
3. あらゆる音源を使う
- 伝統楽器:カリンバ、ベル、ピアノ、マリンバ
- 物理素材:金属、ガラス、木、プラスチック
- 合成音:純音、FM合成、フィルタリングしたノイズ
- 型破りな音源:トースター、ドア、機械音
- 音源よりも、最終的な音の特徴が重要です
4. レイヤーを重ねて豊かな音にする
- 複雑な音を1つ作るより、シンプルな要素を2~3つ組み合わせます
- 基本音+倍音レイヤー+テクスチャ
- 各レイヤーには役割があります(厚み、明るさ、個性)
- レイヤーを重ねても、全体の長さは短く保ちます
技術的な考慮事項
1. クリアな音声
- クリック音を防ぐためにフェードイン/フェードアウトします(10~50ms)
- DCオフセットを除去します
- 音量レベルを一貫して正規化します
- 音が途中で切れたり、突然終わったりしないようにします
2. 周波数フィルタリング
- 80Hzでハイパスフィルターを適用します(サブベースを除去)
- 小型スピーカーでは低周波数を再生できません
- 必要に応じて8kHzでローパスフィルターを適用します(耳障りな高音を除去)
- 効果を最大限にするため、300~3000Hzの範囲にエネルギーを集中させます
3. クロスプラットフォーム設計
- 最も性能の低いスピーカーシステム(スマートフォン、ノートパソコン)を基準に設計します
- ヘッドフォンでは細部まで聞こえますが、スピーカーでの再生を最適化します
- サブベース(<80Hz)は完全に避けます
- 中音域の周波数は、どの環境でも適しています
4. 長さのガイドライン
- 通知:0.3~0.8秒
- ボタンクリック:0.05~0.1秒
- トグル:0.05~0.15秒
- トランジション:0.1~0.3秒
- ユーザーの注意を尊重し、すべてのUIサウンドを短く保ちます
5. ユーザーによる制御
- サウンドを無効にする設定を必ず用意します
- システム音量とは別に音量を調節できるようにします
- サウンドの種類ごとに切り替えられるようにします(通知とUIフィードバックなど)
- システム全体のミュート設定を尊重します
6. 同期の精度
- 音声を、視覚効果とまったく同じタイミングで再生するようにスケジュールします
- 10~20msの遅延は知覚でき、違和感につながります
setTimeout/setIntervalではなく、精度の高いタイミングAPIを使用します- 音声と映像の同期は、反応の良さを感じてもらううえで重要です
設計チェックリスト
サウンドを設計する際の実装上の考慮事項:
- 聞き分けやすい:システムのデフォルト音に似ていない、固有の波形/エンベロープにする
- 繰り返し再生しやすい:複雑または耳障りな音ではなく、シンプルで心地よい音色(サイン波、三角波)にする
- クロスプラットフォーム対応:周波数を300~3000Hzにし、80Hzでハイパスフィルターを適用する
- 聞き取りやすく、耳障りでない:中音域の周波数を使い、極端な高音(>8kHz)や低音(<80Hz)を避ける
- 短い:通知は0.3~0.8秒、UIフィードバックは<0.1秒にする
- 同期している:音声の再生を視覚的な更新と同時にスケジュールする(許容誤差< 10ms)
- ユーザーが制御できる:サウンドを無効にし、音量を調節できる設定を用意する
- 適切な個性がある:エンベロープをインタラクションに合わせる(軽くタップ=短い減衰、強く押す=長い減衰)
- クリアな音声:クリック音を防ぐためにフェードイン/フェードアウトし、不要な周波数を除去するフィルターを適用する
- 意味が伝わる:音色/音程でメッセージを示す(雨なら水のような音、アラートなら金属的な音、成功なら温かみのある音)
重要なエッジケースと注意点
1. ブラウザーの自動再生ポリシー
ユーザーの操作からTone.start()を呼び出す必要があります。これを行わないと、音声は再生されません。
// WRONG - will fail silently
Tone.Transport.start();
// CORRECT
button.addEventListener("click", async () => {
await Tone.start();
Tone.Transport.start();
});
2. メモリ管理
処理が終わったら、必ずノードを破棄します。
const synth = new Tone.Synth().toDestination();
// When finished
synth.dispose();
// For arrays of instruments
players.forEach((player) => player.dispose());
3. タイミングの精度
JavaScriptのコールバックは正確ではありません。必ずtimeパラメーターを使用します。
// WRONG - will drift out of sync
setInterval(() => {
synth.triggerAttackRelease("C4", "8n");
}, 250);
// CORRECT - sample-accurate
new Tone.Loop((time) => {
synth.triggerAttackRelease("C4", "8n", time);
}, "4n").start(0);
4. サンプルの読み込み
再生前に、サンプルの読み込みが完了するまで待ちます。
const sampler = new Tone.Sampler({
urls: { C4: "piano.mp3" },
baseUrl: "/audio/",
}).toDestination();
// WRONG - may not be loaded yet
sampler.triggerAttack("C4");
// CORRECT
await Tone.loaded();
sampler.triggerAttack("C4");
5. モノフォニックとポリフォニック
基本的なシンセはモノフォニック(一度に1音)です。
// Only plays one note
const mono = new Tone.Synth().toDestination();
mono.triggerAttack(["C4", "E4", "G4"]); // Only C4 plays
// Plays all notes
const poly = new Tone.PolySynth(Tone.Synth).toDestination();
poly.triggerAttack(["C4", "E4", "G4"]); // All play
6. ノート形式
ノートは複数の方法で指定できます。
synth.triggerAttackRelease("C4", "8n"); // Pitch-octave notation
synth.triggerAttackRelease(440, "8n"); // Frequency in Hz
synth.triggerAttackRelease("A4", "8n"); // A4 = 440Hz
7. トランスポート時間とオーディオコンテキスト時間
2つの時間システムがあります。
Tone.now(); // AudioContext time (always running)
Tone.Transport.seconds; // Transport time (starts at 0)
// Schedule on AudioContext
synth.triggerAttackRelease("C4", "8n", Tone.now() + 1);
// Schedule on Transport
Tone.Transport.schedule((time) => {
synth.triggerAttackRelease("C4", "8n", time);
}, "1m");
アーキテクチャの概要
ToneAudioNode (base class)
├── Source (audio generators)
│ ├── Oscillator, Player, Noise
│ └── Instrument
│ ├── Synth, FMSynth, AMSynth
│ ├── Sampler
│ └── PolySynth
├── Effect (audio processors)
│ ├── Filter, Delay, Reverb
│ ├── Distortion, Chorus, Phaser
│ └── PitchShift, FrequencyShifter
├── Component (building blocks)
│ ├── Envelope, Filter, LFO
│ └── Channel, Volume, Panner
└── Signal (parameter automation)
├── Signal, Add, Multiply
└── Scale, WaveShaper
クイックリファレンス
インストゥルメントの種類
Tone.Synth- 基本的な単一オシレーターシンセサイザーTone.FMSynth- 周波数変調合成Tone.AMSynth- 振幅変調合成Tone.MonoSynth- フィルターとエンベロープを備えたモノフォニックシンセサイザーTone.DuoSynth- 2ボイスシンセサイザーTone.MembraneSynth- パーカッシブシンセサイザーTone.MetalSynth- 金属的なサウンドTone.NoiseSynth- ノイズベースの合成Tone.PluckSynth- 撥弦楽器モデルTone.PolySynth- ポリフォニック用ラッパーTone.Sampler- マルチサンプルインストゥルメント
主なエフェクト
Tone.Filter- ローパス、ハイパス、バンドパスなどTone.Reverb- コンボリューションリバーブTone.Delay/Tone.FeedbackDelay- エコーエフェクトTone.Distortion- ウェーブシェイピングディストーションTone.Chorus- コーラスエフェクトTone.Phaser- フェイザーエフェクトTone.PitchShift- リアルタイムのピッチシフトTone.Compressor- ダイナミックレンジ圧縮Tone.Limiter- ブリックウォールリミッター
時間表記
"4n"- 4分音符"8n"- 8分音符"16n"- 16分音符"2m"- 2小節"8t"- 8分音符の3連符"1:0:0"- 小節:拍:16分音符0.5- 秒(数値)