🎤

ブラウザだけで「録音して保存する」

に公開

個人開発でブラウザだけで完結する録音→保存が必要になったので、その実装をまとめます。


方針(結論)

  • getUserMedia でマイク権限&音声ストリーム取得
  • MediaRecorder で録音開始/停止と Blob 生成
  • URL.createObjectURL で即時プレビュー/ダウンロード

最小コード

// 1) マイクからストリーム取得(ヒント付き)
const stream = await navigator.mediaDevices.getUserMedia({
  audio: { echoCancellation: true, noiseSuppression: true },
});

// 2) とりあえず無難な既定(詳細は後述のTIPSで最適化)
const mimeType = MediaRecorder.isTypeSupported("audio/webm;codecs=opus")
  ? "audio/webm;codecs=opus"
  : "audio/webm";

const recorder = new MediaRecorder(stream, { mimeType });
const chunks: Blob[] = [];

recorder.ondataavailable = (e) => {
  if (e.data.size) chunks.push(e.data);
};

const start = () => recorder.start();

const stop = () =>
  new Promise<Blob>((resolve) => {
    recorder.onstop = () => {
      stream.getTracks().forEach((t) => t.stop()); // マイク解放
      resolve(new Blob(chunks, { type: mimeType }));
    };
    recorder.stop();
  });

// 使用例:
// start();
// …ユーザー操作で停止…
// const blob = await stop();

ダウンロード

function downloadBlob(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename; // 例: "recording.webm" / "recording.m4a"
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
}

生成した blob<audio src={URL.createObjectURL(blob)} controls> に入れれば、その場で確認再生も可能です。


TIPS:スマホ対応のための「MIME 総当たり」

モバイルはブラウザ/OS差が大きく、audio/webm;codecs=opus が弾かれることが普通にあります。
複数の MIME 候補を用意し、MediaRecorder.isTypeSupported で動くものを選ぶと成功率が一気に上がります。実際、audio/mp4audio/wav を候補に加えるだけで「録音が始まらない/すぐ止まる」症状が解消するケースが多いです。

const CANDIDATES = [
  "audio/webm;codecs=opus",
  "audio/ogg;codecs=opus",
  "audio/mp4",    // iOS Safari で効くことが多い
  "audio/wav",    // 最終手段(サイズ大)
  "audio/webm",
];

const mimeType =
  CANDIDATES.find((t) => MediaRecorder.isTypeSupported(t)) ?? "";
if (!mimeType) throw new Error("対応MIMEが見つかりません");

関連リンク

Discussion