効果音を「探す・買う・書き出す」のではなくコードで設計すると、音程も長さも1行で調整でき、いろんな効果音を量産できます。
セットアップと使い回し
await Tone.start(); // ← 必ずユーザー操作の中で(ブラウザの自動再生制限)
const vol = new Tone.Volume(-14).toDestination(); // マスターは控えめに
const blip = new Tone.Synth({
oscillator: { type: 'triangle' },
envelope: { attack: 0.005, decay: 0.08, sustain: 0, release: 0.05 },
}).connect(vol);
シンセはクリックのたびに new せず、初期化時に一式作って使い回します。Tone.start() を最初のユーザー操作内で呼ぶ「遅延初期化」パターンにしておくと安全です。
let audioReady = false;
async function ensureAudio() {
if (audioReady) return true;
await Tone.start(); // ユーザー操作のイベントハンドラ内でのみ到達
/* ここでシンセを一式生成 */
audioReady = true;
return true;
}
準備ができたら以下の4原則をルールにするとキャラに応じて効果音の量産が可能です。
原則1: 波形=性格
シンセの声色は oscillator の波形でほぼ決まります。
| 波形 | 性格 | デモでの用途 |
|---|---|---|
sine(正弦波) |
丸い・やさしい | 小さいキャラの「ピヨッ」 |
triangle(三角波) |
軽い・ポップ | ホバーの blip |
square(矩形波) |
8bitゲーム・元気 | カート追加のジングル |
sawtooth(ノコギリ波) |
ざらっと存在感 | ひらべったいキャラの「ぺあっ」 |
MembraneSynth(膜) |
ドラム・ボヨン | 大きいキャラの「ボム…」 |
キャラや操作の性格を決めたら、それに合う波形を選ぶ。これで音色の8割が決まります。
原則2: 音程=体格
// でかいキャラ: 低い膜音1発
new Tone.MembraneSynth({ octaves: 4, pitchDecay: 0.12 })
.triggerAttackRelease('C2', 0.4);
// 小さいキャラ: 高音2連
chirp.triggerAttackRelease('E6', 0.07, n);
chirp.triggerAttackRelease('G6', 0.07, n + 0.09);
体の大きさに比例して音を低くする。現実の動物と同じ法則なので、聞いた瞬間に「あの大きい奴が鳴いた」と伝わります。キャラクターは見た目より音で性格がつくというのが作ってみての発見でした。
ちなみに MembraneSynth の pitchDecay は音程の急降下量=「ボヨン」感の正体です。
原則3: 上昇=ポジティブ
// カート追加: C-E-G-C の上昇メジャーアルペジオ
const n = Tone.now();
['C5','E5','G5','C6'].forEach((p, i) =>
synth.triggerAttackRelease(p, 0.12, n + i * 0.07));
「上昇+長調」=達成・報酬の文法です(マリオのコイン音と同じ理屈)。逆に失敗音は下降+短調で作ります。
// 失敗音の例: 下降
synth.triggerAttackRelease('E5', 0.15, n);
synth.triggerAttackRelease('C5', 0.3, n + 0.15);
音の方向だけで感情が反転するので、成功/失敗/注意の3種類くらいは同じシンセの音程違いで賄えます。
原則4: sustain 0 — UI効果音は「短く鳴って消える」
envelope: { attack: 0.005, decay: 0.08, sustain: 0, release: 0.05 }
envelope は音の形(立ち上がり→減衰→保持→余韻)です。UI効果音の設計はほぼこれで固定できます:
-
attack: 0.005〜0.02→ 押した瞬間に鳴る -
sustain: 0→ 押しっぱなしでも伸びない - 全体で 0.05〜0.4秒に収める
「UI効果音は sustain 0」と覚えておけば、envelope で迷うことはなくなります。
音を出すサイトの作法
原則ではなくマナーですが、以下を破ると迷惑サイトになります。
-
勝手に鳴らさない —
Tone.start()はユーザー操作内で(前述の遅延初期化) - いつでも消せる — 画面の目立つ場所に ON/OFF トグルを常設。消しても全機能が使えること
- 短く・小さく — マスター音量は控えめ(デモは -14dB)
コンソールで実験
デモページを開いて DevTools からそのまま試せます(どこかを一度クリックして音を有効化してから):
synths.blip.triggerAttackRelease('A3', 0.3); // 音程を変える
synths.boing.triggerAttackRelease('C4', 0.4); // でかい奴の声を高く→小さい奴になる
音程1つで体格が変わり、方向1つで感情が反転するのが体感できるはずです。
まとめ
- 波形=性格 — sine/triangle/square/sawtooth/膜 から選ぶ
- 音程=体格 — 大きいものは低く、小さいものは高く
- 上昇=ポジティブ — 報酬は上昇メジャー、失敗は下降マイナー
- sustain 0 — UI効果音は短く鳴って消える
このデモのモンスター3体は 画像ゼロの全CSS描画 です(別記事: 「画像ゼロでマスコットキャラを描く — CSSモンスターの4原則」)。
👉 動くデモ: MONSTER DENIM
制作: 株式会社ランドランド — 「買い物が楽しい」を作るWeb制作・アプリ開発をしています。