音声を追加する方法
<audio> 要素で音声ファイルを指定し、controls を付けます。
<audio src="song.mp3" controls></audio>
controls を付けるとブラウザのプレーヤーが表示されます。再生と一時停止、シークバー、時間、音量の調整です。controls がないと要素はまったく場所を取りません。プレビューはパソコンのファイルを読み込めないので、この例ではスクリプトでブラウザ内に短い WAV ファイルを作り、それを src にしています。再生を押してみてください。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Audio</title>
</head>
<body style="font-family: system-ui, sans-serif;">
<audio id="player" controls></audio>
<p>Four notes, 1.8 seconds.</p>
<script>
// Builds a tiny WAV file in the browser (stand-in for song.mp3)
function makeWav(notes) {
const rate = 8000, samples = [];
for (const [freq, secs] of notes) {
const n = secs * rate;
for (let i = 0; i < n; i++) {
samples.push(Math.sin(2 * Math.PI * freq * i / rate) * 0.4 * Math.min(1, (n - i) / 400));
}
}
const view = new DataView(new ArrayBuffer(44 + samples.length * 2));
const text = (at, s) => [...s].forEach((c, i) => view.setUint8(at + i, c.charCodeAt(0)));
text(0, 'RIFF'); view.setUint32(4, 36 + samples.length * 2, true); text(8, 'WAVEfmt ');
view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, 1, true);
view.setUint32(24, rate, true); view.setUint32(28, rate * 2, true);
view.setUint16(32, 2, true); view.setUint16(34, 16, true);
text(36, 'data'); view.setUint32(40, samples.length * 2, true);
samples.forEach((s, i) => view.setInt16(44 + i * 2, s * 32767, true));
return URL.createObjectURL(new Blob([view], { type: 'audio/wav' }));
}
document.getElementById('player').src = makeWav([[523, 0.4], [659, 0.4], [784, 0.4], [1047, 0.6]]);
</script>
</body>
</html>
プレーヤーは各ブラウザが独自に描くので、Chrome、Firefox、Safari で見た目が違います。controls を消すとプレーヤーは消えますが、音声は残っているので JavaScript で再生できます。
audio の属性
| 属性 | 働き |
|---|
controls | ブラウザのプレーヤーを表示する |
autoplay | 読み込み時に再生を始めようとする (音声付きではブロックされる。下を参照) |
loop | ファイルが終わったらもう一度再生する |
muted | 音量をオフにした状態で始める |
preload | none (再生まで何もダウンロードしない)、metadata (長さだけ)、auto (ブラウザが決める) |
src | 再生するファイル。複数の形式には子要素の <source> を使う |
ポッドキャストのエピソード一覧のように多くのプレーヤーがあるページでは、preload="none" か "metadata" を使いましょう。最初にすべてのファイルをダウンロードせずに済みます。
形式と代替手段
<source> で複数のファイルを並べると、ブラウザは対応している最初のものを再生します。要素の中のテキストは、<audio> に対応していないブラウザでだけ表示されます。
<audio controls preload="metadata">
<source src="episode.opus" type="audio/ogg; codecs=opus">
<source src="episode.mp3" type="audio/mpeg">
<p>Your browser cannot play audio. <a href="episode.mp3">Download the episode</a>.</p>
</audio>
| 形式 | MIME タイプ | 補足 |
|---|
| MP3 | audio/mpeg | 現在のどのブラウザでも再生できる。1 つだけなら安全な選択 |
AAC (.m4a) | audio/mp4 | 広く対応していて、小さいサイズでも音質がよい |
Opus または Vorbis (.ogg、.webm) | audio/ogg、audio/webm | ファイルが小さい。Safari の対応がバージョンによって違うので、MP3 を代替として並べる |
| WAV | audio/wav | 非圧縮で大きい。短い効果音には問題ない |
自動再生されない理由
ブラウザは、ページが勝手に音を出し始めることを許しません。autoplay や、ユーザーがクリックやタップをする前の play() の呼び出しは拒否されます。この例は読み込み時に再生を試みて結果を出力し、その後でボタンを表示します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Autoplay</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
button { font: inherit; padding: 6px 14px; }
</style>
</head>
<body>
<p id="result">Trying to play...</p>
<button id="play">Play with a click</button>
<script>
// Builds a short beep as a WAV file (stand-in for a real sound file)
function beep(freq, secs) {
const rate = 8000, n = secs * rate;
const view = new DataView(new ArrayBuffer(44 + n * 2));
const text = (at, s) => [...s].forEach((c, i) => view.setUint8(at + i, c.charCodeAt(0)));
text(0, 'RIFF'); view.setUint32(4, 36 + n * 2, true); text(8, 'WAVEfmt ');
view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, 1, true);
view.setUint32(24, rate, true); view.setUint32(28, rate * 2, true);
view.setUint16(32, 2, true); view.setUint16(34, 16, true);
text(36, 'data'); view.setUint32(40, n * 2, true);
for (let i = 0; i < n; i++) view.setInt16(44 + i * 2, Math.sin(2 * Math.PI * freq * i / rate) * 12000, true);
return URL.createObjectURL(new Blob([view], { type: 'audio/wav' }));
}
const sound = new Audio(beep(440, 0.5));
const result = document.getElementById('result');
sound.play()
.then(() => { result.textContent = 'Autoplay worked this time.'; })
.catch((err) => { result.textContent = 'Autoplay was blocked: ' + err.name; });
document.getElementById('play').onclick = () => {
sound.currentTime = 0;
sound.play()
.then(() => { result.textContent = 'Playing, because you clicked.'; })
.catch((err) => { result.textContent = 'Still blocked: ' + err.name; });
};
</script>
</body>
</html>
たいていは、ブラウザが再生をブロックしたときのエラーである NotAllowedError が表示されます。ボタンが動くのは、クリックが許可とみなされるからです。ブラウザごとに独自の追加ルールがあります (たとえば Chrome は、よくメディアを再生するサイトを覚えています)。自動再生には決して頼らず、音はユーザーの操作から始め、play() が返す Promise を必ず処理しましょう。
new Audio(url) はページに追加せずに JavaScript で audio 要素を作るので、効果音に便利です。
独自のコントロールを作る
controls を省いて組み込みのプレーヤーを隠し、要素を JavaScript の API で操作します。play()、pause()、currentTime、duration、volume と、timeupdate や ended などのイベントです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Custom player</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
.player { display: flex; align-items: center; gap: 10px; max-width: 360px; padding: 10px; border: 2px solid #e5e7eb; border-radius: 10px; }
.player button { font: inherit; width: 72px; padding: 6px 0; border: 0; border-radius: 6px; background: #2563eb; color: #fff; cursor: pointer; }
progress { flex: 1; }
#time { font-variant-numeric: tabular-nums; font-size: 14px; color: #374151; }
</style>
</head>
<body>
<div class="player">
<button id="toggle" aria-label="Play">Play</button>
<progress id="bar" value="0" max="1"></progress>
<span id="time">0.0 s</span>
</div>
<audio id="audio"></audio>
<script>
function makeWav(notes) {
const rate = 8000, samples = [];
for (const [freq, secs] of notes) {
const n = secs * rate;
for (let i = 0; i < n; i++) {
samples.push(Math.sin(2 * Math.PI * freq * i / rate) * 0.4 * Math.min(1, (n - i) / 400));
}
}
const view = new DataView(new ArrayBuffer(44 + samples.length * 2));
const text = (at, s) => [...s].forEach((c, i) => view.setUint8(at + i, c.charCodeAt(0)));
text(0, 'RIFF'); view.setUint32(4, 36 + samples.length * 2, true); text(8, 'WAVEfmt ');
view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, 1, true);
view.setUint32(24, rate, true); view.setUint32(28, rate * 2, true);
view.setUint16(32, 2, true); view.setUint16(34, 16, true);
text(36, 'data'); view.setUint32(40, samples.length * 2, true);
samples.forEach((s, i) => view.setInt16(44 + i * 2, s * 32767, true));
return URL.createObjectURL(new Blob([view], { type: 'audio/wav' }));
}
const audio = document.getElementById('audio');
const toggle = document.getElementById('toggle');
audio.src = makeWav([[392, 0.5], [440, 0.5], [494, 0.5], [523, 1]]);
toggle.onclick = () => {
if (audio.paused) audio.play().catch((err) => { toggle.textContent = err.name; });
else audio.pause();
};
audio.addEventListener('play', () => { toggle.textContent = 'Pause'; toggle.setAttribute('aria-label', 'Pause'); });
audio.addEventListener('pause', () => { toggle.textContent = 'Play'; toggle.setAttribute('aria-label', 'Play'); });
audio.addEventListener('timeupdate', () => {
document.getElementById('bar').value = audio.currentTime / audio.duration || 0;
document.getElementById('time').textContent = audio.currentTime.toFixed(1) + ' s';
});
</script>
</body>
</html>
ボタンは本物の <button> なのでキーボードで操作でき、ラベルは Play と Pause で切り替わります。ファイルの終わりでブラウザが要素を一時停止して ended を発生させるので、ボタンは自動的に Play に戻ります。独自のコントロールを作ると、組み込みのものが何もしなくても提供していたキーボード操作、ラベル、音量調整も自分で引き受けることになります。
アクセシビリティ
- 音を勝手に鳴らさない。 スクリーンリーダーの邪魔になり、人を驚かせます。音声はクリックから始めましょう。
- ポッドキャストや話し声の音声には書き起こしを用意する。 プレーヤーの下にテキスト版へのリンクがあれば十分で、検索エンジンもそれを読めます。
controls を残すか、本物のボタンでコントロールを作る。 そうすればキーボードで音声を一時停止できます。
音声付きの動画、字幕、ポスターについては video タグ のページを参照してください。<audio> は属性のほとんどと JavaScript の API のすべてを video と共有しています。
よくある間違い
controls を忘れる。 これがないと要素は見えず、何も起きていないように見えます。
autoplay が動くと思う。 再生はクリックから始め、play() の Promise を処理しましょう。
- ファイルのパスや type の間違い。 ファイルの URL を直接開いて確認し、
type を実際の形式に合わせましょう (MP3 なら audio/mpeg)。
- 長い一覧ですべてのファイルを読み込む。
preload="none" か "metadata" を使いましょう。
- 止められない BGM。 一時停止する手段を必ず見える形で用意しましょう。